From db538260616866816e38401584993aead8876bcb Mon Sep 17 00:00:00 2001 From: ichmagmaus 812 Date: Mon, 23 Feb 2026 23:04:32 +0100 Subject: [PATCH] chore: migrate to new version + fixed several critical bugs - Migrated project to latest Telegram iOS base (v12.3.2+) - Fixed circular dependency between GhostModeManager and MiscSettingsManager - Fixed multiple Bazel build configuration errors (select() default conditions) - Fixed duplicate type definitions in PeerInfoScreen - Fixed swiftmodule directory resolution in build scripts - Added Ghostgram Settings tab in main Settings menu with all 5 features - Cleared sensitive credentials from config.json (template-only now) - Excluded bazel-cache from version control --- .bazelrc | 11 + .gitignore | 11 +- .gitlab-ci.yml | 7 +- .sourcekit-lsp/config.json | 2 + BUILD.bazel | 2 +- MODULE.bazel.lock | 300 +- README.md | 146 +- Telegram/BUILD | 7 +- .../Telegram-iOS/Config-AppStoreLLC.xcconfig | 2 +- Telegram/Telegram-iOS/Config-Fork.xcconfig | 2 +- .../AppIconLLC.appiconset/Contents.json | 153 +- Telegram/Telegram-iOS/IconDefault-60@2x.png | Bin 4612 -> 11254 bytes Telegram/Telegram-iOS/IconDefault-60@3x.png | Bin 7167 -> 22855 bytes Telegram/Telegram-iOS/IconDefault-76.png | Bin 2849 -> 4913 bytes Telegram/Telegram-iOS/IconDefault-76@2x.png | Bin 5983 -> 16589 bytes Telegram/Telegram-iOS/IconDefault-83.5@2x.png | Bin 6625 -> 20245 bytes .../Telegram-iOS/IconDefault-Small-40.png | Bin 1333 -> 1687 bytes .../Telegram-iOS/IconDefault-Small-40@2x.png | Bin 2994 -> 5416 bytes .../Telegram-iOS/IconDefault-Small-40@3x.png | Bin 4612 -> 11254 bytes Telegram/Telegram-iOS/IconDefault-Small.png | Bin 925 -> 1045 bytes .../Telegram-iOS/IconDefault-Small@2x.png | Bin 1972 -> 3231 bytes .../Telegram-iOS/IconDefault-Small@3x.png | Bin 3208 -> 6630 bytes Telegram/Telegram-iOS/InfoBazel.plist | 10 +- Telegram/Telegram-iOS/Resources/Cocoon.tgs | Bin 0 -> 2164 bytes .../Telegram-iOS/en.lproj/InfoPlist.strings | 16 +- .../Telegram-iOS/en.lproj/Localizable.strings | 58 +- .../Telegram-iOS/ru.lproj/InfoPlist.strings | 9 +- bazel-telegram-antidelete | 1 + build-system/Make/GenerateProfiles.py | 157 +- build-system/Make/Make.py | 47 +- build-system/Make/TartBuild.py | 2 +- build-system/MakeProject/Package.resolved | 50 + build-system/MakeProject/Package.swift | 26 + .../Sources/MakeProject/ModuleJSON.swift | 54 + .../MakeProject/ProjectGenerator.swift | 147 + .../Sources/MakeProject/SchemeGenerator.swift | 74 + .../Sources/MakeProject/SymlinkManager.swift | 116 + .../Sources/MakeProject/TargetBuilder.swift | 792 ++ .../Sources/MakeProject/main.swift | 51 + build-system/appcenter-configuration.json | 2 +- build-system/appstore-configuration.json | 2 +- .../BroadcastUpload.mobileprovision | Bin 0 -> 4657 bytes .../provisioning/Intents.mobileprovision | Bin 0 -> 4646 bytes .../NotificationContent.mobileprovision | Bin 0 -> 4669 bytes .../NotificationService.mobileprovision | Bin 0 -> 4669 bytes .../provisioning/Share.mobileprovision | Bin 0 -> 4627 bytes .../provisioning/Telegram.mobileprovision | Bin 0 -> 6173 bytes .../provisioning/WatchApp.mobileprovision | Bin 0 -> 4643 bytes .../WatchExtension.mobileprovision | Bin 0 -> 4684 bytes .../provisioning/Widget.mobileprovision | Bin 0 -> 4629 bytes .../example-configuration/variables.bzl | 2 +- .../profiles/BroadcastUpload.mobileprovision | Bin 0 -> 7398 bytes .../profiles/Intents.mobileprovision | Bin 0 -> 4497 bytes .../NotificationContent.mobileprovision | Bin 0 -> 4520 bytes .../NotificationService.mobileprovision | Bin 0 -> 4591 bytes .../profiles/Share.mobileprovision | Bin 0 -> 4478 bytes .../profiles/Telegram.mobileprovision | Bin 0 -> 6876 bytes .../profiles/WatchApp.mobileprovision | Bin 0 -> 4494 bytes .../profiles/WatchExtension.mobileprovision | Bin 0 -> 4535 bytes .../profiles/Widget.mobileprovision | Bin 0 -> 4480 bytes build-system/verify.sh | 2 +- ...egraph.NotificationContent.mobileprovision | Bin 0 -> 4655 bytes ...egraph.NotificationService.mobileprovision | Bin 0 -> 4655 bytes ...ph.telegra.Telegraph.Share.mobileprovision | Bin 0 -> 4613 bytes ...egra.Telegraph.SiriIntents.mobileprovision | Bin 0 -> 4632 bytes ...h.telegra.Telegraph.Widget.mobileprovision | Bin 0 -> 4615 bytes ...Store_ph.telegra.Telegraph.mobileprovision | Bin 0 -> 6015 bytes ...egra.Telegraph.watchkitapp.mobileprovision | Bin 0 -> 4629 bytes ...chkitapp.watchkitextension.mobileprovision | Bin 0 -> 4670 bytes ...oadcastUpload_Distribution.mobileprovision | Bin 0 -> 12217 bytes .../Ghostgram_Distribution.mobileprovision | Bin 0 -> 12581 bytes ...cationContent_Distribution.mobileprovision | Bin 0 -> 12249 bytes ...m_NotificationService_Dist.mobileprovision | Bin 0 -> 12229 bytes ...ostgram_Share_Distribution.mobileprovision | Bin 0 -> 12151 bytes ...m_SiriIntents_Distribution.mobileprovision | Bin 0 -> 12189 bytes ...gram_WatchApp_Distribution.mobileprovision | Bin 0 -> 12175 bytes ...atchExtension_Distribution.mobileprovision | Bin 0 -> 12249 bytes ...stgram_Widget_Distribution.mobileprovision | Bin 0 -> 12154 bytes .../Sources/AccountContext.swift | 18 +- .../Sources/ChatController.swift | 2 +- .../AccountUtils/Sources/AccountUtils.swift | 5 +- submodules/AdUI/Sources/AdInfoScreen.swift | 2 +- submodules/AttachmentUI/BUILD | 2 + .../Sources/AttachmentPanel.swift | 341 +- submodules/AuthorizationUI/BUILD | 1 + ...orizationSequenceCodeEntryController.swift | 13 +- .../AuthorizationSequenceController.swift | 60 +- ...ationSequencePasswordEntryController.swift | 12 +- ...nSequencePasswordEntryControllerNode.swift | 2 +- .../AuthorizationSequencePaymentScreen.swift | 2 +- ...rizationSequencePhoneEntryController.swift | 9 +- ...tionSequencePhoneEntryControllerNode.swift | 4 +- ...uthorizationSequenceSignUpController.swift | 10 +- ...uthorizationSequenceSplashController.swift | 4 +- .../Sources/BotCheckoutHeaderItem.swift | 2 +- .../Sources/BotCheckoutPriceItem.swift | 2 +- .../Sources/BotCheckoutTipItem.swift | 2 +- submodules/BrowserUI/BUILD | 7 + .../Sources/BrowserAddressBarComponent.swift | 297 +- .../Sources/BrowserAddressListComponent.swift | 28 +- .../BrowserAddressListItemComponent.swift | 13 +- .../Sources/BrowserBookmarksScreen.swift | 157 +- .../Sources/BrowserInstantPageContent.swift | 22 +- .../BrowserNavigationBarComponent.swift | 622 +- .../BrowserUI/Sources/BrowserPdfContent.swift | 30 +- .../BrowserUI/Sources/BrowserScreen.swift | 142 +- .../Sources/BrowserSearchBarComponent.swift | 313 +- .../Sources/BrowserTitleBarComponent.swift | 42 +- .../Sources/BrowserToolbarComponent.swift | 204 +- .../BrowserUI/Sources/BrowserWebContent.swift | 110 +- .../Sources/CalendarMessageScreen.swift | 8 +- submodules/CallListUI/BUILD | 3 + .../CallListUI/Sources/CallListCallItem.swift | 5 +- .../Sources/CallListController.swift | 47 +- .../Sources/CallListControllerNode.swift | 29 +- .../Sources/CallListGroupCallItem.swift | 5 +- .../CallListUI/Sources/CallListHoleItem.swift | 2 +- .../ChatListFilterSettingsHeaderItem.swift | 2 +- submodules/ChatListSearchItemHeader/BUILD | 9 +- .../Sources/ChatListSearchItemHeader.swift | 7 + submodules/ChatListUI/BUILD | 9 +- .../ChatListAdditionalCategoryItem.swift | 2 +- .../Sources/ChatListContainerItemNode.swift | 4 +- .../Sources/ChatListController.swift | 1139 ++- .../Sources/ChatListControllerNode.swift | 291 +- .../ChatListFilterPresetCategoryItem.swift | 2 +- .../ChatListFilterPresetController.swift | 9 +- .../ChatListFilterPresetListController.swift | 7 +- .../ChatListFilterPresetListItem.swift | 2 +- ...hatListFilterPresetListSuggestedItem.swift | 2 +- .../ChatListFilterTagSectionHeaderItem.swift | 2 +- .../Sources/ChatListRecentPeersListItem.swift | 2 +- .../Sources/ChatListSearchContainerNode.swift | 119 +- .../Sources/ChatListSearchListPaneNode.swift | 29 +- .../ChatListSearchPaneContainerNode.swift | 50 +- .../Sources/ChatListShimmerNode.swift | 1 - .../ItemListFilterTitleInputItem.swift | 2 +- .../Node/ChatListArchiveInfoItem.swift | 2 +- .../Node/ChatListEmptyHeaderItem.swift | 2 +- .../Sources/Node/ChatListEmptyInfoItem.swift | 4 +- .../Sources/Node/ChatListHoleItem.swift | 4 +- .../Sources/Node/ChatListItem.swift | 54 +- .../Sources/Node/ChatListItemStrings.swift | 10 +- .../Sources/Node/ChatListNode.swift | 320 +- .../Sources/Node/ChatListNodeEntries.swift | 63 +- .../ChatPresentationInterfaceState.swift | 190 +- submodules/ChatTextLinkEditUI/BUILD | 19 +- .../Sources/ChatTextLinkEditController.swift | 514 +- .../ChatTitleActivityContentNode.swift | 4 +- .../Sources/ChatTitleActivityNode.swift | 5 +- .../Source/Base/CombinedComponent.swift | 24 +- .../Source/Base/Transition.swift | 74 +- .../Source/Components/Button.swift | 15 + .../Source/Components/Image.swift | 12 +- .../MultilineTextWithEntitiesComponent.swift | 7 + .../Sources/PagerComponent.swift | 3 +- .../Sources/SheetComponent.swift | 9 +- .../Sources/ViewControllerComponent.swift | 16 +- submodules/ComposePollUI/BUILD | 1 - .../Sources/ComposePollScreen.swift | 40 +- submodules/ContactListUI/BUILD | 2 +- .../Sources/ContactAddItem.swift | 2 +- .../Sources/ContactListActionItem.swift | 2 +- .../Sources/ContactListNameIndexHeader.swift | 1 + .../Sources/ContactListNode.swift | 41 +- .../Sources/ContactsController.swift | 39 +- .../Sources/ContactsControllerNode.swift | 30 +- .../Sources/ContactsSearchContainerNode.swift | 39 +- .../Sources/InviteContactsController.swift | 5 +- .../InviteContactsControllerNode.swift | 2 +- .../Sources/LimitedPermissionItem.swift | 2 +- .../Sources/ContactsPeerItem.swift | 16 +- .../ContextControllerActionsStackNode.swift | 8 +- ...onSequenceCountrySelectionController.swift | 40 +- .../Display/Source/ImmediateTextNode.swift | 6 +- .../Source/KeyShortcutsController.swift | 6 +- .../Display/Source/LinkHighlightingNode.swift | 451 +- submodules/Display/Source/ListView.swift | 81 +- .../Display/Source/ListViewItemHeader.swift | 62 +- .../Display/Source/ListViewItemNode.swift | 52 +- .../Navigation/NavigationModalFrame.swift | 2 +- .../Source/NavigationBackgroundView.swift | 332 + submodules/Display/Source/NavigationBar.swift | 1833 +--- .../Display/Source/NavigationBarBadge.swift | 4 +- .../Source/NavigationBarContentNode.swift | 3 +- .../Source/NavigationBarTitleView.swift | 6 +- .../NavigationTransitionCoordinator.swift | 16 +- .../Display/Source/SparseContainerView.swift | 47 + submodules/Display/Source/UIKitUtils.swift | 18 +- .../Display/Source/ViewController.swift | 68 +- .../Sources/FeaturedStickersScreen.swift | 10 +- .../GalleryUI/Sources/GalleryController.swift | 8 +- .../GalleryUI/Sources/GalleryTitleView.swift | 8 +- .../Sources/Items/ChatImageGalleryItem.swift | 2 +- submodules/HashtagSearchUI/BUILD | 2 + .../Sources/HashtagSearchController.swift | 4 +- .../HashtagSearchNavigationContentNode.swift | 122 +- .../Sources/HashtagSearchRecentListNode.swift | 2 +- .../Sources/HorizontalPeerItem.swift | 2 +- .../Sources/AdditionalLinkItem.swift | 2 +- .../FolderInviteLinkListController.swift | 26 +- .../Sources/InviteLinkHeaderItem.swift | 2 +- .../Sources/InviteLinkInviteHeaderItem.swift | 2 +- .../Sources/InviteLinkInviteManageItem.swift | 2 +- .../Sources/InviteRequestsSearchItem.swift | 8 +- .../ItemListFolderInviteLinkItem.swift | 2 +- .../ItemListFolderInviteLinkListItem.swift | 2 +- .../ItemListInviteLinkDateLimitItem.swift | 2 +- .../Sources/ItemListInviteLinkItem.swift | 2 +- .../ItemListInviteLinkUsageLimitItem.swift | 2 +- .../Sources/ItemListInviteRequestItem.swift | 2 +- .../ItemListPermanentInviteLinkItem.swift | 6 +- .../Sources/ItemListAddressItem.swift | 2 +- .../Sources/ItemListAvatarAndNameItem.swift | 2 +- .../Sources/ItemListPeerActionItem.swift | 10 +- .../Sources/ItemListPeerItem.swift | 2 +- .../Sources/ItemListStickerPackItem.swift | 2 +- submodules/ItemListUI/BUILD | 2 + .../Sources/ItemListController.swift | 46 +- .../Sources/ItemListControllerNode.swift | 6 +- ...ItemListControllerSegmentedTitleView.swift | 64 +- .../ItemListControllerTabsContentNode.swift | 6 +- .../Sources/Items/ItemListActionItem.swift | 2 +- .../Items/ItemListActivityTextItem.swift | 2 +- .../Sources/Items/ItemListCheckboxItem.swift | 2 +- .../Items/ItemListDisclosureItem.swift | 2 +- .../Sources/Items/ItemListEditableItem.swift | 4 +- .../Items/ItemListExpandableSwitchItem.swift | 2 +- .../Sources/Items/ItemListInfoItem.swift | 2 +- .../Items/ItemListMultilineInputItem.swift | 2 +- .../Items/ItemListMultilineTextItem.swift | 2 +- .../Items/ItemListPlaceholderItem.swift | 2 +- .../Items/ItemListSectionHeaderItem.swift | 2 +- .../Items/ItemListSingleLineInputItem.swift | 2 +- .../Sources/Items/ItemListSwitchItem.swift | 2 +- .../Sources/Items/ItemListTextItem.swift | 2 +- .../Items/ItemListTextWithLabelItem.swift | 2 +- .../Sources/ItemListVenueItem.swift | 2 +- .../Sources/ListMessageHoleItem.swift | 2 +- .../Sources/ListMessageNode.swift | 2 +- .../Sources/ListMessageSnippetItemNode.swift | 16 +- submodules/ListSectionHeaderNode/BUILD | 7 +- .../Sources/ListSectionHeaderNode.swift | 26 +- .../Sources/LocationActionListItem.swift | 2 +- .../Sources/LocationAttributionItem.swift | 2 +- .../Sources/LocationInfoListItem.swift | 2 +- .../Sources/LocationLiveListItem.swift | 2 +- .../Sources/LocationPickerController.swift | 2 +- .../LocationPickerControllerNode.swift | 6 +- .../LocationSearchNavigationContentNode.swift | 8 +- .../Sources/LocationSectionHeaderItem.swift | 2 +- .../Sources/LocationViewController.swift | 2 +- submodules/MediaPickerUI/BUILD | 1 + .../Sources/MediaGroupsAlbumGridItem.swift | 4 +- .../Sources/MediaGroupsAlbumItem.swift | 2 +- .../Sources/MediaGroupsHeaderItem.swift | 2 +- .../Sources/MediaPickerScreen.swift | 375 +- .../Sources/MediaPickerTitleView.swift | 8 +- .../MtProtoKit/Sources/MTApiEnvironment.m | 1554 +-- .../Sources/MTNetworkAvailability.m | 9 + .../Sources/NotificationSoundSelection.swift | 2 +- submodules/OpusBinding/BUILD | 2 + .../PublicHeaders/OpusBinding/OpusBinding.h | 3 +- .../Sources/PasscodeSetupController.swift | 2 +- .../SetupTwoStepVerificationController.swift | 4 +- .../TwoFactorAuthDataInputScreen.swift | 12 +- .../Sources/TwoFactorAuthSplashScreen.swift | 2 +- .../Sources/AvatarGalleryController.swift | 2 +- submodules/PeerInfoUI/BUILD | 3 + .../CreateExternalMediaStreamScreen.swift | 2 +- .../Sources/ChannelAdminController.swift | 60 +- ...elDiscussionGroupSearchContainerNode.swift | 2 +- ...hannelDiscussionGroupSetupHeaderItem.swift | 2 +- ...hannelDiscussionGroupSetupSearchItem.swift | 163 +- .../ChannelMembersSearchContainerNode.swift | 2 +- .../ChannelMembersSearchController.swift | 3 +- .../ChannelMembersSearchControllerNode.swift | 2 +- .../ChannelPermissionsController.swift | 36 +- .../PeerInfoUI/Sources/ChatSlowmodeItem.swift | 2 +- .../Sources/ChatUnrestrictBoostersItem.swift | 2 +- .../Sources/GroupInfoSearchItem.swift | 4 +- ...GroupInfoSearchNavigationContentNode.swift | 161 +- .../Sources/ItemListCallListItem.swift | 2 +- .../Sources/ItemListReactionItem.swift | 2 +- .../Sources/ItemListSecretChatKeyItem.swift | 2 +- .../Sources/PeerAutoremoveTimeoutItem.swift | 2 +- .../UserInfoEditingPhoneActionItem.swift | 2 +- .../Sources/UserInfoEditingPhoneItem.swift | 2 +- .../Sources/PeersNearbyHeaderItem.swift | 2 +- .../Postbox/Sources/DeletedMessagesView.swift | 2 +- .../Postbox/Sources/HistoryTagInfoView.swift | 2 +- .../Sources/MessageHistoryOperation.swift | 2 +- .../Postbox/Sources/MessageHistoryTable.swift | 26 +- .../Postbox/Sources/MessageHistoryView.swift | 8 +- submodules/Postbox/Sources/MessageView.swift | 2 +- .../PeerChatTopIndexableMessageIds.swift | 72 +- submodules/Postbox/Sources/PeerView.swift | 2 +- submodules/Postbox/Sources/Postbox.swift | 14 +- submodules/PremiumUI/BUILD | 3 + .../Sources/CreateGiveawayController.swift | 14 +- .../PremiumUI/Sources/GiftOptionItem.swift | 2 +- .../Sources/GiveawayInfoController.swift | 327 +- .../Sources/IncreaseLimitHeaderItem.swift | 2 +- .../PremiumUI/Sources/PremiumDemoScreen.swift | 4 +- .../Sources/PremiumGiftCodeScreen.swift | 308 +- .../Sources/PremiumIntroScreen.swift | 111 +- .../Sources/PremiumLimitScreen.swift | 2 +- .../Sources/PremiumPrivacyScreen.swift | 2 +- .../Sources/SubscriptionsCountItem.swift | 2 +- submodules/PresentationDataUtils/BUILD | 1 + .../Sources/AlertTheme.swift | 96 +- .../Sources/OpenUrl.swift | 7 +- submodules/PromptUI/BUILD | 17 +- .../PromptUI/Sources/PromptController.swift | 391 +- submodules/QrCodeUI/BUILD | 9 + .../QrCodeUI/Sources/QrCodeScanScreen.swift | 2 +- .../QrCodeUI/Sources/QrCodeScreen.swift | 953 +- submodules/SearchBarNode/BUILD | 2 + .../SearchBarNode/Sources/SearchBarNode.swift | 296 +- .../Sources/SearchBarPlaceholderNode.swift | 623 +- submodules/SearchUI/BUILD | 16 +- .../FixSearchableListNodeScrolling.swift | 46 - .../NavigationBarSearchContentNode.swift | 43 +- .../Sources/SearchDisplayController.swift | 229 +- .../Sources/SectionHeaderItem.swift | 2 +- submodules/SettingsUI/BUILD | 5 +- .../BubbleSettingsController.swift | 4 +- .../Sources/ChangePhoneNumberController.swift | 14 +- .../AutodownloadDataUsagePickerItem.swift | 2 +- .../AutodownloadSizeLimitItem.swift | 2 +- .../CalculatingCacheSizeItem.swift | 2 +- .../EnergyUsageBatteryLevelItem.swift | 2 +- .../KeepMediaDurationPickerItem.swift | 2 +- .../MaximumCacheSizePickerItem.swift | 2 +- .../ProxyServerActionSheetController.swift | 20 +- .../ProxySettingsActionItem.swift | 2 +- .../ProxySettingsServerItem.swift | 2 +- .../Data and Storage/StorageUsageItem.swift | 2 +- .../WebBrowserDomainController.swift | 528 +- .../WebBrowserDomainExceptionItem.swift | 2 +- .../Data and Storage/WebBrowserItem.swift | 2 +- .../WebBrowserSettingsController.swift | 2 +- .../DeleteAccountOptionsController.swift | 73 +- .../Sources/DeleteAccountPeersItem.swift | 2 +- .../Sources/DeleteAccountPhoneItem.swift | 2 +- .../LocalizationListController.swift | 4 +- .../LocalizationListControllerNode.swift | 4 +- .../Sources/LogoutOptionsController.swift | 6 +- .../NotificationExceptionControllerNode.swift | 34 +- .../Exceptions/NotificationExceptions.swift | 6 +- .../NotificationsCategoryItemListItem.swift | 2 +- .../ForwardPrivacyChatPreviewItem.swift | 2 +- .../GlobalAutoremoveHeaderItem.swift | 2 +- .../GlobalAutoremoveScreen.swift | 8 +- .../LoginEmailSetupController.swift | 11 +- .../PrivacyAndSecurityController.swift | 99 +- .../PrivacyIntroController.swift | 4 +- .../ItemListRecentSessionItem.swift | 2 +- .../Recent Sessions/ItemListWebsiteItem.swift | 2 +- .../RecentSessionsHeaderItem.swift | 2 +- .../SelectivePrivacySettingsController.swift | 6 + .../Sources/Search/SettingsSearchItem.swift | 123 +- .../Search/SettingsSearchRecentItem.swift | 2 +- .../Search/SettingsSearchResultItem.swift | 2 +- .../TermsOfServiceController.swift | 10 +- .../TextSizeSelectionController.swift | 4 +- .../Text Size/TextSizeSelectionItem.swift | 2 +- .../Sources/ThemePickerGridItem.swift | 2 +- .../Themes/ThemePreviewController.swift | 2 +- .../Themes/ThemePreviewControllerNode.swift | 1 - .../Themes/ThemeSettingsAccentColorItem.swift | 6 +- .../Themes/ThemeSettingsAppIconItem.swift | 2 +- .../Themes/ThemeSettingsBrightnessItem.swift | 2 +- .../Themes/ThemeSettingsChatPreviewItem.swift | 2 +- .../Themes/ThemeSettingsFontSizeItem.swift | 2 +- .../ShimmerEffect/Sources/ShimmerEffect.swift | 5 +- submodules/StatisticsUI/BUILD | 3 +- .../StatisticsUI/Sources/BackButton.swift | 2 +- .../Sources/BoostLevelHeaderItem.swift | 2 +- .../StatisticsUI/Sources/BoostsTabsItem.swift | 2 +- .../Sources/ChannelStatsController.swift | 2 +- .../Sources/MonetizationBalanceItem.swift | 2 +- .../Sources/MonetizationIntroScreen.swift | 56 +- .../Sources/RevenueWithdrawalController.swift | 189 +- .../Sources/StarsTransactionItem.swift | 2 +- .../StatisticsUI/Sources/StatsGraphItem.swift | 2 +- .../Sources/StatsMessageItem.swift | 2 +- .../Sources/StatsOverviewItem.swift | 2 +- .../Sources/TransactionInfoScreen.swift | 95 +- .../Sources/StickerPackScreen.swift | 19 +- submodules/TabBarUI/BUILD | 1 + .../Sources/TabBarContollerNode.swift | 203 +- .../TabBarUI/Sources/TabBarController.swift | 37 +- submodules/TelegramApi/Sources/Api0.swift | 14 +- submodules/TelegramApi/Sources/Api1.swift | 216 +- submodules/TelegramApi/Sources/Api10.swift | 297 +- submodules/TelegramApi/Sources/Api11.swift | 422 +- submodules/TelegramApi/Sources/Api12.swift | 208 +- submodules/TelegramApi/Sources/Api13.swift | 301 +- submodules/TelegramApi/Sources/Api14.swift | 354 +- submodules/TelegramApi/Sources/Api15.swift | 812 +- submodules/TelegramApi/Sources/Api16.swift | 451 +- submodules/TelegramApi/Sources/Api17.swift | 170 +- submodules/TelegramApi/Sources/Api18.swift | 305 +- submodules/TelegramApi/Sources/Api19.swift | 374 +- submodules/TelegramApi/Sources/Api2.swift | 274 +- submodules/TelegramApi/Sources/Api20.swift | 268 +- submodules/TelegramApi/Sources/Api21.swift | 108 +- submodules/TelegramApi/Sources/Api22.swift | 192 +- submodules/TelegramApi/Sources/Api23.swift | 291 +- submodules/TelegramApi/Sources/Api24.swift | 244 +- submodules/TelegramApi/Sources/Api25.swift | 388 +- submodules/TelegramApi/Sources/Api26.swift | 275 +- submodules/TelegramApi/Sources/Api27.swift | 1611 ++-- submodules/TelegramApi/Sources/Api28.swift | 414 +- submodules/TelegramApi/Sources/Api29.swift | 289 +- submodules/TelegramApi/Sources/Api3.swift | 506 +- submodules/TelegramApi/Sources/Api30.swift | 294 +- submodules/TelegramApi/Sources/Api31.swift | 247 +- submodules/TelegramApi/Sources/Api32.swift | 258 +- submodules/TelegramApi/Sources/Api33.swift | 514 +- submodules/TelegramApi/Sources/Api34.swift | 478 +- submodules/TelegramApi/Sources/Api35.swift | 510 +- submodules/TelegramApi/Sources/Api36.swift | 592 +- submodules/TelegramApi/Sources/Api37.swift | 480 +- submodules/TelegramApi/Sources/Api38.swift | 361 +- submodules/TelegramApi/Sources/Api39.swift | 33 + submodules/TelegramApi/Sources/Api4.swift | 352 +- submodules/TelegramApi/Sources/Api5.swift | 343 +- submodules/TelegramApi/Sources/Api6.swift | 268 +- submodules/TelegramApi/Sources/Api7.swift | 341 +- submodules/TelegramApi/Sources/Api8.swift | 297 +- submodules/TelegramApi/Sources/Api9.swift | 256 +- .../Sources/TelegramBaseController.swift | 879 +- submodules/TelegramCallsUI/BUILD | 3 +- .../Sources/AccountGroupCallContextImpl.swift | 40 + .../Sources/CallFeedbackController.swift | 6 +- .../Sources/CallRatingController.swift | 492 +- .../Sources/CallStatusBarNode.swift | 16 + .../Sources/CallSuggestTabController.swift | 306 +- .../Components/MediaStreamComponent.swift | 4 +- .../Sources/PresentationGroupCall.swift | 7 +- .../VideoChatScheduledInfoComponent.swift | 16 + .../Sources/VideoChatScreen.swift | 3 +- .../Sources/VideoChatScreenMoreMenu.swift | 2 +- ...ideoChatScreenParticipantContextMenu.swift | 4 +- .../Sources/VoiceChatActionItem.swift | 2 +- .../VoiceChatFullscreenParticipantItem.swift | 2 +- .../Sources/VoiceChatMicrophoneNode.swift | 22 +- .../Sources/VoiceChatParticipantItem.swift | 2 +- .../Sources/VoiceChatTileGridNode.swift | 2 +- .../Sources/VoiceChatTimerNode.swift | 16 + .../VoiceChatTitleEditController.swift | 884 +- submodules/TelegramCore/BUILD | 1 + .../Account/AccountIntermediateState.swift | 18 +- .../Sources/Account/AccountManager.swift | 1 + .../Sources/AntiDelete/LocalEditManager.swift | 85 + .../ApiUtils/StoreMessage_Telegram.swift | 29 +- .../PendingMessageUploadedContent.swift | 22 +- .../State/AccountStateManagementUtils.swift | 186 +- .../Sources/State/AccountStateManager.swift | 7 + .../Sources/State/AccountTaskManager.swift | 1 + .../Sources/State/AccountViewTracker.swift | 4 +- .../Sources/State/ApplyUpdateMessage.swift | 4 +- .../State/ManagedAccountPresence.swift | 12 +- ...gedCloudChatRemoveMessagesOperations.swift | 20 + .../State/ManagedEmojiGameUpdates.swift | 104 + .../State/ManagedLocalInputActivities.swift | 5 + ...agedSynchronizeViewStoriesOperations.swift | 5 + .../Sources/State/PendingMessageManager.swift | 4 +- .../Sources/State/Serialization.swift | 2 +- .../State/SynchronizePeerReadState.swift | 5 + .../Sources/State/UpdateMessageService.swift | 4 +- .../Sources/State/UpdatesApiUtils.swift | 12 +- .../State/UserLimitsConfiguration.swift | 14 +- .../SummarizationMessageAttribute.swift | 87 + ...re_AutoremoveTimeoutMessageAttribute.swift | 10 + .../SyncCore/SyncCore_Namespaces.swift | 7 + .../SyncCore_TelegramChatAdminRights.swift | 3 +- .../SyncCore/SyncCore_TelegramMediaDice.swift | 36 +- .../Data/ConfigurationData.swift | 21 + .../TelegramEngine/Messages/AdMessages.swift | 6 + .../ApplyMaxReadIndexInteractively.swift | 5 +- ...essageContentAsConsumedInteractively.swift | 9 +- .../TelegramEngine/Messages/Summarize.swift | 83 + .../Messages/TelegramEngineMessages.swift | 4 + .../TelegramEngine/Payments/StarGifts.swift | 30 +- .../Payments/StarGiftsAuctions.swift | 12 +- .../TelegramEngine/Payments/Stars.swift | 5 +- .../Sources/Utils/MessageUtils.swift | 5 + .../TelegramNotices/Sources/Notices.swift | 104 - .../Sources/PermissionContentNode.swift | 2 +- .../Sources/PermissionController.swift | 30 +- .../Sources/ComponentsThemes.swift | 37 +- .../DefaultDarkPresentationTheme.swift | 6 +- .../DefaultDarkTintedPresentationTheme.swift | 10 +- .../Sources/DefaultDayPresentationTheme.swift | 8 +- .../PresentationThemeEssentialGraphics.swift | 17 + .../Resources/PresentationResourceKey.swift | 2 + .../Resources/PresentationResourcesChat.swift | 12 + .../PresentationResourcesRootController.swift | 4 +- .../Sources/ServiceMessageStrings.swift | 39 +- submodules/TelegramUI/BUILD | 19 +- .../AdPanelHeaderPanelComponent/BUILD | 36 + .../Sources/AdPanelHeaderPanelComponent.swift | 127 + .../Sources/ChatAdPanelNode.swift | 523 + .../Sources/AdminUserActionsSheet.swift | 3 +- .../Sources/RecentActionsSettingsSheet.swift | 2 +- .../AlertComponent/AlertCheckComponent/BUILD | 28 + .../Sources/AlertCheckComponent.swift | 186 + .../AlertInputFieldComponent/BUILD | 28 + .../Sources/AlertInputFieldComponent.swift | 353 + .../AlertMultilineInputFieldComponent/BUILD | 27 + .../AlertMultilineInputFieldComponent.swift | 361 + .../AlertComponent/AlertTableComponent/BUILD | 23 + .../Sources/AlertTableComponent.swift | 70 + .../AlertTransferHeaderComponent/BUILD | 23 + .../AlertTransferHeaderComponent.swift | 126 + .../Components/AlertComponent/BUILD | 12 +- .../Sources/AlertActionComponent.swift | 228 + .../Sources/AlertComponent.swift | 1233 ++- .../AlertComponent/Sources/AlertContent.swift | 366 + .../Components/AnimatedTextComponent/BUILD | 1 + .../Sources/AnimatedTextComponent.swift | 61 +- .../Sources/AsyncListComponent.swift | 2 +- .../Sources/AttachmentFileController.swift | 22 +- .../Components/AvatarComponent/BUILD | 24 + .../Sources/AvatarComponent.swift | 116 + .../Components/AvatarEditorScreen/BUILD | 2 +- .../Sources/AvatarEditorScreen.swift | 1 + .../Sources/BackButtonComponent.swift | 2 +- .../Sources/Components/BackButtonView.swift | 2 +- .../TelegramUI/Components/CameraScreen/BUILD | 2 + .../CameraScreen/Sources/CameraScreen.swift | 3 +- .../CameraScreen/Sources/ModeComponent.swift | 176 +- .../ChatAgeRestrictionAlertController/BUILD | 27 + .../ChatAgeRestrictionAlertController.swift | 64 + .../Sources/ChatAvatarNavigationNode.swift | 10 +- .../Sources/ChatBotInfoItem.swift | 2 +- .../ChatChannelSubscriberInputPanelNode.swift | 46 +- .../Sources/ChatHistoryEntry.swift | 82 +- .../ChatHistorySearchContainerNode.swift | 2 +- ...ChatInlineSearchResultsListComponent.swift | 2 - .../ChatMessageActionButtonsNode.swift | 1 + .../ChatMessageAnimatedStickerItemNode/BUILD | 1 + .../ChatMessageAnimatedStickerItemNode.swift | 124 + .../Chat/ChatMessageBubbleItemNode/BUILD | 1 + .../Sources/ChatMessageBubbleItemNode.swift | 162 +- .../ChatMessageDateAndStatusNode.swift | 84 +- .../ChatMessageGiftBubbleContentNode.swift | 7 +- ...hatMessageGiftOfferBubbleContentNode.swift | 2 +- .../ChatMessageInstantVideoItemNode.swift | 1 + .../ChatMessageInteractiveFileNode.swift | 75 +- ...atMessageInteractiveInstantVideoNode.swift | 41 +- .../Sources/ChatMessageDateHeader.swift | 4 +- .../Sources/ChatReplyCountItem.swift | 2 +- .../Sources/ChatUnreadItem.swift | 2 +- .../Sources/ChatMessageItemView.swift | 4 +- .../Sources/ChatCallNotificationItem.swift | 14 +- .../Sources/ChatMessageNotificationItem.swift | 18 +- .../ChatMessagePaymentAlertController/BUILD | 2 + .../ChatMessagePaymentAlertController.swift | 584 +- .../ChatMessagePollBubbleContentNode.swift | 9 +- .../Sources/ChatMessageReplyInfoNode.swift | 130 +- .../ChatMessageSelectionInputPanelNode.swift | 11 +- .../Chat/ChatMessageShareButton/BUILD | 1 + .../Sources/ChatMessageShareButton.swift | 118 +- .../Sources/ChatMessageStickerItemNode.swift | 1 + .../ChatMessageTextBubbleContentNode.swift | 62 +- .../ChatMessageTodoBubbleContentNode.swift | 9 +- .../Sources/ChatNewThreadInfoItem.swift | 2 +- .../Sources/ChatQrCodeScreen.swift | 6 +- .../Sources/ChatRecentActionsController.swift | 4 +- .../ChatRecentActionsControllerNode.swift | 1 - ...ntActionsSearchNavigationContentNode.swift | 6 +- .../ChatSearchNavigationContentNode/BUILD | 31 + .../ChatSearchNavigationContentNode.swift | 299 + .../Sources/ChatFloatingTopicsPanel.swift | 18 +- .../Sources/ChatSideTopicsPanel.swift | 12 +- .../ChatTextInputActionButtonsNode.swift | 9 +- .../Sources/ChatTextInputPanelComponent.swift | 2 - .../Sources/ChatTextInputPanelNode.swift | 35 +- .../Sources/ChatUserInfoItem.swift | 2 +- .../Chat/EditableTokenListNode/BUILD | 3 + .../Sources/EditableTokenListNode.swift | 192 +- .../Chat/FactCheckAlertController/BUILD | 3 +- .../Sources/FactCheckAlertController.swift | 462 +- .../Chat/ForwardAccessoryPanelNode/BUILD | 2 + .../Sources/ForwardAccessoryPanelNode.swift | 60 +- .../Sources/ManagedDiceAnimationNode.swift | 11 +- .../Sources/ShimmeringLinkNode.swift | 4 +- .../Sources/ChatControllerInteraction.swift | 2 + .../Sources/ChatEntityKeyboardInputNode.swift | 15 +- .../Sources/ChatFolderLinkPreviewScreen.swift | 7 +- .../ChatListFilterTabContainerNode/BUILD | 27 + .../ChatListFilterTabContainerNode.swift | 1127 +++ .../ChatListHeaderNoticeComponent/BUILD | 33 + .../ChatListHeaderNoticeComponent.swift | 139 + .../Sources/ChatListNoticeItem.swift | 548 ++ .../ChatListSearchFiltersContainerNode/BUILD | 26 + .../ChatListSearchFiltersContainerNode.swift | 295 + .../Sources/ItemNode.swift | 206 + .../Components/ChatListHeaderComponent/BUILD | 2 + .../Sources/ChatListHeaderComponent.swift | 648 +- .../Sources/ChatListNavigationBar.swift | 355 +- .../Sources/NavigationButtonComponent.swift | 254 + .../Sources/ChatListTitleProxyNode.swift | 18 +- .../Sources/ChatListTitleView.swift | 22 +- .../Sources/ChatScheduleTimeScreen.swift | 2 +- .../Components/ChatThemeScreen/BUILD | 3 + .../Sources/ChatThemeScreen.swift | 12 +- .../GiftThemeTransferAlertController.swift | 322 +- .../TelegramUI/Components/ChatTitleView/BUILD | 2 + .../Sources/ChatTitleComponent.swift | 1164 +++ .../ChatTitleView/Sources/ChatTitleView.swift | 433 +- .../Sources/TitleIconComponent.swift | 96 + .../Components/CocoonInfoScreen/BUILD | 37 + .../Sources/CocoonInfoScreen.swift | 669 ++ .../Components/ComposeTodoScreen/BUILD | 1 - .../Sources/ComposeTodoScreen.swift | 40 +- .../Sources/NewContactScreen.swift | 2 +- .../Sources/PhoneInputItem.swift | 2 +- .../Sources/ContentReportScreen.swift | 2 +- .../TelegramUI/Components/EdgeEffect/BUILD | 1 + .../EdgeEffect/Sources/EdgeEffect.swift | 62 +- .../Components/EmojiGameStakeScreen/BUILD | 55 + .../Sources/EmojiGameStakeScreen.swift | 2220 +++++ .../Sources/EmojiTextAttachmentView.swift | 44 + .../Components/EntityKeyboard/BUILD | 3 + .../Sources/EmojiKeyboardItemLayer.swift | 10 +- .../Sources/EmojiPagerContentComponent.swift | 2 +- .../Sources/EntityKeyboard.swift | 17 +- .../EntityKeyboardBottomPanelButton.swift | 159 +- .../EntityKeyboardBottomPanelComponent.swift | 226 +- ...tyKeyboardTopContainerPanelComponent.swift | 15 +- .../EntityKeyboardTopPanelComponent.swift | 2 +- .../Sources/AgeVerificationScreen.swift | 2 +- .../Sources/ForumCreateTopicScreen.swift | 2 +- .../Sources/GiftCompositionComponent.swift | 1 - .../Sources/GiftLoadingShimmerView.swift | 2 +- .../Components/Gifts/GiftOptionsScreen/BUILD | 3 + .../GiftAuctionTransferController.swift | 296 +- .../Sources/GiftOptionsScreen.swift | 28 +- .../Sources/ChatGiftPreviewItem.swift | 2 +- .../Sources/GiftSetupScreen.swift | 15 +- .../Components/Gifts/GiftStoreScreen/BUILD | 2 + .../Sources/FilterSelectorComponent.swift | 114 +- .../Sources/GiftStoreScreen.swift | 131 +- .../Components/Gifts/GiftViewScreen/BUILD | 8 +- .../Sources/GiftAuctionAcquiredScreen.swift | 6 +- .../Sources/GiftAuctionActiveBidsScreen.swift | 13 +- .../Sources/GiftAuctionBidScreen.swift | 70 +- .../GiftAuctionCustomBidController.swift | 590 +- .../Sources/GiftAuctionInfoScreen.swift | 5 +- .../Sources/GiftAuctionViewScreen.swift | 23 +- .../GiftAuctionWearPreviewScreen.swift | 3 +- .../Sources/GiftOfferAlertController.swift | 682 +- .../Sources/GiftPurchaseAlertController.swift | 716 +- .../GiftRemoveInfoAlertController.swift | 391 +- .../Sources/GiftTransferAlertController.swift | 605 +- .../Sources/GiftUnpinScreen.swift | 2 +- .../Sources/GiftUpgradeCostScreen.swift | 1 + .../Sources/GiftUpgradeVariantsScreen.swift | 1526 +++ .../Sources/GiftValueScreen.swift | 3 +- .../Sources/GiftViewScreen.swift | 540 +- .../Sources/GiftWithdrawAlertController.swift | 537 +- .../Gifts/PeerTableCellComponent/BUILD | 26 + .../Sources/PeerTableCellComponent.swift | 123 + .../Components/Gifts/TableComponent/BUILD | 23 + .../Sources/TableComponent.swift | 375 + .../Components/GlassBackgroundComponent/BUILD | 1 + .../Sources/GlassBackgroundComponent.swift | 232 +- .../Sources/LegacyGlassView.swift | 178 + .../Sources/GlassBarButtonComponent.swift | 113 +- .../Sources/GlassControlGroup.swift | 10 +- .../Sources/GlassControlPanel.swift | 30 +- .../GlobalControlPanelsContext/BUILD | 24 + .../Sources/GlobalControlPanelsContext.swift | 698 ++ .../GroupCallHeaderPanelComponent/BUILD | 38 + .../GroupCallHeaderPanelComponent.swift | 124 + .../GroupCallNavigationAccessoryPanel.swift | 808 ++ .../GroupStickerPackSetupController/BUILD | 5 + .../Sources/GroupStickerPackCurrentItem.swift | 2 +- .../Sources/GroupStickerSearchItem.swift | 4 +- ...upStickerSearchNavigationContentNode.swift | 164 +- .../HeaderPanelContainerComponent/BUILD | 22 + .../HeaderPanelContainerComponent.swift | 267 + .../Components/HorizontalTabsComponent/BUILD | 29 + .../Sources/HorizontalTabsComponent.swift | 1270 +++ .../Sources/ItemListDatePickerItem.swift | 2 +- .../LegacyChatHeaderPanelComponent/BUILD | 24 + .../LegacyChatHeaderPanelComponent.swift | 90 + .../LiquidLens/Sources/LiquidLensView.swift | 218 +- .../LiveLocationHeaderPanelComponent/BUILD | 34 + .../LiveLocationHeaderPanelComponent.swift | 286 + .../LocationBroadcastActionSheetItem.swift | 139 + ...ionBroadcastNavigationAccessoryPanel.swift | 212 + .../Sources/CreateLinkScreen.swift | 2 +- .../Sources/MediaEditorScreen.swift | 1 - .../MediaPlaybackHeaderPanelComponent/BUILD | 37 + ...ediaNavigationAccessoryContainerNode.swift | 39 + .../MediaNavigationAccessoryHeaderNode.swift | 817 ++ .../MediaNavigationAccessoryPanel.swift | 91 + .../MediaPlaybackHeaderPanelComponent.swift | 362 + .../TelegramUI/Components/MeshTransform/BUILD | 18 + .../MeshTransform/MeshTransformApi/BUILD | 22 + .../MeshTransformApi/MeshTransformApi.h | 33 + .../Sources/MeshTransformApi.m | 2 + .../MeshTransform/Sources/GenerateMesh.swift | 627 ++ .../MeshTransform/Sources/MeshTransform.swift | 150 + .../MessageFeeHeaderPanelComponent/BUILD | 31 + .../Sources/ChatFeePanelNode.swift | 155 + .../MessageFeeHeaderPanelComponent.swift | 113 + .../Sources/MiniAppListScreen.swift | 9 +- .../Sources/MoreHeaderButton.swift | 13 +- .../Components/NavigationBarImpl/BUILD | 25 + .../Sources/NavigationBarImpl.swift | 1241 +++ .../Sources/NavigationButtonNode.swift | 794 ++ .../NotificationPeerExceptionController.swift | 2 +- .../Sources/PeerAllowedReactionsScreen.swift | 5 +- .../AffiliateProgramSetupScreen/BUILD | 2 + .../Sources/AffiliateProgramSetupScreen.swift | 85 +- .../Sources/JoinAffiliateProgramScreen.swift | 11 +- .../Sources/CollectionTabItemComponent.swift | 7 +- .../Sources/MessagePriceItem.swift | 2 +- .../Sources/PeerInfoChatListPaneNode.swift | 25 +- .../Sources/PeerInfoChatPaneNode.swift | 10 +- .../Components/PeerInfo/PeerInfoScreen/BUILD | 5 + .../PeerInfoScreenBusinessHoursItem.swift | 4 +- .../PeerInfoScreenCallListItem.swift | 7 +- .../PeerInfoScreenPersonalChannelItem.swift | 3 - .../Sources/Panes/PeerInfoGifPaneNode.swift | 16 +- .../Sources/Panes/PeerInfoListPaneNode.swift | 2 +- .../Sources/Panes/PeerInfoMembersPane.swift | 1 + .../PeerInfoHeaderNavigationButton.swift | 120 +- ...oHeaderNavigationButtonContainerNode.swift | 294 +- .../Sources/PeerInfoHeaderNode.swift | 287 +- .../Sources/PeerInfoInteraction.swift | 238 + .../Sources/PeerInfoPaneContainerNode.swift | 661 +- .../Sources/PeerInfoProfileItems.swift | 1730 ++++ .../Sources/PeerInfoScreen.swift | 8581 +---------------- .../Sources/PeerInfoScreenAvatarSetup.swift | 58 +- .../PeerInfoScreenBusinessActions.swift | 182 + .../Sources/PeerInfoScreenCallActions.swift | 361 + ...eerInfoScreenDisplayGiftsContextMenu.swift | 258 + ...ScreenDisplayMediaGalleryContextMenu.swift | 452 + .../Sources/PeerInfoScreenItem.swift | 23 + ...erInfoScreenItemSectionContainerNode.swift | 148 + .../PeerInfoScreenMessageActions.swift | 282 + .../Sources/PeerInfoScreenOpenBio.swift | 129 + .../Sources/PeerInfoScreenOpenBirthday.swift | 69 + .../Sources/PeerInfoScreenOpenChat.swift | 156 + .../Sources/PeerInfoScreenOpenMessage.swift | 141 + .../Sources/PeerInfoScreenOpenNote.swift | 76 + ...eerInfoScreenOpenPeerInfoContextMenu.swift | 178 + .../Sources/PeerInfoScreenOpenPhone.swift | 179 + .../Sources/PeerInfoScreenOpenStories.swift | 138 + .../Sources/PeerInfoScreenOpenURL.swift | 317 + .../Sources/PeerInfoScreenOpenUsername.swift | 57 + .../PeerInfoScreenPerformButtonAction.swift | 1214 +++ .../PeerInfoScreenSettingsActions.swift | 356 + .../Sources/PeerInfoSelectionPanelNode.swift | 209 + .../Sources/PeerInfoSettingsItems.swift | 509 + .../Sources/PeerInfoSettingsTabActions.swift | 322 + .../PhotoUpdateConfirmationController.swift | 333 +- .../Sources/PresentAddMembers.swift | 220 + .../Sources/PeerInfoStoryGridScreen.swift | 6 +- .../Sources/StorySearchGridScreen.swift | 6 +- .../PeerInfoVisualMediaPaneNode/BUILD | 3 +- .../Sources/AddGiftsScreen.swift | 6 +- .../Sources/PeerInfoGiftsPaneNode.swift | 89 +- .../Sources/PeerInfoStoryPaneNode.swift | 64 +- .../PostSuggestionsSettingsScreen/BUILD | 3 + .../PostSuggestionsSettingsScreen.swift | 454 + .../ProfileLevelRatingBarComponent.swift | 2 +- .../Sources/OldChannelsSearch.swift | 2 +- .../OwnershipTransferController/BUILD | 3 +- .../ChannelOwnershipTransferController.swift | 672 +- .../Sources/OwnershipTransferController.swift | 199 +- .../Components/PeerSelectionController/BUILD | 6 +- .../Sources/PeerSelectionController.swift | 25 +- .../Sources/PeerSelectionControllerNode.swift | 97 +- .../Sources/PremiumDiamondComponent.swift | 2 +- .../Components/PremiumAlertController/BUILD | 27 + .../Sources/PremiumAlertController.swift | 124 + .../Sources/SearchInputPanelComponent.swift | 6 +- .../Sources/AccountFreezeInfoScreen.swift | 49 +- ...aticBusinessMessageListItemComponent.swift | 2 - .../AutomaticBusinessMessageSetupScreen.swift | 4 +- .../Sources/BusinessLinksSetupScreen.swift | 3 +- .../Sources/QuickReplySetupScreen.swift | 13 +- .../Sources/BirthdayPickerScreen.swift | 2 +- .../Sources/BusinessDaySetupScreen.swift | 3 +- .../Sources/BusinessHoursSetupScreen.swift | 3 +- .../Sources/ChatIntroItemComponent.swift | 1 - .../BusinessLinkNameAlertController/BUILD | 2 + .../BusinessLinkNameAlertController.swift | 584 +- .../Sources/BusinessLocationSetupScreen.swift | 6 +- .../Settings/ChatbotSetupScreen/BUILD | 1 + .../Sources/ChatbotSetupScreen.swift | 25 +- .../Sources/CollectibleItemInfoScreen.swift | 2 +- .../Sources/LanguageSelectionScreen.swift | 2 +- .../Sources/LanguageSelectionScreenNode.swift | 3 +- .../Sources/PasskeysScreen.swift | 23 +- .../Sources/PeerNameColorItem.swift | 2 +- .../Sources/BackButton.swift | 2 +- .../Sources/ChannelAppearanceScreen.swift | 75 +- .../Sources/EmojiPickerItem.swift | 2 +- .../Sources/GiftListItemComponent.swift | 2 +- .../PeerNameColorChatPreviewItem.swift | 2 +- .../PeerNameColorProfilePreviewItem.swift | 2 +- .../Sources/UserApperanceScreen.swift | 92 +- .../Sources/PeerSelectionScreen.swift | 21 +- .../Sources/ItemListReactionItem.swift | 4 +- .../Sources/ReactionChatPreviewItem.swift | 2 +- .../QuickReplyNameAlertController/BUILD | 2 + .../QuickReplyNameAlertController.swift | 96 + .../Sources/ThemeAccentColorController.swift | 2 +- .../ThemeAccentColorControllerNode.swift | 1 - .../Sources/ThemeCarouselItem.swift | 4 +- .../Sources/ThemeSettingsThemeItem.swift | 4 +- .../Sources/TimezoneSelectionScreen.swift | 6 +- .../Sources/TimezoneSelectionScreenNode.swift | 4 +- .../Sources/WallpaperGalleryController.swift | 2 +- .../Sources/WallpaperPatternPanelNode.swift | 2 +- .../Sources/ThemeColorsGridController.swift | 4 +- .../Sources/ThemeGridController.swift | 54 +- .../Sources/ThemeGridControllerNode.swift | 70 - .../Sources/ThemeGridSearchColorsItem.swift | 2 +- .../Sources/LiveStreamSettingsScreen.swift | 6 +- .../Sources/ShareWithPeersScreen.swift | 4 +- .../Stars/BalanceNeededScreen/BUILD | 2 + .../Sources/BalanceNeededScreen.swift | 50 +- .../Sources/StarsPurchaseScreen.swift | 75 +- .../Stars/StarsTransactionScreen/BUILD | 2 + .../Sources/StarsTransactionScreen.swift | 259 +- .../Sources/StarsStatisticsScreen.swift | 55 +- .../Sources/StarsTransactionsScreen.swift | 77 +- .../Sources/StarsTransferScreen.swift | 6 +- .../Stars/StarsWithdrawalScreen/BUILD | 2 + .../StarsRevenueWithdrawalController.swift | 181 +- .../Sources/StarsWithdrawalScreen.swift | 59 +- .../Components/StorageUsageScreen/BUILD | 1 + .../Sources/DataUsageScreen.swift | 97 +- .../Sources/StorageUsageScreen.swift | 129 +- .../Stories/StoryContainerScreen/BUILD | 1 + .../Sources/StoryItemContentComponent.swift | 6 +- .../Sources/StoryItemImageView.swift | 25 +- .../StoryItemSetContainerComponent.swift | 2 +- ...StoryItemSetContainerViewSendMessage.swift | 21 +- .../Sources/StoryPeerListComponent.swift | 10 +- .../Sources/StoryStealthModeSheetScreen.swift | 2 +- .../Sources/SuggestedPostApproveAlert.swift | 13 +- .../Components/TabBarComponent/BUILD | 2 + .../Sources/TabBarComponent.swift | 607 +- .../Components/TabSelectionRecognizer/BUILD | 17 + .../Sources/TabSelectionRecognizer.swift | 58 + .../Sources/TabSelectorComponent.swift | 21 +- .../Components/TextFieldComponent/BUILD | 1 - .../Sources/TextFieldComponent.swift | 32 +- .../Sources/TextNodeWithEntities.swift | 26 +- .../Sources/EditableTokenListNode.swift | 3 - .../TranslateHeaderPanelComponent/BUILD | 34 + .../Sources/ChatTranslationPanelNode.swift | 1112 +++ .../TranslateHeaderPanelComponent.swift | 137 + .../Contents.json | 2 +- .../backspace_30.pdf | Bin 0 -> 4847 bytes .../Contents.json | 2 +- .../keyboard_30.pdf | Bin 0 -> 6044 bytes .../Contents.json | 2 +- .../settings_30 (4).pdf | Bin 0 -> 7917 bytes .../PanelFeaturedIcon.imageset/Contents.json | 2 +- .../trending_44.pdf | Bin 0 -> 8700 bytes .../CollapseIcon.imageset/Contents.json | 12 + .../Message/CollapseIcon.imageset/summary.pdf | Bin 0 -> 4636 bytes .../Message/ExpandIcon.imageset/Contents.json | 12 + .../Message/ExpandIcon.imageset/original.pdf | Bin 0 -> 4641 bytes .../Instant View/Back.imageset/Contents.json | 2 +- .../Back.imageset/browser_back_30.pdf | Bin 0 -> 4189 bytes .../Bookmark.imageset/Contents.json | 2 +- .../Bookmark.imageset/browser_bookmark_30.pdf | Bin 0 -> 5104 bytes .../Browser.imageset/Contents.json | 2 +- .../Browser.imageset/browser_safari_30.pdf | Bin 0 -> 4956 bytes .../Forward.imageset/Contents.json | 2 +- .../Forward.imageset/browser_forward_30.pdf | Bin 0 -> 4194 bytes .../OpenDocument.imageset/Contents.json | 2 +- .../OpenDocument.imageset/browser_doc_30.pdf | Bin 0 -> 5672 bytes .../Search.imageset/Contents.json | 12 + .../Search.imageset/browser_search_30.pdf | Bin 0 -> 4516 bytes .../Instant View/Share.imageset/Contents.json | 12 + .../Share.imageset/browser_share_30.pdf | Bin 0 -> 5208 bytes .../Premium/Cocoon.imageset/Contents.json | 21 + .../Premium/Cocoon.imageset/cocoon.png | Bin 0 -> 101385 bytes .../Wearable.imageset/Contents.json | 12 + .../Wearable.imageset/wear_30 (2).pdf | Bin 0 -> 8186 bytes .../Premium/Dice.imageset/Contents.json | 12 + .../Premium/Dice.imageset/dice.pdf | Bin 0 -> 4749 bytes .../TelegramUI/Sources/AppDelegate.swift | 49 +- .../Sources/ApplicationContext.swift | 2 +- .../Chat/ChatControllerLoadDisplayNode.swift | 126 +- .../Chat/ChatControllerMediaRecording.swift | 123 +- ...ChatControllerNavigationButtonAction.swift | 60 +- ...ChatControllerOpenMessageContextMenu.swift | 13 +- .../ChatControllerOpenMessageFactCheck.swift | 7 +- .../Sources/Chat/ChatControllerOpenPeer.swift | 4 +- ...UpdateChatPresentationInterfaceState.swift | 1 + .../ChatBusinessLinkTitlePanelNode.swift | 1 + .../TelegramUI/Sources/ChatController.swift | 284 +- .../Sources/ChatControllerAdminBanUsers.swift | 9 +- .../Sources/ChatControllerContentData.swift | 44 +- .../ChatControllerDisplayDiceTooltip.swift | 124 + .../Sources/ChatControllerEditChat.swift | 31 +- .../Sources/ChatControllerNode.swift | 779 +- .../ChatControllerOpenAttachmentMenu.swift | 14 +- ...rollerOpenMessageReactionContextMenu.swift | 3 +- .../Sources/ChatHistoryEntriesForView.swift | 25 +- .../Sources/ChatHistoryListNode.swift | 79 +- .../ChatHistoryNavigationButtonNode.swift | 38 +- .../ChatHistoryNavigationButtons.swift | 4 +- .../ChatInterfaceInputContextPanels.swift | 29 +- .../ChatInterfaceStateAccessoryPanels.swift | 54 +- .../ChatInterfaceStateContextMenus.swift | 377 +- .../ChatInterfaceTitlePanelNodes.swift | 1 + .../ChatInviteRequestsTitlePanelNode.swift | 1 + .../ChatManagingBotTitlePanelNode.swift | 1 + .../Sources/ChatMessageTransitionNode.swift | 2 +- .../ChatPinnedMessageTitlePanelNode.swift | 55 +- .../ChatReportPeerTitlePanelNode.swift | 1 + .../ChatRequestInProgressTitlePanelNode.swift | 1 + .../ChatRestrictedInputPanelNode.swift | 5 +- .../ChatSearchResultsContollerNode.swift | 1 - .../Sources/ChatSearchResultsController.swift | 2 +- .../ChatSearchTitleAccessoryPanelNode.swift | 9 +- .../Sources/ChatTagSearchInputPanelNode.swift | 28 +- .../Sources/ChatToastAlertPanelNode.swift | 1 + .../ChatVerifiedPeerTitlePanelNode.swift | 1 + .../CommandChatInputContextPanelNode.swift | 2 - .../Sources/CommandChatInputPanelItem.swift | 2 +- .../CommandMenuChatInputPanelItem.swift | 2 +- .../Sources/ComposeController.swift | 7 +- .../Sources/ComposeControllerNode.swift | 2 +- .../ContactMultiselectionController.swift | 5 +- .../ContactMultiselectionControllerNode.swift | 12 +- .../Sources/ContactSelectionController.swift | 46 +- .../ContactSelectionControllerNode.swift | 2 +- .../Sources/CreateChannelController.swift | 8 +- .../Sources/CreateGroupController.swift | 18 +- .../Sources/EmojisChatInputPanelItem.swift | 2 +- .../HashtagChatInputContextPanelNode.swift | 2 +- .../Sources/HashtagChatInputPanelItem.swift | 2 +- ...ListContextResultsChatInputPanelItem.swift | 2 +- .../Sources/MentionChatInputPanelItem.swift | 2 +- .../NotificationContainerControllerNode.swift | 10 +- .../NotificationItemContainerNode.swift | 32 +- .../TelegramUI/Sources/OpenResolvedUrl.swift | 114 +- submodules/TelegramUI/Sources/OpenUrl.swift | 14 +- .../OverlayAudioPlayerControllerNode.swift | 4 +- .../Sources/SharedAccountContext.swift | 30 +- .../Sources/TelegramRootController.swift | 7 +- ...ntextResultsChatInputPanelButtonItem.swift | 2 +- ...ListContextResultsChatInputPanelItem.swift | 2 +- .../Sources/ExperimentalUISettings.swift | 6 - .../Sources/UpdateInfoItem.swift | 2 +- .../Sources/ChatTextInputAttributes.swift | 1 + submodules/TranslateUI/BUILD | 1 - .../TranslateUI/Sources/ChatTranslation.swift | 80 +- .../Sources/LocalizationListItem.swift | 5 +- .../Sources/UndoOverlayController.swift | 2 +- .../Sources/UndoOverlayControllerNode.swift | 43 +- .../UrlHandling/Sources/UrlHandling.swift | 27 +- .../DeviceModel/Sources/DeviceModel.swift | 7 +- submodules/WallpaperBackgroundNode/BUILD | 3 + .../Sources/WallpaperBackgroundNode.swift | 260 +- .../Sources/WallpaperEdgeEffectNodeImpl.swift | 228 + .../Sources/WebSearchGalleryController.swift | 2 +- .../WebSearchNavigationContentNode.swift | 6 +- .../Sources/WebSearchRecentQueryItem.swift | 2 +- submodules/WebUI/BUILD | 4 + .../WebAppAddToAttachmentController.swift | 174 + .../WebUI/Sources/WebAppController.swift | 332 +- .../WebAppEmojiStatusAlertController.swift | 561 +- .../WebAppLaunchConfirmationController.swift | 607 +- .../WebAppLocationAlertController.swift | 307 +- .../WebAppMessageChatPreviewItem.swift | 2 +- .../Sources/WebAppMessagePreviewScreen.swift | 39 +- .../Sources/WebAppSetEmojiStatusScreen.swift | 8 +- .../Sources/WebAppTermsAlertController.swift | 423 +- submodules/ffmpeg/BUILD | 5 + third-party/libyuv/BUILD | 1 + third-party/openh264/BUILD | 3 + third-party/recaptcha/BUILD | 4 +- .../Info.plist | 40 + .../_CodeSignature/CodeDirectory | Bin 0 -> 166 bytes .../_CodeSignature/CodeRequirements | Bin 0 -> 184 bytes .../_CodeSignature/CodeRequirements-1 | Bin 0 -> 214 bytes .../_CodeSignature/CodeResources | 338 + .../_CodeSignature/CodeSignature | Bin 0 -> 9140 bytes .../Headers/RecaptchaEnterpriseSDK.h | 519 + .../Info.plist | Bin 0 -> 685 bytes .../arm64.swiftdoc | Bin 0 -> 13328 bytes .../arm64.swiftinterface | 107 + .../Modules/module.modulemap | 5 + .../PrivacyInfo.xcprivacy | 56 + .../RecaptchaEnterpriseSDK | Bin 0 -> 4345456 bytes .../Headers/RecaptchaEnterpriseSDK.h | 519 + .../Info.plist | Bin 0 -> 712 bytes .../arm64.swiftdoc | Bin 0 -> 13340 bytes .../arm64.swiftinterface | 107 + .../x86_64.swiftdoc | Bin 0 -> 13340 bytes .../x86_64.swiftinterface | 107 + .../Modules/module.modulemap | 5 + .../PrivacyInfo.xcprivacy | 56 + .../RecaptchaEnterpriseSDK | Bin 0 -> 8507592 bytes third-party/webrtc/BUILD | 6 +- third-party/webrtc/crc32c/BUILD | 3 + third-party/webrtc/libsrtp/BUILD | 1 + versions.json | 6 +- 1017 files changed, 62337 insertions(+), 40559 deletions(-) create mode 100644 Telegram/Telegram-iOS/Resources/Cocoon.tgs create mode 120000 bazel-telegram-antidelete create mode 100644 build-system/MakeProject/Package.resolved create mode 100644 build-system/MakeProject/Package.swift create mode 100644 build-system/MakeProject/Sources/MakeProject/ModuleJSON.swift create mode 100644 build-system/MakeProject/Sources/MakeProject/ProjectGenerator.swift create mode 100644 build-system/MakeProject/Sources/MakeProject/SchemeGenerator.swift create mode 100644 build-system/MakeProject/Sources/MakeProject/SymlinkManager.swift create mode 100644 build-system/MakeProject/Sources/MakeProject/TargetBuilder.swift create mode 100644 build-system/MakeProject/Sources/MakeProject/main.swift create mode 100644 build-system/example-configuration/provisioning/BroadcastUpload.mobileprovision create mode 100644 build-system/example-configuration/provisioning/Intents.mobileprovision create mode 100644 build-system/example-configuration/provisioning/NotificationContent.mobileprovision create mode 100644 build-system/example-configuration/provisioning/NotificationService.mobileprovision create mode 100644 build-system/example-configuration/provisioning/Share.mobileprovision create mode 100644 build-system/example-configuration/provisioning/Telegram.mobileprovision create mode 100644 build-system/example-configuration/provisioning/WatchApp.mobileprovision create mode 100644 build-system/example-configuration/provisioning/WatchExtension.mobileprovision create mode 100644 build-system/example-configuration/provisioning/Widget.mobileprovision create mode 100644 build-system/fake-codesigning/profiles/BroadcastUpload.mobileprovision create mode 100644 build-system/fake-codesigning/profiles/Intents.mobileprovision create mode 100644 build-system/fake-codesigning/profiles/NotificationContent.mobileprovision create mode 100644 build-system/fake-codesigning/profiles/NotificationService.mobileprovision create mode 100644 build-system/fake-codesigning/profiles/Share.mobileprovision create mode 100644 build-system/fake-codesigning/profiles/Telegram.mobileprovision create mode 100644 build-system/fake-codesigning/profiles/WatchApp.mobileprovision create mode 100644 build-system/fake-codesigning/profiles/WatchExtension.mobileprovision create mode 100644 build-system/fake-codesigning/profiles/Widget.mobileprovision create mode 100644 buildbox/fake-codesigning/profiles/appstore/AppStore_ph.telegra.Telegraph.NotificationContent.mobileprovision create mode 100644 buildbox/fake-codesigning/profiles/appstore/AppStore_ph.telegra.Telegraph.NotificationService.mobileprovision create mode 100644 buildbox/fake-codesigning/profiles/appstore/AppStore_ph.telegra.Telegraph.Share.mobileprovision create mode 100644 buildbox/fake-codesigning/profiles/appstore/AppStore_ph.telegra.Telegraph.SiriIntents.mobileprovision create mode 100644 buildbox/fake-codesigning/profiles/appstore/AppStore_ph.telegra.Telegraph.Widget.mobileprovision create mode 100644 buildbox/fake-codesigning/profiles/appstore/AppStore_ph.telegra.Telegraph.mobileprovision create mode 100644 buildbox/fake-codesigning/profiles/appstore/AppStore_ph.telegra.Telegraph.watchkitapp.mobileprovision create mode 100644 buildbox/fake-codesigning/profiles/appstore/AppStore_ph.telegra.Telegraph.watchkitapp.watchkitextension.mobileprovision create mode 100644 codesigning/profiles_backup/Ghostgram_BroadcastUpload_Distribution.mobileprovision create mode 100644 codesigning/profiles_backup/Ghostgram_Distribution.mobileprovision create mode 100644 codesigning/profiles_backup/Ghostgram_NotificationContent_Distribution.mobileprovision create mode 100644 codesigning/profiles_backup/Ghostgram_NotificationService_Dist.mobileprovision create mode 100644 codesigning/profiles_backup/Ghostgram_Share_Distribution.mobileprovision create mode 100644 codesigning/profiles_backup/Ghostgram_SiriIntents_Distribution.mobileprovision create mode 100644 codesigning/profiles_backup/Ghostgram_WatchApp_Distribution.mobileprovision create mode 100644 codesigning/profiles_backup/Ghostgram_WatchExtension_Distribution.mobileprovision create mode 100644 codesigning/profiles_backup/Ghostgram_Widget_Distribution.mobileprovision create mode 100644 submodules/Display/Source/NavigationBackgroundView.swift create mode 100644 submodules/Display/Source/SparseContainerView.swift create mode 100644 submodules/TelegramCore/Sources/AntiDelete/LocalEditManager.swift create mode 100644 submodules/TelegramCore/Sources/State/ManagedEmojiGameUpdates.swift create mode 100644 submodules/TelegramCore/Sources/SyncCore/SummarizationMessageAttribute.swift create mode 100644 submodules/TelegramCore/Sources/TelegramEngine/Messages/Summarize.swift create mode 100644 submodules/TelegramUI/Components/AdPanelHeaderPanelComponent/BUILD create mode 100644 submodules/TelegramUI/Components/AdPanelHeaderPanelComponent/Sources/AdPanelHeaderPanelComponent.swift create mode 100644 submodules/TelegramUI/Components/AdPanelHeaderPanelComponent/Sources/ChatAdPanelNode.swift create mode 100644 submodules/TelegramUI/Components/AlertComponent/AlertCheckComponent/BUILD create mode 100644 submodules/TelegramUI/Components/AlertComponent/AlertCheckComponent/Sources/AlertCheckComponent.swift create mode 100644 submodules/TelegramUI/Components/AlertComponent/AlertInputFieldComponent/BUILD create mode 100644 submodules/TelegramUI/Components/AlertComponent/AlertInputFieldComponent/Sources/AlertInputFieldComponent.swift create mode 100644 submodules/TelegramUI/Components/AlertComponent/AlertMultilineInputFieldComponent/BUILD create mode 100644 submodules/TelegramUI/Components/AlertComponent/AlertMultilineInputFieldComponent/Sources/AlertMultilineInputFieldComponent.swift create mode 100644 submodules/TelegramUI/Components/AlertComponent/AlertTableComponent/BUILD create mode 100644 submodules/TelegramUI/Components/AlertComponent/AlertTableComponent/Sources/AlertTableComponent.swift create mode 100644 submodules/TelegramUI/Components/AlertComponent/AlertTransferHeaderComponent/BUILD create mode 100644 submodules/TelegramUI/Components/AlertComponent/AlertTransferHeaderComponent/Sources/AlertTransferHeaderComponent.swift create mode 100644 submodules/TelegramUI/Components/AlertComponent/Sources/AlertActionComponent.swift create mode 100644 submodules/TelegramUI/Components/AlertComponent/Sources/AlertContent.swift create mode 100644 submodules/TelegramUI/Components/AvatarComponent/BUILD create mode 100644 submodules/TelegramUI/Components/AvatarComponent/Sources/AvatarComponent.swift create mode 100644 submodules/TelegramUI/Components/Chat/ChatAgeRestrictionAlertController/BUILD create mode 100644 submodules/TelegramUI/Components/Chat/ChatAgeRestrictionAlertController/Sources/ChatAgeRestrictionAlertController.swift create mode 100644 submodules/TelegramUI/Components/Chat/ChatSearchNavigationContentNode/BUILD create mode 100644 submodules/TelegramUI/Components/Chat/ChatSearchNavigationContentNode/Sources/ChatSearchNavigationContentNode.swift create mode 100644 submodules/TelegramUI/Components/ChatList/ChatListFilterTabContainerNode/BUILD create mode 100644 submodules/TelegramUI/Components/ChatList/ChatListFilterTabContainerNode/Sources/ChatListFilterTabContainerNode.swift create mode 100644 submodules/TelegramUI/Components/ChatList/ChatListHeaderNoticeComponent/BUILD create mode 100644 submodules/TelegramUI/Components/ChatList/ChatListHeaderNoticeComponent/Sources/ChatListHeaderNoticeComponent.swift create mode 100644 submodules/TelegramUI/Components/ChatList/ChatListHeaderNoticeComponent/Sources/ChatListNoticeItem.swift create mode 100644 submodules/TelegramUI/Components/ChatList/ChatListSearchFiltersContainerNode/BUILD create mode 100644 submodules/TelegramUI/Components/ChatList/ChatListSearchFiltersContainerNode/Sources/ChatListSearchFiltersContainerNode.swift create mode 100644 submodules/TelegramUI/Components/ChatList/ChatListSearchFiltersContainerNode/Sources/ItemNode.swift create mode 100644 submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/NavigationButtonComponent.swift create mode 100644 submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleComponent.swift create mode 100644 submodules/TelegramUI/Components/ChatTitleView/Sources/TitleIconComponent.swift create mode 100644 submodules/TelegramUI/Components/CocoonInfoScreen/BUILD create mode 100644 submodules/TelegramUI/Components/CocoonInfoScreen/Sources/CocoonInfoScreen.swift create mode 100644 submodules/TelegramUI/Components/EmojiGameStakeScreen/BUILD create mode 100644 submodules/TelegramUI/Components/EmojiGameStakeScreen/Sources/EmojiGameStakeScreen.swift create mode 100644 submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftUpgradeVariantsScreen.swift create mode 100644 submodules/TelegramUI/Components/Gifts/PeerTableCellComponent/BUILD create mode 100644 submodules/TelegramUI/Components/Gifts/PeerTableCellComponent/Sources/PeerTableCellComponent.swift create mode 100644 submodules/TelegramUI/Components/Gifts/TableComponent/BUILD create mode 100644 submodules/TelegramUI/Components/Gifts/TableComponent/Sources/TableComponent.swift create mode 100644 submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/LegacyGlassView.swift create mode 100644 submodules/TelegramUI/Components/GlobalControlPanelsContext/BUILD create mode 100644 submodules/TelegramUI/Components/GlobalControlPanelsContext/Sources/GlobalControlPanelsContext.swift create mode 100644 submodules/TelegramUI/Components/GroupCallHeaderPanelComponent/BUILD create mode 100644 submodules/TelegramUI/Components/GroupCallHeaderPanelComponent/Sources/GroupCallHeaderPanelComponent.swift create mode 100644 submodules/TelegramUI/Components/GroupCallHeaderPanelComponent/Sources/GroupCallNavigationAccessoryPanel.swift create mode 100644 submodules/TelegramUI/Components/HeaderPanelContainerComponent/BUILD create mode 100644 submodules/TelegramUI/Components/HeaderPanelContainerComponent/Sources/HeaderPanelContainerComponent.swift create mode 100644 submodules/TelegramUI/Components/HorizontalTabsComponent/BUILD create mode 100644 submodules/TelegramUI/Components/HorizontalTabsComponent/Sources/HorizontalTabsComponent.swift create mode 100644 submodules/TelegramUI/Components/LegacyChatHeaderPanelComponent/BUILD create mode 100644 submodules/TelegramUI/Components/LegacyChatHeaderPanelComponent/Sources/LegacyChatHeaderPanelComponent.swift create mode 100644 submodules/TelegramUI/Components/LiveLocationHeaderPanelComponent/BUILD create mode 100644 submodules/TelegramUI/Components/LiveLocationHeaderPanelComponent/Sources/LiveLocationHeaderPanelComponent.swift create mode 100644 submodules/TelegramUI/Components/LiveLocationHeaderPanelComponent/Sources/LocationBroadcastActionSheetItem.swift create mode 100644 submodules/TelegramUI/Components/LiveLocationHeaderPanelComponent/Sources/LocationBroadcastNavigationAccessoryPanel.swift create mode 100644 submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent/BUILD create mode 100644 submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent/Sources/MediaNavigationAccessoryContainerNode.swift create mode 100644 submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent/Sources/MediaNavigationAccessoryHeaderNode.swift create mode 100644 submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent/Sources/MediaNavigationAccessoryPanel.swift create mode 100644 submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent/Sources/MediaPlaybackHeaderPanelComponent.swift create mode 100644 submodules/TelegramUI/Components/MeshTransform/BUILD create mode 100644 submodules/TelegramUI/Components/MeshTransform/MeshTransformApi/BUILD create mode 100644 submodules/TelegramUI/Components/MeshTransform/MeshTransformApi/PublicHeaders/MeshTransformApi/MeshTransformApi.h create mode 100644 submodules/TelegramUI/Components/MeshTransform/MeshTransformApi/Sources/MeshTransformApi.m create mode 100644 submodules/TelegramUI/Components/MeshTransform/Sources/GenerateMesh.swift create mode 100644 submodules/TelegramUI/Components/MeshTransform/Sources/MeshTransform.swift create mode 100644 submodules/TelegramUI/Components/MessageFeeHeaderPanelComponent/BUILD create mode 100644 submodules/TelegramUI/Components/MessageFeeHeaderPanelComponent/Sources/ChatFeePanelNode.swift create mode 100644 submodules/TelegramUI/Components/MessageFeeHeaderPanelComponent/Sources/MessageFeeHeaderPanelComponent.swift create mode 100644 submodules/TelegramUI/Components/NavigationBarImpl/BUILD create mode 100644 submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationBarImpl.swift create mode 100644 submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationButtonNode.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoInteraction.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoProfileItems.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenBusinessActions.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenCallActions.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenDisplayGiftsContextMenu.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenDisplayMediaGalleryContextMenu.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenItem.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenItemSectionContainerNode.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenMessageActions.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenBio.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenBirthday.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenChat.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenMessage.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenNote.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenPeerInfoContextMenu.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenPhone.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenStories.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenURL.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenUsername.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenPerformButtonAction.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenSettingsActions.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSelectionPanelNode.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSettingsItems.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSettingsTabActions.swift create mode 100644 submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PresentAddMembers.swift create mode 100644 submodules/TelegramUI/Components/PremiumAlertController/BUILD create mode 100644 submodules/TelegramUI/Components/PremiumAlertController/Sources/PremiumAlertController.swift create mode 100644 submodules/TelegramUI/Components/TabSelectionRecognizer/BUILD create mode 100644 submodules/TelegramUI/Components/TabSelectionRecognizer/Sources/TabSelectionRecognizer.swift create mode 100644 submodules/TelegramUI/Components/TranslateHeaderPanelComponent/BUILD create mode 100644 submodules/TelegramUI/Components/TranslateHeaderPanelComponent/Sources/ChatTranslationPanelNode.swift create mode 100644 submodules/TelegramUI/Components/TranslateHeaderPanelComponent/Sources/TranslateHeaderPanelComponent.swift create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Input/Media/EntityInputClearIcon.imageset/backspace_30.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Input/Media/EntityInputGlobeIcon.imageset/keyboard_30.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Input/Media/EntityInputSettingsIcon.imageset/settings_30 (4).pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Input/Media/PanelFeaturedIcon.imageset/trending_44.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Message/CollapseIcon.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Message/CollapseIcon.imageset/summary.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Message/ExpandIcon.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Message/ExpandIcon.imageset/original.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Instant View/Back.imageset/browser_back_30.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Instant View/Bookmark.imageset/browser_bookmark_30.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Instant View/Browser.imageset/browser_safari_30.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Instant View/Forward.imageset/browser_forward_30.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Instant View/OpenDocument.imageset/browser_doc_30.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Instant View/Search.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Instant View/Search.imageset/browser_search_30.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Instant View/Share.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Instant View/Share.imageset/browser_share_30.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/Cocoon.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/Cocoon.imageset/cocoon.png create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/Collectible/Wearable.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/Collectible/Wearable.imageset/wear_30 (2).pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/Dice.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/Dice.imageset/dice.pdf create mode 100644 submodules/TelegramUI/Sources/ChatControllerDisplayDiceTooltip.swift create mode 100644 submodules/WallpaperBackgroundNode/Sources/WallpaperEdgeEffectNodeImpl.swift create mode 100644 submodules/WebUI/Sources/WebAppAddToAttachmentController.swift create mode 100755 third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/Info.plist create mode 100644 third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/_CodeSignature/CodeDirectory create mode 100644 third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/_CodeSignature/CodeRequirements create mode 100644 third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/_CodeSignature/CodeRequirements-1 create mode 100644 third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/_CodeSignature/CodeResources create mode 100644 third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/_CodeSignature/CodeSignature create mode 100755 third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64/RecaptchaEnterpriseSDK.framework/Headers/RecaptchaEnterpriseSDK.h create mode 100755 third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64/RecaptchaEnterpriseSDK.framework/Info.plist create mode 100755 third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/arm64.swiftdoc create mode 100755 third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/arm64.swiftinterface create mode 100755 third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64/RecaptchaEnterpriseSDK.framework/Modules/module.modulemap create mode 100755 third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64/RecaptchaEnterpriseSDK.framework/PrivacyInfo.xcprivacy create mode 100755 third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64/RecaptchaEnterpriseSDK.framework/RecaptchaEnterpriseSDK create mode 100755 third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Headers/RecaptchaEnterpriseSDK.h create mode 100755 third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Info.plist create mode 100755 third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/arm64.swiftdoc create mode 100755 third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/arm64.swiftinterface create mode 100755 third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/x86_64.swiftdoc create mode 100755 third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/x86_64.swiftinterface create mode 100755 third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Modules/module.modulemap create mode 100755 third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/PrivacyInfo.xcprivacy create mode 100755 third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/RecaptchaEnterpriseSDK diff --git a/.bazelrc b/.bazelrc index b083e61e..4471fa12 100644 --- a/.bazelrc +++ b/.bazelrc @@ -22,9 +22,20 @@ build --spawn_strategy=standalone build --strategy=SwiftCompile=standalone build --define RULES_SWIFT_BUILD_DUMMY_WORKER=1 +build:swift_profile --jobs=1 +build:swift_profile --local_cpu_resources=1 +build:swift_profile --features=-swift.enable_batch_mode +build:swift_profile --experimental_ui_max_stdouterr_bytes=104857600 +build:swift_profile --@build_bazel_rules_swift//swift:copt=-Xfrontend +build:swift_profile --@build_bazel_rules_swift//swift:copt=-warn-long-function-bodies=350 +build:swift_profile --@build_bazel_rules_swift//swift:copt=-Xfrontend +build:swift_profile --@build_bazel_rules_swift//swift:copt=-warn-long-expression-type-checking=350 + common:index_build --experimental_convenience_symlinks=ignore common:index_build --bes_backend= --bes_results_url= common:index_build --nolegacy_important_outputs common:index_build --show_result=0 common:index_build --define=buildNumber=10000 common:index_build --define=telegramVersion=12.2.2 + +common --registry=https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/ diff --git a/.gitignore b/.gitignore index 230ee773..100cdd79 100644 --- a/.gitignore +++ b/.gitignore @@ -59,8 +59,6 @@ bazel-telegram-ios bazel-telegram-ios/* bazel-testlogs bazel-testlogs/* -bazel-telegram-antidelete -bazel-telegram-antidelete/* xcodeproj.bazelrc */*.swp *.swp @@ -76,10 +74,7 @@ buildServer.json Telegram.LSP.json **/.build/** spm-files +xcode-files .bsp/** - -*.mobileprovision -build-system/fake-codesigning/profiles/* - -*.backup -*_BACKUP* \ No newline at end of file +/.claude/ +/buildbox/* diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 39daf6b3..b9fde043 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -50,6 +50,11 @@ internal_testflight: - python3 -u build-system/Make/Make.py remote-deploy-testflight --darwinContainers="$DARWIN_CONTAINERS" --darwinContainersHost="$DARWIN_CONTAINERS_HOST" --ipa="build/artifacts/Telegram.ipa" --dsyms="build/artifacts/Telegram.DSYMs.zip" environment: name: testflight_llc + artifacts: + when: always + paths: + - build/artifacts + expire_in: 1 week appstore_development: tags: @@ -80,7 +85,7 @@ experimental_i: - export PATH=/opt/homebrew/opt/ruby/bin:$PATH - export PATH=`gem environment gemdir`/bin:$PATH - python3 -u build-system/Make/Make.py remote-build --darwinContainers="$DARWIN_CONTAINERS" --darwinContainersHost="$DARWIN_CONTAINERS_HOST" --cacheHost="$TELEGRAM_BAZEL_CACHE_HOST" --configurationPath="build-system/appcenter-configuration.json" --gitCodesigningRepository="$TELEGRAM_GIT_CODESIGNING_REPOSITORY" --gitCodesigningType=adhoc --configuration=release_arm64 - - python3 -u build-system/Make/DeployToFirebase.py --configuration="$TELEGRAM_PRIVATE_DATA_PATH/firebase-configurations/firebase-internal.json" --ipa="build/artifacts/Telegram.ipa" --dsyms="build/artifacts/Telegram.DSYMs.zip" + - python3 -u build-system/Make/Make.py remote-deploy-testflight --darwinContainers="$DARWIN_CONTAINERS" --darwinContainersHost="$DARWIN_CONTAINERS_HOST" --ipa="build/artifacts/Telegram.ipa" --dsyms="build/artifacts/Telegram.DSYMs.zip" environment: name: experimental artifacts: diff --git a/.sourcekit-lsp/config.json b/.sourcekit-lsp/config.json index 7715da74..8837d093 100644 --- a/.sourcekit-lsp/config.json +++ b/.sourcekit-lsp/config.json @@ -1,6 +1,8 @@ { "backgroundIndexing": true, "backgroundPreparationMode": "build", + "buildServerWorkspaceRequestsTimeout": 999, + "buildSettingsTimeout": 999, "defaultWorkspaceType": "buildServer", "logging": { "level": "error", diff --git a/BUILD.bazel b/BUILD.bazel index 37688a7c..929cf6a2 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -2,7 +2,7 @@ load("@sourcekit_bazel_bsp//rules:setup_sourcekit_bsp.bzl", "setup_sourcekit_bsp setup_sourcekit_bsp( name = "setup_sourcekit_bsp_telegram_project", - bazel_wrapper = "./build-input/bazel-8.4.2-darwin-arm64", + bazel_wrapper = "./build-input/bazel-8.4.2-darwin-x86_64", files_to_watch = [ "**/*.swift", ], diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index ff664751..c24d0548 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -1,156 +1,156 @@ { "lockFileVersion": 18, "registryFileHashes": { - "https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497", - "https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2", - "https://bcr.bazel.build/modules/abseil-cpp/20211102.0/MODULE.bazel": "70390338f7a5106231d20620712f7cccb659cd0e9d073d1991c038eb9fc57589", - "https://bcr.bazel.build/modules/abseil-cpp/20230125.1/MODULE.bazel": "89047429cb0207707b2dface14ba7f8df85273d484c2572755be4bab7ce9c3a0", - "https://bcr.bazel.build/modules/abseil-cpp/20230802.0.bcr.1/MODULE.bazel": "1c8cec495288dccd14fdae6e3f95f772c1c91857047a098fad772034264cc8cb", - "https://bcr.bazel.build/modules/abseil-cpp/20230802.0/MODULE.bazel": "d253ae36a8bd9ee3c5955384096ccb6baf16a1b1e93e858370da0a3b94f77c16", - "https://bcr.bazel.build/modules/abseil-cpp/20230802.1/MODULE.bazel": "fa92e2eb41a04df73cdabeec37107316f7e5272650f81d6cc096418fe647b915", - "https://bcr.bazel.build/modules/abseil-cpp/20240116.1/MODULE.bazel": "37bcdb4440fbb61df6a1c296ae01b327f19e9bb521f9b8e26ec854b6f97309ed", - "https://bcr.bazel.build/modules/abseil-cpp/20240116.1/source.json": "9be551b8d4e3ef76875c0d744b5d6a504a27e3ae67bc6b28f46415fd2d2957da", - "https://bcr.bazel.build/modules/bazel_features/1.1.1/MODULE.bazel": "27b8c79ef57efe08efccbd9dd6ef70d61b4798320b8d3c134fd571f78963dbcd", - "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8", - "https://bcr.bazel.build/modules/bazel_features/1.15.0/MODULE.bazel": "d38ff6e517149dc509406aca0db3ad1efdd890a85e049585b7234d04238e2a4d", - "https://bcr.bazel.build/modules/bazel_features/1.17.0/MODULE.bazel": "039de32d21b816b47bd42c778e0454217e9c9caac4a3cf8e15c7231ee3ddee4d", - "https://bcr.bazel.build/modules/bazel_features/1.18.0/MODULE.bazel": "1be0ae2557ab3a72a57aeb31b29be347bcdc5d2b1eb1e70f39e3851a7e97041a", - "https://bcr.bazel.build/modules/bazel_features/1.19.0/MODULE.bazel": "59adcdf28230d220f0067b1f435b8537dd033bfff8db21335ef9217919c7fb58", - "https://bcr.bazel.build/modules/bazel_features/1.21.0/MODULE.bazel": "675642261665d8eea09989aa3b8afb5c37627f1be178382c320d1b46afba5e3b", - "https://bcr.bazel.build/modules/bazel_features/1.27.0/MODULE.bazel": "621eeee06c4458a9121d1f104efb80f39d34deff4984e778359c60eaf1a8cb65", - "https://bcr.bazel.build/modules/bazel_features/1.28.0/MODULE.bazel": "4b4200e6cbf8fa335b2c3f43e1d6ef3e240319c33d43d60cc0fbd4b87ece299d", - "https://bcr.bazel.build/modules/bazel_features/1.3.0/MODULE.bazel": "cdcafe83ec318cda34e02948e81d790aab8df7a929cec6f6969f13a489ccecd9", - "https://bcr.bazel.build/modules/bazel_features/1.30.0/MODULE.bazel": "a14b62d05969a293b80257e72e597c2da7f717e1e69fa8b339703ed6731bec87", - "https://bcr.bazel.build/modules/bazel_features/1.30.0/source.json": "b07e17f067fe4f69f90b03b36ef1e08fe0d1f3cac254c1241a1818773e3423bc", - "https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7", - "https://bcr.bazel.build/modules/bazel_features/1.9.0/MODULE.bazel": "885151d58d90d8d9c811eb75e3288c11f850e1d6b481a8c9f766adee4712358b", - "https://bcr.bazel.build/modules/bazel_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a", - "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8", - "https://bcr.bazel.build/modules/bazel_skylib/1.1.1/MODULE.bazel": "1add3e7d93ff2e6998f9e118022c84d163917d912f5afafb3058e3d2f1545b5e", - "https://bcr.bazel.build/modules/bazel_skylib/1.2.0/MODULE.bazel": "44fe84260e454ed94ad326352a698422dbe372b21a1ac9f3eab76eb531223686", - "https://bcr.bazel.build/modules/bazel_skylib/1.2.1/MODULE.bazel": "f35baf9da0efe45fa3da1696ae906eea3d615ad41e2e3def4aeb4e8bc0ef9a7a", - "https://bcr.bazel.build/modules/bazel_skylib/1.3.0/MODULE.bazel": "20228b92868bf5cfc41bda7afc8a8ba2a543201851de39d990ec957b513579c5", - "https://bcr.bazel.build/modules/bazel_skylib/1.4.1/MODULE.bazel": "a0dcb779424be33100dcae821e9e27e4f2901d9dfd5333efe5ac6a8d7ab75e1d", - "https://bcr.bazel.build/modules/bazel_skylib/1.4.2/MODULE.bazel": "3bd40978e7a1fac911d5989e6b09d8f64921865a45822d8b09e815eaa726a651", - "https://bcr.bazel.build/modules/bazel_skylib/1.5.0/MODULE.bazel": "32880f5e2945ce6a03d1fbd588e9198c0a959bb42297b2cfaf1685b7bc32e138", - "https://bcr.bazel.build/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917", - "https://bcr.bazel.build/modules/bazel_skylib/1.7.0/MODULE.bazel": "0db596f4563de7938de764cc8deeabec291f55e8ec15299718b93c4423e9796d", - "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b", - "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/source.json": "f121b43eeefc7c29efbd51b83d08631e2347297c95aac9764a701f2a6a2bb953", - "https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84", - "https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8", - "https://bcr.bazel.build/modules/google_benchmark/1.8.2/MODULE.bazel": "a70cf1bba851000ba93b58ae2f6d76490a9feb74192e57ab8e8ff13c34ec50cb", - "https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4", - "https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "22c31a561553727960057361aa33bf20fb2e98584bc4fec007906e27053f80c6", - "https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/source.json": "41e9e129f80d8c8bf103a7acc337b76e54fad1214ac0a7084bf24f4cd924b8b4", - "https://bcr.bazel.build/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f", - "https://bcr.bazel.build/modules/jsoncpp/1.9.5/MODULE.bazel": "31271aedc59e815656f5736f282bb7509a97c7ecb43e927ac1a37966e0578075", - "https://bcr.bazel.build/modules/jsoncpp/1.9.5/source.json": "4108ee5085dd2885a341c7fab149429db457b3169b86eb081fa245eadf69169d", - "https://bcr.bazel.build/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902", - "https://bcr.bazel.build/modules/nlohmann_json/3.6.1/MODULE.bazel": "6f7b417dcc794d9add9e556673ad25cb3ba835224290f4f848f8e2db1e1fca74", - "https://bcr.bazel.build/modules/nlohmann_json/3.6.1/source.json": "f448c6e8963fdfa7eb831457df83ad63d3d6355018f6574fb017e8169deb43a9", - "https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5", - "https://bcr.bazel.build/modules/platforms/0.0.11/MODULE.bazel": "0daefc49732e227caa8bfa834d65dc52e8cc18a2faf80df25e8caea151a9413f", - "https://bcr.bazel.build/modules/platforms/0.0.11/source.json": "f7e188b79ebedebfe75e9e1d098b8845226c7992b307e28e1496f23112e8fc29", - "https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee", - "https://bcr.bazel.build/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37", - "https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615", - "https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814", - "https://bcr.bazel.build/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d", - "https://bcr.bazel.build/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc", - "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7", - "https://bcr.bazel.build/modules/protobuf/27.0/MODULE.bazel": "7873b60be88844a0a1d8f80b9d5d20cfbd8495a689b8763e76c6372998d3f64c", - "https://bcr.bazel.build/modules/protobuf/27.1/MODULE.bazel": "703a7b614728bb06647f965264967a8ef1c39e09e8f167b3ca0bb1fd80449c0d", - "https://bcr.bazel.build/modules/protobuf/29.0-rc2/MODULE.bazel": "6241d35983510143049943fc0d57937937122baf1b287862f9dc8590fc4c37df", - "https://bcr.bazel.build/modules/protobuf/29.0-rc3/MODULE.bazel": "33c2dfa286578573afc55a7acaea3cada4122b9631007c594bf0729f41c8de92", - "https://bcr.bazel.build/modules/protobuf/29.0/MODULE.bazel": "319dc8bf4c679ff87e71b1ccfb5a6e90a6dbc4693501d471f48662ac46d04e4e", - "https://bcr.bazel.build/modules/protobuf/29.0/source.json": "b857f93c796750eef95f0d61ee378f3420d00ee1dd38627b27193aa482f4f981", - "https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0", - "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e", - "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/source.json": "be4789e951dd5301282729fe3d4938995dc4c1a81c2ff150afc9f1b0504c6022", - "https://bcr.bazel.build/modules/re2/2023-09-01/MODULE.bazel": "cb3d511531b16cfc78a225a9e2136007a48cf8a677e4264baeab57fe78a80206", - "https://bcr.bazel.build/modules/re2/2023-09-01/source.json": "e044ce89c2883cd957a2969a43e79f7752f9656f6b20050b62f90ede21ec6eb4", - "https://bcr.bazel.build/modules/rules_android/0.1.1/MODULE.bazel": "48809ab0091b07ad0182defb787c4c5328bd3a278938415c00a7b69b50c4d3a8", - "https://bcr.bazel.build/modules/rules_android/0.1.1/source.json": "e6986b41626ee10bdc864937ffb6d6bf275bb5b9c65120e6137d56e6331f089e", - "https://bcr.bazel.build/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647", - "https://bcr.bazel.build/modules/rules_cc/0.0.10/MODULE.bazel": "ec1705118f7eaedd6e118508d3d26deba2a4e76476ada7e0e3965211be012002", - "https://bcr.bazel.build/modules/rules_cc/0.0.13/MODULE.bazel": "0e8529ed7b323dad0775ff924d2ae5af7640b23553dfcd4d34344c7e7a867191", - "https://bcr.bazel.build/modules/rules_cc/0.0.14/MODULE.bazel": "5e343a3aac88b8d7af3b1b6d2093b55c347b8eefc2e7d1442f7a02dc8fea48ac", - "https://bcr.bazel.build/modules/rules_cc/0.0.15/MODULE.bazel": "6704c35f7b4a72502ee81f61bf88706b54f06b3cbe5558ac17e2e14666cd5dcc", - "https://bcr.bazel.build/modules/rules_cc/0.0.16/MODULE.bazel": "7661303b8fc1b4d7f532e54e9d6565771fea666fbdf839e0a86affcd02defe87", - "https://bcr.bazel.build/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c", - "https://bcr.bazel.build/modules/rules_cc/0.0.6/MODULE.bazel": "abf360251023dfe3efcef65ab9d56beefa8394d4176dd29529750e1c57eaa33f", - "https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e", - "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5", - "https://bcr.bazel.build/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513", - "https://bcr.bazel.build/modules/rules_cc/0.1.2/MODULE.bazel": "557ddc3a96858ec0d465a87c0a931054d7dcfd6583af2c7ed3baf494407fd8d0", - "https://bcr.bazel.build/modules/rules_cc/0.1.2/source.json": "53fcb09b5816c83ca60d9d7493faf3bfaf410dfc2f15deb52d6ddd146b8d43f0", - "https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6", - "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8", - "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/source.json": "c8b1e2c717646f1702290959a3302a178fb639d987ab61d548105019f11e527e", - "https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74", - "https://bcr.bazel.build/modules/rules_java/5.3.5/MODULE.bazel": "a4ec4f2db570171e3e5eb753276ee4b389bae16b96207e9d3230895c99644b86", - "https://bcr.bazel.build/modules/rules_java/6.0.0/MODULE.bazel": "8a43b7df601a7ec1af61d79345c17b31ea1fedc6711fd4abfd013ea612978e39", - "https://bcr.bazel.build/modules/rules_java/6.4.0/MODULE.bazel": "e986a9fe25aeaa84ac17ca093ef13a4637f6107375f64667a15999f77db6c8f6", - "https://bcr.bazel.build/modules/rules_java/6.5.2/MODULE.bazel": "1d440d262d0e08453fa0c4d8f699ba81609ed0e9a9a0f02cd10b3e7942e61e31", - "https://bcr.bazel.build/modules/rules_java/7.10.0/MODULE.bazel": "530c3beb3067e870561739f1144329a21c851ff771cd752a49e06e3dc9c2e71a", - "https://bcr.bazel.build/modules/rules_java/7.12.2/MODULE.bazel": "579c505165ee757a4280ef83cda0150eea193eed3bef50b1004ba88b99da6de6", - "https://bcr.bazel.build/modules/rules_java/7.2.0/MODULE.bazel": "06c0334c9be61e6cef2c8c84a7800cef502063269a5af25ceb100b192453d4ab", - "https://bcr.bazel.build/modules/rules_java/7.3.2/MODULE.bazel": "50dece891cfdf1741ea230d001aa9c14398062f2b7c066470accace78e412bc2", - "https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe", - "https://bcr.bazel.build/modules/rules_java/8.14.0/MODULE.bazel": "717717ed40cc69994596a45aec6ea78135ea434b8402fb91b009b9151dd65615", - "https://bcr.bazel.build/modules/rules_java/8.14.0/source.json": "8a88c4ca9e8759da53cddc88123880565c520503321e2566b4e33d0287a3d4bc", - "https://bcr.bazel.build/modules/rules_java/8.3.2/MODULE.bazel": "7336d5511ad5af0b8615fdc7477535a2e4e723a357b6713af439fe8cf0195017", - "https://bcr.bazel.build/modules/rules_java/8.5.1/MODULE.bazel": "d8a9e38cc5228881f7055a6079f6f7821a073df3744d441978e7a43e20226939", - "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7", - "https://bcr.bazel.build/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909", - "https://bcr.bazel.build/modules/rules_jvm_external/5.2/MODULE.bazel": "d9351ba35217ad0de03816ef3ed63f89d411349353077348a45348b096615036", - "https://bcr.bazel.build/modules/rules_jvm_external/5.3/MODULE.bazel": "bf93870767689637164657731849fb887ad086739bd5d360d90007a581d5527d", - "https://bcr.bazel.build/modules/rules_jvm_external/6.1/MODULE.bazel": "75b5fec090dbd46cf9b7d8ea08cf84a0472d92ba3585b476f44c326eda8059c4", - "https://bcr.bazel.build/modules/rules_jvm_external/6.3/MODULE.bazel": "c998e060b85f71e00de5ec552019347c8bca255062c990ac02d051bb80a38df0", - "https://bcr.bazel.build/modules/rules_jvm_external/6.3/source.json": "6f5f5a5a4419ae4e37c35a5bb0a6ae657ed40b7abc5a5189111b47fcebe43197", - "https://bcr.bazel.build/modules/rules_kotlin/1.9.0/MODULE.bazel": "ef85697305025e5a61f395d4eaede272a5393cee479ace6686dba707de804d59", - "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/MODULE.bazel": "d269a01a18ee74d0335450b10f62c9ed81f2321d7958a2934e44272fe82dcef3", - "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/source.json": "2faa4794364282db7c06600b7e5e34867a564ae91bda7cae7c29c64e9466b7d5", - "https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0", - "https://bcr.bazel.build/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d", - "https://bcr.bazel.build/modules/rules_license/1.0.0/MODULE.bazel": "a7fda60eefdf3d8c827262ba499957e4df06f659330bbe6cdbdb975b768bb65c", - "https://bcr.bazel.build/modules/rules_license/1.0.0/source.json": "a52c89e54cc311196e478f8382df91c15f7a2bfdf4c6cd0e2675cc2ff0b56efb", - "https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc", - "https://bcr.bazel.build/modules/rules_pkg/1.0.1/MODULE.bazel": "5b1df97dbc29623bccdf2b0dcd0f5cb08e2f2c9050aab1092fd39a41e82686ff", - "https://bcr.bazel.build/modules/rules_pkg/1.0.1/source.json": "bd82e5d7b9ce2d31e380dd9f50c111d678c3bdaca190cb76b0e1c71b05e1ba8a", - "https://bcr.bazel.build/modules/rules_proto/4.0.0/MODULE.bazel": "a7a7b6ce9bee418c1a760b3d84f83a299ad6952f9903c67f19e4edd964894e06", - "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7", - "https://bcr.bazel.build/modules/rules_proto/6.0.2/MODULE.bazel": "ce916b775a62b90b61888052a416ccdda405212b6aaeb39522f7dc53431a5e73", - "https://bcr.bazel.build/modules/rules_proto/7.0.2/MODULE.bazel": "bf81793bd6d2ad89a37a40693e56c61b0ee30f7a7fdbaf3eabbf5f39de47dea2", - "https://bcr.bazel.build/modules/rules_proto/7.0.2/source.json": "1e5e7260ae32ef4f2b52fd1d0de8d03b606a44c91b694d2f1afb1d3b28a48ce1", - "https://bcr.bazel.build/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f", - "https://bcr.bazel.build/modules/rules_python/0.23.1/MODULE.bazel": "49ffccf0511cb8414de28321f5fcf2a31312b47c40cc21577144b7447f2bf300", - "https://bcr.bazel.build/modules/rules_python/0.25.0/MODULE.bazel": "72f1506841c920a1afec76975b35312410eea3aa7b63267436bfb1dd91d2d382", - "https://bcr.bazel.build/modules/rules_python/0.27.1/MODULE.bazel": "65dc875cc1a06c30d5bbdba7ab021fd9e551a6579e408a3943a61303e2228a53", - "https://bcr.bazel.build/modules/rules_python/0.28.0/MODULE.bazel": "cba2573d870babc976664a912539b320cbaa7114cd3e8f053c720171cde331ed", - "https://bcr.bazel.build/modules/rules_python/0.31.0/MODULE.bazel": "93a43dc47ee570e6ec9f5779b2e64c1476a6ce921c48cc9a1678a91dd5f8fd58", - "https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c", - "https://bcr.bazel.build/modules/rules_python/0.40.0/MODULE.bazel": "9d1a3cd88ed7d8e39583d9ffe56ae8a244f67783ae89b60caafc9f5cf318ada7", - "https://bcr.bazel.build/modules/rules_python/1.3.0/MODULE.bazel": "8361d57eafb67c09b75bf4bbe6be360e1b8f4f18118ab48037f2bd50aa2ccb13", - "https://bcr.bazel.build/modules/rules_python/1.3.0/source.json": "25932f917cd279c7baefa6cb1d3fa8750a7a29de522024449b19af6eab51f4a0", - "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c", - "https://bcr.bazel.build/modules/rules_shell/0.3.0/MODULE.bazel": "de4402cd12f4cc8fda2354fce179fdb068c0b9ca1ec2d2b17b3e21b24c1a937b", - "https://bcr.bazel.build/modules/rules_shell/0.3.0/source.json": "c55ed591aa5009401ddf80ded9762ac32c358d2517ee7820be981e2de9756cf3", - "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8", - "https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c", - "https://bcr.bazel.build/modules/stardoc/0.5.6/MODULE.bazel": "c43dabc564990eeab55e25ed61c07a1aadafe9ece96a4efabb3f8bf9063b71ef", - "https://bcr.bazel.build/modules/stardoc/0.7.0/MODULE.bazel": "05e3d6d30c099b6770e97da986c53bd31844d7f13d41412480ea265ac9e8079c", - "https://bcr.bazel.build/modules/stardoc/0.7.1/MODULE.bazel": "3548faea4ee5dda5580f9af150e79d0f6aea934fc60c1cc50f4efdd9420759e7", - "https://bcr.bazel.build/modules/stardoc/0.7.2/MODULE.bazel": "fc152419aa2ea0f51c29583fab1e8c99ddefd5b3778421845606ee628629e0e5", - "https://bcr.bazel.build/modules/stardoc/0.7.2/source.json": "58b029e5e901d6802967754adf0a9056747e8176f017cfe3607c0851f4d42216", - "https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.1/MODULE.bazel": "5e463fbfba7b1701d957555ed45097d7f984211330106ccd1352c6e0af0dcf91", - "https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.1/source.json": "32bd87e5f4d7acc57c5b2ff7c325ae3061d5e242c0c4c214ae87e0f1c13e54cb", - "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43", - "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0", - "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "eec517b5bbe5492629466e11dae908d043364302283de25581e3eb944326c4ca", - "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/source.json": "22bc55c47af97246cfc093d0acf683a7869377de362b5d1c552c2c2e16b7a806", - "https://bcr.bazel.build/modules/zlib/1.3.1/MODULE.bazel": "751c9940dcfe869f5f7274e1295422a34623555916eb98c174c1e945594bf198" + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/abseil-cpp/20211102.0/MODULE.bazel": "70390338f7a5106231d20620712f7cccb659cd0e9d073d1991c038eb9fc57589", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/abseil-cpp/20230125.1/MODULE.bazel": "89047429cb0207707b2dface14ba7f8df85273d484c2572755be4bab7ce9c3a0", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/abseil-cpp/20230802.0.bcr.1/MODULE.bazel": "1c8cec495288dccd14fdae6e3f95f772c1c91857047a098fad772034264cc8cb", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/abseil-cpp/20230802.0/MODULE.bazel": "d253ae36a8bd9ee3c5955384096ccb6baf16a1b1e93e858370da0a3b94f77c16", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/abseil-cpp/20230802.1/MODULE.bazel": "fa92e2eb41a04df73cdabeec37107316f7e5272650f81d6cc096418fe647b915", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/abseil-cpp/20240116.1/MODULE.bazel": "37bcdb4440fbb61df6a1c296ae01b327f19e9bb521f9b8e26ec854b6f97309ed", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/abseil-cpp/20240116.1/source.json": "9be551b8d4e3ef76875c0d744b5d6a504a27e3ae67bc6b28f46415fd2d2957da", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.1.1/MODULE.bazel": "27b8c79ef57efe08efccbd9dd6ef70d61b4798320b8d3c134fd571f78963dbcd", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.15.0/MODULE.bazel": "d38ff6e517149dc509406aca0db3ad1efdd890a85e049585b7234d04238e2a4d", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.17.0/MODULE.bazel": "039de32d21b816b47bd42c778e0454217e9c9caac4a3cf8e15c7231ee3ddee4d", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.18.0/MODULE.bazel": "1be0ae2557ab3a72a57aeb31b29be347bcdc5d2b1eb1e70f39e3851a7e97041a", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.19.0/MODULE.bazel": "59adcdf28230d220f0067b1f435b8537dd033bfff8db21335ef9217919c7fb58", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.21.0/MODULE.bazel": "675642261665d8eea09989aa3b8afb5c37627f1be178382c320d1b46afba5e3b", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.27.0/MODULE.bazel": "621eeee06c4458a9121d1f104efb80f39d34deff4984e778359c60eaf1a8cb65", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.28.0/MODULE.bazel": "4b4200e6cbf8fa335b2c3f43e1d6ef3e240319c33d43d60cc0fbd4b87ece299d", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.3.0/MODULE.bazel": "cdcafe83ec318cda34e02948e81d790aab8df7a929cec6f6969f13a489ccecd9", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.30.0/MODULE.bazel": "a14b62d05969a293b80257e72e597c2da7f717e1e69fa8b339703ed6731bec87", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.30.0/source.json": "b07e17f067fe4f69f90b03b36ef1e08fe0d1f3cac254c1241a1818773e3423bc", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.9.0/MODULE.bazel": "885151d58d90d8d9c811eb75e3288c11f850e1d6b481a8c9f766adee4712358b", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_skylib/1.1.1/MODULE.bazel": "1add3e7d93ff2e6998f9e118022c84d163917d912f5afafb3058e3d2f1545b5e", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_skylib/1.2.0/MODULE.bazel": "44fe84260e454ed94ad326352a698422dbe372b21a1ac9f3eab76eb531223686", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_skylib/1.2.1/MODULE.bazel": "f35baf9da0efe45fa3da1696ae906eea3d615ad41e2e3def4aeb4e8bc0ef9a7a", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_skylib/1.3.0/MODULE.bazel": "20228b92868bf5cfc41bda7afc8a8ba2a543201851de39d990ec957b513579c5", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_skylib/1.4.1/MODULE.bazel": "a0dcb779424be33100dcae821e9e27e4f2901d9dfd5333efe5ac6a8d7ab75e1d", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_skylib/1.4.2/MODULE.bazel": "3bd40978e7a1fac911d5989e6b09d8f64921865a45822d8b09e815eaa726a651", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_skylib/1.5.0/MODULE.bazel": "32880f5e2945ce6a03d1fbd588e9198c0a959bb42297b2cfaf1685b7bc32e138", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_skylib/1.7.0/MODULE.bazel": "0db596f4563de7938de764cc8deeabec291f55e8ec15299718b93c4423e9796d", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_skylib/1.7.1/source.json": "f121b43eeefc7c29efbd51b83d08631e2347297c95aac9764a701f2a6a2bb953", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/google_benchmark/1.8.2/MODULE.bazel": "a70cf1bba851000ba93b58ae2f6d76490a9feb74192e57ab8e8ff13c34ec50cb", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "22c31a561553727960057361aa33bf20fb2e98584bc4fec007906e27053f80c6", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/googletest/1.14.0.bcr.1/source.json": "41e9e129f80d8c8bf103a7acc337b76e54fad1214ac0a7084bf24f4cd924b8b4", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/jsoncpp/1.9.5/MODULE.bazel": "31271aedc59e815656f5736f282bb7509a97c7ecb43e927ac1a37966e0578075", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/jsoncpp/1.9.5/source.json": "4108ee5085dd2885a341c7fab149429db457b3169b86eb081fa245eadf69169d", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/nlohmann_json/3.6.1/MODULE.bazel": "6f7b417dcc794d9add9e556673ad25cb3ba835224290f4f848f8e2db1e1fca74", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/nlohmann_json/3.6.1/source.json": "f448c6e8963fdfa7eb831457df83ad63d3d6355018f6574fb017e8169deb43a9", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/platforms/0.0.11/MODULE.bazel": "0daefc49732e227caa8bfa834d65dc52e8cc18a2faf80df25e8caea151a9413f", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/platforms/0.0.11/source.json": "f7e188b79ebedebfe75e9e1d098b8845226c7992b307e28e1496f23112e8fc29", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/protobuf/27.0/MODULE.bazel": "7873b60be88844a0a1d8f80b9d5d20cfbd8495a689b8763e76c6372998d3f64c", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/protobuf/27.1/MODULE.bazel": "703a7b614728bb06647f965264967a8ef1c39e09e8f167b3ca0bb1fd80449c0d", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/protobuf/29.0-rc2/MODULE.bazel": "6241d35983510143049943fc0d57937937122baf1b287862f9dc8590fc4c37df", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/protobuf/29.0-rc3/MODULE.bazel": "33c2dfa286578573afc55a7acaea3cada4122b9631007c594bf0729f41c8de92", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/protobuf/29.0/MODULE.bazel": "319dc8bf4c679ff87e71b1ccfb5a6e90a6dbc4693501d471f48662ac46d04e4e", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/protobuf/29.0/source.json": "b857f93c796750eef95f0d61ee378f3420d00ee1dd38627b27193aa482f4f981", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/pybind11_bazel/2.11.1/MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/pybind11_bazel/2.11.1/source.json": "be4789e951dd5301282729fe3d4938995dc4c1a81c2ff150afc9f1b0504c6022", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/re2/2023-09-01/MODULE.bazel": "cb3d511531b16cfc78a225a9e2136007a48cf8a677e4264baeab57fe78a80206", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/re2/2023-09-01/source.json": "e044ce89c2883cd957a2969a43e79f7752f9656f6b20050b62f90ede21ec6eb4", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_android/0.1.1/MODULE.bazel": "48809ab0091b07ad0182defb787c4c5328bd3a278938415c00a7b69b50c4d3a8", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_android/0.1.1/source.json": "e6986b41626ee10bdc864937ffb6d6bf275bb5b9c65120e6137d56e6331f089e", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.0.10/MODULE.bazel": "ec1705118f7eaedd6e118508d3d26deba2a4e76476ada7e0e3965211be012002", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.0.13/MODULE.bazel": "0e8529ed7b323dad0775ff924d2ae5af7640b23553dfcd4d34344c7e7a867191", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.0.14/MODULE.bazel": "5e343a3aac88b8d7af3b1b6d2093b55c347b8eefc2e7d1442f7a02dc8fea48ac", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.0.15/MODULE.bazel": "6704c35f7b4a72502ee81f61bf88706b54f06b3cbe5558ac17e2e14666cd5dcc", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.0.16/MODULE.bazel": "7661303b8fc1b4d7f532e54e9d6565771fea666fbdf839e0a86affcd02defe87", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.0.6/MODULE.bazel": "abf360251023dfe3efcef65ab9d56beefa8394d4176dd29529750e1c57eaa33f", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.1.2/MODULE.bazel": "557ddc3a96858ec0d465a87c0a931054d7dcfd6583af2c7ed3baf494407fd8d0", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.1.2/source.json": "53fcb09b5816c83ca60d9d7493faf3bfaf410dfc2f15deb52d6ddd146b8d43f0", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_fuzzing/0.5.2/source.json": "c8b1e2c717646f1702290959a3302a178fb639d987ab61d548105019f11e527e", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/5.3.5/MODULE.bazel": "a4ec4f2db570171e3e5eb753276ee4b389bae16b96207e9d3230895c99644b86", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/6.0.0/MODULE.bazel": "8a43b7df601a7ec1af61d79345c17b31ea1fedc6711fd4abfd013ea612978e39", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/6.4.0/MODULE.bazel": "e986a9fe25aeaa84ac17ca093ef13a4637f6107375f64667a15999f77db6c8f6", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/6.5.2/MODULE.bazel": "1d440d262d0e08453fa0c4d8f699ba81609ed0e9a9a0f02cd10b3e7942e61e31", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/7.10.0/MODULE.bazel": "530c3beb3067e870561739f1144329a21c851ff771cd752a49e06e3dc9c2e71a", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/7.12.2/MODULE.bazel": "579c505165ee757a4280ef83cda0150eea193eed3bef50b1004ba88b99da6de6", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/7.2.0/MODULE.bazel": "06c0334c9be61e6cef2c8c84a7800cef502063269a5af25ceb100b192453d4ab", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/7.3.2/MODULE.bazel": "50dece891cfdf1741ea230d001aa9c14398062f2b7c066470accace78e412bc2", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/8.14.0/MODULE.bazel": "717717ed40cc69994596a45aec6ea78135ea434b8402fb91b009b9151dd65615", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/8.14.0/source.json": "8a88c4ca9e8759da53cddc88123880565c520503321e2566b4e33d0287a3d4bc", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/8.3.2/MODULE.bazel": "7336d5511ad5af0b8615fdc7477535a2e4e723a357b6713af439fe8cf0195017", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/8.5.1/MODULE.bazel": "d8a9e38cc5228881f7055a6079f6f7821a073df3744d441978e7a43e20226939", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_jvm_external/5.2/MODULE.bazel": "d9351ba35217ad0de03816ef3ed63f89d411349353077348a45348b096615036", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_jvm_external/5.3/MODULE.bazel": "bf93870767689637164657731849fb887ad086739bd5d360d90007a581d5527d", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_jvm_external/6.1/MODULE.bazel": "75b5fec090dbd46cf9b7d8ea08cf84a0472d92ba3585b476f44c326eda8059c4", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_jvm_external/6.3/MODULE.bazel": "c998e060b85f71e00de5ec552019347c8bca255062c990ac02d051bb80a38df0", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_jvm_external/6.3/source.json": "6f5f5a5a4419ae4e37c35a5bb0a6ae657ed40b7abc5a5189111b47fcebe43197", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_kotlin/1.9.0/MODULE.bazel": "ef85697305025e5a61f395d4eaede272a5393cee479ace6686dba707de804d59", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_kotlin/1.9.6/MODULE.bazel": "d269a01a18ee74d0335450b10f62c9ed81f2321d7958a2934e44272fe82dcef3", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_kotlin/1.9.6/source.json": "2faa4794364282db7c06600b7e5e34867a564ae91bda7cae7c29c64e9466b7d5", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_license/1.0.0/MODULE.bazel": "a7fda60eefdf3d8c827262ba499957e4df06f659330bbe6cdbdb975b768bb65c", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_license/1.0.0/source.json": "a52c89e54cc311196e478f8382df91c15f7a2bfdf4c6cd0e2675cc2ff0b56efb", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_pkg/1.0.1/MODULE.bazel": "5b1df97dbc29623bccdf2b0dcd0f5cb08e2f2c9050aab1092fd39a41e82686ff", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_pkg/1.0.1/source.json": "bd82e5d7b9ce2d31e380dd9f50c111d678c3bdaca190cb76b0e1c71b05e1ba8a", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_proto/4.0.0/MODULE.bazel": "a7a7b6ce9bee418c1a760b3d84f83a299ad6952f9903c67f19e4edd964894e06", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_proto/6.0.2/MODULE.bazel": "ce916b775a62b90b61888052a416ccdda405212b6aaeb39522f7dc53431a5e73", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_proto/7.0.2/MODULE.bazel": "bf81793bd6d2ad89a37a40693e56c61b0ee30f7a7fdbaf3eabbf5f39de47dea2", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_proto/7.0.2/source.json": "1e5e7260ae32ef4f2b52fd1d0de8d03b606a44c91b694d2f1afb1d3b28a48ce1", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_python/0.23.1/MODULE.bazel": "49ffccf0511cb8414de28321f5fcf2a31312b47c40cc21577144b7447f2bf300", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_python/0.25.0/MODULE.bazel": "72f1506841c920a1afec76975b35312410eea3aa7b63267436bfb1dd91d2d382", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_python/0.27.1/MODULE.bazel": "65dc875cc1a06c30d5bbdba7ab021fd9e551a6579e408a3943a61303e2228a53", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_python/0.28.0/MODULE.bazel": "cba2573d870babc976664a912539b320cbaa7114cd3e8f053c720171cde331ed", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_python/0.31.0/MODULE.bazel": "93a43dc47ee570e6ec9f5779b2e64c1476a6ce921c48cc9a1678a91dd5f8fd58", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_python/0.40.0/MODULE.bazel": "9d1a3cd88ed7d8e39583d9ffe56ae8a244f67783ae89b60caafc9f5cf318ada7", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_python/1.3.0/MODULE.bazel": "8361d57eafb67c09b75bf4bbe6be360e1b8f4f18118ab48037f2bd50aa2ccb13", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_python/1.3.0/source.json": "25932f917cd279c7baefa6cb1d3fa8750a7a29de522024449b19af6eab51f4a0", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_shell/0.3.0/MODULE.bazel": "de4402cd12f4cc8fda2354fce179fdb068c0b9ca1ec2d2b17b3e21b24c1a937b", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_shell/0.3.0/source.json": "c55ed591aa5009401ddf80ded9762ac32c358d2517ee7820be981e2de9756cf3", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/stardoc/0.5.6/MODULE.bazel": "c43dabc564990eeab55e25ed61c07a1aadafe9ece96a4efabb3f8bf9063b71ef", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/stardoc/0.7.0/MODULE.bazel": "05e3d6d30c099b6770e97da986c53bd31844d7f13d41412480ea265ac9e8079c", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/stardoc/0.7.1/MODULE.bazel": "3548faea4ee5dda5580f9af150e79d0f6aea934fc60c1cc50f4efdd9420759e7", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/stardoc/0.7.2/MODULE.bazel": "fc152419aa2ea0f51c29583fab1e8c99ddefd5b3778421845606ee628629e0e5", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/stardoc/0.7.2/source.json": "58b029e5e901d6802967754adf0a9056747e8176f017cfe3607c0851f4d42216", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/swift_argument_parser/1.3.1.1/MODULE.bazel": "5e463fbfba7b1701d957555ed45097d7f984211330106ccd1352c6e0af0dcf91", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/swift_argument_parser/1.3.1.1/source.json": "32bd87e5f4d7acc57c5b2ff7c325ae3061d5e242c0c4c214ae87e0f1c13e54cb", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "eec517b5bbe5492629466e11dae908d043364302283de25581e3eb944326c4ca", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/zlib/1.3.1.bcr.5/source.json": "22bc55c47af97246cfc093d0acf683a7869377de362b5d1c552c2c2e16b7a806", + "https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/zlib/1.3.1/MODULE.bazel": "751c9940dcfe869f5f7274e1295422a34623555916eb98c174c1e945594bf198" }, "selectedYankedVersions": {}, "moduleExtensions": { diff --git a/README.md b/README.md index 22582050..79f325aa 100644 --- a/README.md +++ b/README.md @@ -1,72 +1,116 @@ -# 👻 Ghostgram iOS +# Telegram iOS Source Code Compilation Guide -

- Ghostgram Logo -

+We welcome all developers to use our API and source code to create applications on our platform. +There are several things we require from **all developers** for the moment. -

- The ultimate Telegram fork for privacy, control, and freedom. -

+# Creating your Telegram Application -

- Telegram Support - Platform iOS - License GPLv2 -

+1. [**Obtain your own api_id**](https://core.telegram.org/api/obtaining_api_id) for your application. +2. Please **do not** use the name Telegram for your app — or make sure your users understand that it is unofficial. +3. Kindly **do not** use our standard logo (white paper plane in a blue circle) as your app's logo. +3. Please study our [**security guidelines**](https://core.telegram.org/mtproto/security_guidelines) and take good care of your users' data and privacy. +4. Please remember to publish **your** code too in order to comply with the licences. ---- +# Quick Compilation Guide -## 🌟 Key Features +## Get the Code -### 🛡️ Anti-Delete System -*Never miss a message again. Ghostgram keeps what others try to hide.* -- **Local Archive:** Automatically saves messages deleted by the sender for everyone. -- **Media Preservation:** Photos and videos from deleted messages are archived locally. -- **Edit History:** View original content of edited messages. -- **Visual Indicators:** Deleted messages are clearly marked with a 🗑️ icon. +``` +git clone --recursive -j8 https://github.com/TelegramMessenger/Telegram-iOS.git +``` -### 👤 Ghost Mode -- **Invisible Reading:** Read messages without sending "Read" receipts (single checkmark). -- **Stealth Stories:** View stories anonymously without appearing in the viewer list. -- **Hidden Input:** Hide "Typing..." status from your contacts. -- **Online Privacy:** Hide your online status or use **Force Offline** to always appear as "last seen recently". +## Setup Xcode -### 🎙️ Advanced Audio Tools -- **Free Transcription:** Get text from voice and video messages without Telegram Premium (On-device processing). -- **Voice Morpher:** Real-time voice changer for your outgoing audio messages. +Install Xcode (directly from https://developer.apple.com/download/applications or using the App Store). -### 🔓 Bypass Limits -- **Copy Protection Bypass:** Forward or save content from channels that have restricted it. -- **Infinite View-Once:** View-once media stays available as long as you need. -- **Secret Screenshots:** Take screenshots in secret chats without notification. +## Adjust Configuration -### 🛠️ More Features -- **Device Spoofing:** Change how your device appears to others. -- **Ad Blocker:** Say goodbye to sponsored messages in channels. -- **Always Online:** Keep your status active even when the app is in the background. +1. Generate a random identifier: +``` +openssl rand -hex 8 +``` +2. Create a new Xcode project. Use `Telegram` as the Product Name. Use `org.{identifier from step 1}` as the Organization Identifier. +3. Open `Keychain Access` and navigate to `Certificates`. Locate `Apple Development: your@email.address (XXXXXXXXXX)` and double tap the certificate. Under `Details`, locate `Organizational Unit`. This is the Team ID. +4. Edit `build-system/template_minimal_development_configuration.json`. Use data from the previous steps. ---- +## Generate an Xcode project -## 🛠 Installation & Building +``` +python3 build-system/Make/Make.py \ + --cacheDir="$HOME/telegram-bazel-cache" \ + generateProject \ + --configurationPath=build-system/template_minimal_development_configuration.json \ + --xcodeManagedCodesigning +``` -Ghostgram is a developer-centric fork. To build it from source, follow our detailed guide: +# Advanced Compilation Guide -📖 **[Build & Compilation Guide](BUILD.md)** +## Xcode ---- +1. Copy and edit `build-system/appstore-configuration.json`. +2. Copy `build-system/fake-codesigning`. Create and download provisioning profiles, using the `profiles` folder as a reference for the entitlements. +3. Generate an Xcode project: +``` +python3 build-system/Make/Make.py \ + --cacheDir="$HOME/telegram-bazel-cache" \ + generateProject \ + --configurationPath=configuration_from_step_1.json \ + --codesigningInformationPath=directory_from_step_2 +``` -## ⚠️ Disclaimer & Legal +## IPA -> **Educational Use Only:** This is a modified version of the Telegram client. Use it at your own risk. The developer is not responsible for any account bans or data loss. +1. Repeat the steps from the previous section. Use distribution provisioning profiles. +2. Run: +``` +python3 build-system/Make/Make.py \ + --cacheDir="$HOME/telegram-bazel-cache" \ + build \ + --configurationPath=...see previous section... \ + --codesigningInformationPath=...see previous section... \ + --buildNumber=100001 \ + --configuration=release_arm64 +``` -- **Unofficial:** This project is not affiliated with, endorsed by, or in any way officially connected with Telegram Messenger Inc. -- **Terms of Service:** Using third-party clients may violate Telegram's ToS. -- **License:** Based on [Telegram-iOS](https://github.com/TelegramMessenger/Telegram-iOS), licensed under **GPLv2**. +# FAQ ---- +## Xcode is stuck at "build-request.json not updated yet" -### 👨‍💻 Support & Community -Report issues or join the discussion: -- **Telegram:** [@ceopoco](https://t.me/ceosppw) +Occasionally, you might observe the following message in your build log: +``` +"/Users/xxx/Library/Developer/Xcode/DerivedData/Telegram-xxx/Build/Intermediates.noindex/XCBuildData/xxx.xcbuilddata/build-request.json" not updated yet, waiting... +``` -**Made with 🖤 for the community.** +Should this occur, simply cancel the ongoing build and initiate a new one. + +## Telegram_xcodeproj: no such package + +Following a system restart, the auto-generated Xcode project might encounter a build failure accompanied by this error: +``` +ERROR: Skipping '@rules_xcodeproj_generated//generator/Telegram/Telegram_xcodeproj:Telegram_xcodeproj': no such package '@rules_xcodeproj_generated//generator/Telegram/Telegram_xcodeproj': BUILD file not found in directory 'generator/Telegram/Telegram_xcodeproj' of external repository @rules_xcodeproj_generated. Add a BUILD file to a directory to mark it as a package. +``` + +If you encounter this issue, re-run the project generation steps in the README. + + +# Tips + +## Codesigning is not required for simulator-only builds + +Add `--disableProvisioningProfiles`: +``` +python3 build-system/Make/Make.py \ + --cacheDir="$HOME/telegram-bazel-cache" \ + generateProject \ + --configurationPath=path-to-configuration.json \ + --codesigningInformationPath=path-to-provisioning-data \ + --disableProvisioningProfiles +``` + +## Versions + +Each release is built using a specific Xcode version (see `versions.json`). The helper script checks the versions of the installed software and reports an error if they don't match the ones specified in `versions.json`. It is possible to bypass these checks: + +``` +python3 build-system/Make/Make.py --overrideXcodeVersion build ... # Don't check the version of Xcode +``` diff --git a/Telegram/BUILD b/Telegram/BUILD index 74b3ecae..4b84ab9c 100644 --- a/Telegram/BUILD +++ b/Telegram/BUILD @@ -8,8 +8,6 @@ load("@build_bazel_rules_apple//apple:ios.bzl", "ios_application", "ios_extension", "ios_framework", - "ios_unit_test", - "ios_ui_test", ) load("@build_bazel_rules_apple//apple:resources.bzl", @@ -22,7 +20,6 @@ load("@build_bazel_rules_swift//swift:swift.bzl", load( "@rules_xcodeproj//xcodeproj:defs.bzl", - "top_level_target", "top_level_targets", "xcodeproj", "xcode_provisioning_profile", @@ -338,6 +335,7 @@ filegroup( objc_library( name = "Main", + module_name = "Main", srcs = [ "Telegram-iOS/main.m" ], @@ -345,6 +343,7 @@ objc_library( swift_library( name = "Lib", + module_name = "Lib", srcs = glob([ "Telegram-iOS/Application.swift", ]), @@ -1810,7 +1809,7 @@ ios_application( #"//third-party/boringssl:ssl", #"//third-party/boringssl:crypto", #"//submodules/TelegramVoip", - #"//third-party/recaptcha:RecaptchaEnterprise", + #"//third-party/recaptcha:RecaptchaEnterpriseSDK", "//submodules/TelegramUI", ], ) diff --git a/Telegram/Telegram-iOS/Config-AppStoreLLC.xcconfig b/Telegram/Telegram-iOS/Config-AppStoreLLC.xcconfig index a220df74..76c1f1e5 100644 --- a/Telegram/Telegram-iOS/Config-AppStoreLLC.xcconfig +++ b/Telegram/Telegram-iOS/Config-AppStoreLLC.xcconfig @@ -5,4 +5,4 @@ APP_SPECIFIC_URL_SCHEME=tgapp GLOBAL_CONSTANTS = APP_CONFIG_IS_INTERNAL_BUILD=false APP_CONFIG_IS_APPSTORE_BUILD=true APP_CONFIG_APPSTORE_ID=686449807 APP_SPECIFIC_URL_SCHEME="\"$(APP_SPECIFIC_URL_SCHEME)\"" GCC_PREPROCESSOR_DEFINITIONS = $(inherited) $(GLOBAL_CONSTANTS) -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) APP_CONFIG_API_ID=YOUR_API_ID APP_CONFIG_API_HASH="\"YOUR_API_HASH\"" APP_CONFIG_HOCKEYAPP_ID="\"ad8831329ffc8f8aff9a2b0b86558b24\"" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) APP_CONFIG_API_ID=8 APP_CONFIG_API_HASH="\"7245de8e747a0d6fbe11f7cc14fcc0bb\"" APP_CONFIG_HOCKEYAPP_ID="\"ad8831329ffc8f8aff9a2b0b86558b24\"" diff --git a/Telegram/Telegram-iOS/Config-Fork.xcconfig b/Telegram/Telegram-iOS/Config-Fork.xcconfig index 2e78f776..c3192251 100644 --- a/Telegram/Telegram-iOS/Config-Fork.xcconfig +++ b/Telegram/Telegram-iOS/Config-Fork.xcconfig @@ -5,4 +5,4 @@ APP_SPECIFIC_URL_SCHEME=tgfork GLOBAL_CONSTANTS = APP_CONFIG_IS_INTERNAL_BUILD=false APP_CONFIG_IS_APPSTORE_BUILD=true APP_CONFIG_APPSTORE_ID=0 APP_SPECIFIC_URL_SCHEME="\"$(APP_SPECIFIC_URL_SCHEME)\"" GCC_PREPROCESSOR_DEFINITIONS = $(inherited) $(GLOBAL_CONSTANTS) -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) APP_CONFIG_API_ID=YOUR_API_ID APP_CONFIG_API_HASH="\"YOUR_API_HASH\"" APP_CONFIG_HOCKEYAPP_ID="\"\"" \ No newline at end of file +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) APP_CONFIG_API_ID=8 APP_CONFIG_API_HASH="\"7245de8e747a0d6fbe11f7cc14fcc0bb\"" APP_CONFIG_HOCKEYAPP_ID="\"\"" \ No newline at end of file diff --git a/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/AppIconLLC.appiconset/Contents.json b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/AppIconLLC.appiconset/Contents.json index 4d654570..221e2b44 100644 --- a/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/AppIconLLC.appiconset/Contents.json +++ b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/AppIconLLC.appiconset/Contents.json @@ -1,115 +1,116 @@ { - "images" : [ + "images": [ { - "filename" : "BlueNotificationIcon@2x.png", - "idiom" : "iphone", - "scale" : "2x", - "size" : "20x20" + "filename": "GhostIcon@40x40.png", + "idiom": "iphone", + "scale": "2x", + "size": "20x20" }, { - "filename" : "BlueNotificationIcon@3x.png", - "idiom" : "iphone", - "scale" : "3x", - "size" : "20x20" + "filename": "GhostIcon@60x60.png", + "idiom": "iphone", + "scale": "3x", + "size": "20x20" }, { - "filename" : "Simple@58x58.png", - "idiom" : "iphone", - "scale" : "2x", - "size" : "29x29" + "filename": "GhostIcon@58x58.png", + "idiom": "iphone", + "scale": "2x", + "size": "29x29" }, { - "filename" : "Simple@87x87.png", - "idiom" : "iphone", - "scale" : "3x", - "size" : "29x29" + "filename": "GhostIcon@87x87.png", + "idiom": "iphone", + "scale": "3x", + "size": "29x29" }, { - "filename" : "Simple@80x80.png", - "idiom" : "iphone", - "scale" : "2x", - "size" : "40x40" + "filename": "GhostIcon@80x80.png", + "idiom": "iphone", + "scale": "2x", + "size": "40x40" }, { - "filename" : "BlueIcon@2x-1.png", - "idiom" : "iphone", - "scale" : "3x", - "size" : "40x40" + "filename": "GhostIcon@120x120.png", + "idiom": "iphone", + "scale": "3x", + "size": "40x40" }, { - "filename" : "BlueIcon@2x.png", - "idiom" : "iphone", - "scale" : "2x", - "size" : "60x60" + "filename": "GhostIcon@120x120.png", + "idiom": "iphone", + "scale": "2x", + "size": "60x60" }, { - "filename" : "BlueIcon@3x.png", - "idiom" : "iphone", - "scale" : "3x", - "size" : "60x60" + "filename": "GhostIcon@180x180.png", + "idiom": "iphone", + "scale": "3x", + "size": "60x60" }, { - "filename" : "BlueNotificationIcon.png", - "idiom" : "ipad", - "scale" : "1x", - "size" : "20x20" + "filename": "GhostIcon@20x20.png", + "idiom": "ipad", + "scale": "1x", + "size": "20x20" }, { - "filename" : "BlueNotificationIcon@2x-1.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "20x20" + "filename": "GhostIcon@40x40.png", + "idiom": "ipad", + "scale": "2x", + "size": "20x20" }, { - "filename" : "Simple@29x29.png", - "idiom" : "ipad", - "scale" : "1x", - "size" : "29x29" + "filename": "GhostIcon@29x29.png", + "idiom": "ipad", + "scale": "1x", + "size": "29x29" }, { - "filename" : "Simple@58x58-1.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "29x29" + "filename": "GhostIcon@58x58.png", + "idiom": "ipad", + "scale": "2x", + "size": "29x29" }, { - "filename" : "Simple@40x40-1.png", - "idiom" : "ipad", - "scale" : "1x", - "size" : "40x40" + "filename": "GhostIcon@40x40.png", + "idiom": "ipad", + "scale": "1x", + "size": "40x40" }, { - "filename" : "Simple@80x80-1.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "40x40" + "filename": "GhostIcon@80x80.png", + "idiom": "ipad", + "scale": "2x", + "size": "40x40" }, { - "idiom" : "ipad", - "scale" : "1x", - "size" : "76x76" + "filename": "GhostIcon@76x76.png", + "idiom": "ipad", + "scale": "1x", + "size": "76x76" }, { - "filename" : "BlueIconIpad@2x.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "76x76" + "filename": "GhostIcon@152x152.png", + "idiom": "ipad", + "scale": "2x", + "size": "76x76" }, { - "filename" : "BlueIconLargeIpad@2x.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "83.5x83.5" + "filename": "GhostIcon@167x167.png", + "idiom": "ipad", + "scale": "2x", + "size": "83.5x83.5" }, { - "filename" : "Simple-iTunesArtwork.png", - "idiom" : "ios-marketing", - "scale" : "1x", - "size" : "1024x1024" + "filename": "GhostIcon@1024x1024.png", + "idiom": "ios-marketing", + "scale": "1x", + "size": "1024x1024" } ], - "info" : { - "author" : "xcode", - "version" : 1 + "info": { + "author": "xcode", + "version": 1 } -} +} \ No newline at end of file diff --git a/Telegram/Telegram-iOS/IconDefault-60@2x.png b/Telegram/Telegram-iOS/IconDefault-60@2x.png index 9525324b1e6c9cb9a49f8718c8154361c7756a3f..bc3738e3aa56eba8cc626acff7beb51b94dad9ab 100644 GIT binary patch literal 11254 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 literal 4612 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¨nK`?I z=3Z0G0062H3Ziv15i`6;EJN+xitX23N&zdA8k?A`{xUzvGqq9M4dE_gG1n=W)6WMw zLHChkfD4VCogX8O5!4M9p6=v!mg4Jj2n&@TN4ayi0*%unZcM5nI+L8aN#@QDqb z_2M-tA#g?%H7K@-X#BNT)ZX5tg_zQx|$#(^DT)P zyI}{TcfP`Bad?bbAw{-ZPK+cx;Z%1q6htB}Jc5@-UXlZSL^uFk^zSHe2cSXoSepqK z6rQwE%p{;YF)>IU!e2@8D*6<$h{^0Ns~7k8M_j#{$||h8 zUwqb_RKNtn*|kPd8;;O1p@b^ehqC~{`_q=pDoK0)e(Fcml#bE0h0{{FU8EvitA1u~ z|Hz@VOH=`{L-URI-~49q=@|)FtDWR?b`nM*aUE%eFdLd~u_9Tz!;&iA@k1Nq)4_h1 zWQeERQo)|gf5<&i`u;3q9T$f_?0sHFvDVwA*A!QOOn)Rj1geFH$9H$9YmqK@2gXTQ zdXFtUPhFYQx_@`7MH5C(C_cht%L=c4;hpbJk8t$WpIT244DP@l(-u~OU?F#JoYBdb zkC6;g!!RF%Pm4Vplg&LfV#1jj#v?gx^n#$PP80U{uH7N!=%Fddl z_xaaT@*YR_fSj>+y7vsyu3zaGM;q4KUdEEKgj?4gCr#e6yQI<@7t9w29FPqDgPx- zUG(#D_42+7fn85w^`F-eSL(0B*|HpA{#`ypJ*I0PiAbPuSguH_NAca4M~&*t6c1ZC zPgr;206uj)Secs|$trVKUVh*1p&@q~Q%-l@T4R=)5-Ih_=!1qpxV!(Fwr-@JUhlMZ zUnBU`s;a1H^Dr&}JKr3UE07{I?m_=`2}_!x<)|qtl3y$n_D2@l3r4V4L#ne0g6u+1 z`v+SnF;19R{*ub_50z3;zoV9Yr|s{LT>2x~7txRod1P~za?aj-v>Ql$WXS6vLv0xD zLm|u*VV4kr0I-de2~x`L#PyZxokTRGH8*`e9t}T80;(6M-N6a}iiEvR61wMY-C6;^ zQx+xS3k^78rtFI(!M%@ITct0>SfjAF4@R8sW@XspXTaB#jAR-vJ|$KGRSu3B*qRCU z=hSq5uk|km0$rX6Z+|mz55dZYM_G6hV@@fpQYw;Jcn|#F+}V32Xvk9U|MwP1X1}5; XH}HuWAV=BQqte$g)-Jv85dMDvewtE$ diff --git a/Telegram/Telegram-iOS/IconDefault-76.png b/Telegram/Telegram-iOS/IconDefault-76.png index 07de560340fa1061d065ef87ef4d81f445d1f67a..88823054503f362ab25310c6e59af362c03f69f2 100644 GIT binary patch literal 4913 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 delta 2843 zcmV+$3*_{%CZQIPBYz6$NklUQ+w)-Ia5cf9&e|RoCpyyqT3*(*L#p2R?e1{Ld`evt-Yb zt?~AJcYIDm^qb1a^V{1V%n#kZsr8!m9fElvKZM}dm0=|0yMN+H{x>aY@Fzx@B99id zo|6?kb8X!N75A1&#QB!4r6FV4o$Efl6Fq0?bfq#>C=LI{G2oQtyqXp83ic*%IO zeM)WQ{I&kmSAS!yG-=4Lq>_(qNtz3?{5S#!?VBZcc;dHh2w;xWR{0b%J+)+AFmZLZ z566sL?g1f8Ktc$T5H=x&QHv`i`U@k~UY+B^FuQ+A+&pZEV@BX4P?ESLj2zcdE5x9i zq#Zd(4Yk*WhWs7Nnxb8TJ4=Vu5net&GyJvNBE)j9=R5h zXMa0$sS{&ou;e5|Ye(z(=&lrlAPouO7?Ormm4g)K;uXz5mjxOEvA<7J-4atdS0Rj@ z!IC#+xdzU+p$trk21Iv~#zqRG6(=b+#3kEP8$G#}wsrVL4m92!!0?fFX`KbHC}L4UQshHL#D!zd(LiRNMk46kd+A4~iL=2{2L zvywC6giu0VT~oH8iIXinIUA8!l=ce*<@Nn=ULt_^q71a zpP41@$W5yq59c|%O1OEogUg^NDSwQ-B+ZFVKN?!&I=*KX zHHR--W<#Gd7Fa4gk*?C3LveQ2IRgiNDA%d6kyJ=r5*j$W>3@wa?s7M0 z)%Xa-5}y`%`lLFXG2g8IowmRpMw*HgbJEe6=D!y5gJQgx56qHpG^K~Fn_nJO?%K{XZZ3Z z=98z^p#+5PTVoGK6FN5xL=%d0NrmK`>sM$ki8)T1QU{$(ri4loLJ;gdweG@c>Rums3yVIKr7c&$!bW7DtO=P{C-t|dD}BOSn; ztCyI+EOM`|50<;a>wnClA9s3&&#GsMX!2~HBbLbQS(N}+tzvXme$9}zwkcNcN zh}0<*lGL?}8%^Gb=8|u0aEL;w4-Kdk#?IjXuA_^+$Br|i5tue4G}cHegalF;IgYyn z_L^8dw%C(U{q6U<#SKy)6Dfv~&%)RlEQ!DC`%dm?dVkDVLrO*xLXZ?jj?0qsZ(7<| zf}vS#`<;)3_3k z{0B|GwN*Zgg)03qaK_JS;ZI@Q&Z9?HLFhuJ3`s*O)ycw=MBCe!H8%O9e?R)AJewj% zn6A#ksDBu^J5R#?y3@@Aun9>+LPwF*C=x=Dq#>C=vd}<6y(SpG%y$|+VSgk(quhJU zn9ll;k{HCO-F-Se(SH8y+M`BQQq+-Y(o~TUf+U1fNwJljLjy|E?jw%oFz3y##i-qX zVZ>Hz=$O$}NB-W?3xv>@q#+fOa&~7e7bBsgM}Jje$bb40mv4)l=zhfS6<~+?t%9T> zB~_3R3Q354)bnZNI1VBqG$Enyi=4aX#Y(p>X<&DqI{deCl7^I2PSTK>>b9i~$=&s` zGjBL?-mIF#e=S$&h+iv6VN~J@VMJ0G^|-8aXI5jd-T4ASOCUCMlHssl%Fvyps8o;? zMt?J2MhYXvAsS^ZG;FdFBklf|f|@)L?)jH~CTU3Ja0r5`IW(j$4V`4fFnj7ny_P`q zoav@Re=e1WMvzRYjHDr1MbeN$S~+Fp+0#vY4ENBI{Q8XFvZ(IRpGuYbnG{APt`tTm z#U^q-b8cBwj{)}53+=zoZ^k`7H; zcj!txNt#1`*o6`lqWs_VCQ*MRkzMB>wY~Y}tfm|0Ri8i2Fk(VQ?@?tC%mvep2)>+U t9<|+xG$i-D-M44So+W#h{2%)Ge-r=#aySc~_Hh6J002ovPDHLkV1j4ZU^oB( diff --git a/Telegram/Telegram-iOS/IconDefault-76@2x.png b/Telegram/Telegram-iOS/IconDefault-76@2x.png index d71dcd205e79c7a04a46d22d825afe3d4051f0e9..b7d12ca81293a715583c07dcad994aa833e583ba 100644 GIT binary patch literal 16589 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

alW5<@3Px1{(%)%n;GAzHGpp`rOM<$g&J59pG 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 Qv;Y7A07*qoM6N<$f(q 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 GBmM`psnnMM diff --git a/Telegram/Telegram-iOS/IconDefault-83.5@2x.png b/Telegram/Telegram-iOS/IconDefault-83.5@2x.png index f51ae17df908d7c08fdbb2759b8f0dff035483f5..a514afb2ba5dda9e7726c536543fa020e42ff3d8 100644 GIT binary patch literal 20245 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 diff --git a/Telegram/Telegram-iOS/IconDefault-Small-40.png b/Telegram/Telegram-iOS/IconDefault-Small-40.png index e2b1ba789097cd51299045900fe0ca0bc175e357..7425a6501d6e4acaddb5d4ac135948b99a8f4d49 100644 GIT binary patch delta 1672 zcmV;326y?j3YQI#BYyw^b5ch_0Itp)=>Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031 z000^Q000001E2u_0{{R30RRC20H6W@1ONa40RR91D4+uX1ONa40RR91C;$Ke0D9(T ztN;K80ZBwbR9FeUS51r*MHKF;?)lry?xO383oHT#B&f*GB7Yb|q9z`^cp@fpGU4Q% zvllNOj7Rjq#SrmeLL>nbAQ56bprUa_el_B<%z_TH?9c2@_jGr)->d4bF1p*DMWVHn znpdyhd*6GnUcH)SobxD(EX!hyfxrwJLopStW+ebx6XtGDT3c2YY;)aH`5^@~76|^3 zwjM}5%_6f>V1MmS-j>?Z0{=v4zP6w_iqK04{I977Ug7~yBi3#%L6|NViuDwTwW25m z);ft(_@)ZKpHF2k2l363lOzZT&{sm}C2{n1k^;RZg5rz}ah0K5tw~t zyRYi>kFH)jIywU2#^mkETa%q&t{X1;K}U#?Spwo2c(qgkKno5R0Oxg}z%0A$^i}ME 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 S9w7bz0000 delta 2989 zcmV;e3sUr`DzX=lBYz8dNkl{p)3)&J>TH^n1jQ_DN_Q%>74E}S0Em7%8p#Wtc;(x&X$?PsqYFtzNQ~Sb? zZ0&z|bDyRyArg9IfXblkQ~M+18slh0`-dZNDwEyqN&TuS`s7w*lX?hgb?SzsLZmWi z#}JIdzz=BPNGiLyE%EA({)aYcNp#W#Ng7f&B*aH^*Sxy3A7e560~9!s$VFgU2Ints@0B7l)vz zk#le<$$#7N*51?|AIiW@!PG+)+8vtK`beu{u?&DilJb$Fp2ql`dZ^rwC5I+(IGKHO zTj1_BzPr~#m`*Aq&kZE&^F245i6z6Sp$Pn|qoKRkco7(cFq)(xEz3tjcfshSAt5Vw zp_mlCSoKT;?{z2cD%0kYG$e#uNx8%TSwI@hp?@*#rR2RrXx!&c6dpK{&c3nLe^;3Y z(@CK_ai~*MDGA|_R7Rf5nKU`-y`-!eAQvE6{KnQEtX*K>+09|?VktLCLk_(RUvb95 z+Ia`&&$myM`#9o`5;v)gI2YpK5ciT49vyr>*2eOA2cF*&zP;Fmejy%$G~_Vp(RH35 z9DnF7stf(`Sn%Fb^Ly1p^P0o?1x{wNPnUb|DAt;VcnB&}(7A!!cbB++c%XNCcPx?4 zsFfD+4Zt}hrM1Yro+RB!oi}8oZZfvi|O3=Z_Br4ttEg zJ>2Jtu|-`f<_rDsK)`C?P$F|r30KLXTgg*$kPr?@A@vZ8Nj2p8a`()JkT+`nEmDas ztnQ@|`-pQyKoCoTE4yR2u5?h;Eu=0?B&B!=8uXI7zOvJ|tt*;JTYX|~N?jj&t$)m? z5X4g8za0;k?Wem>Q__dwB-HvD`WAxq|N`ItFZQ(?E(3&U99BLm72jyK-T?m3WCV?9R2@bu9 zq#m+GNwSPb2m;|0) z*N0LAsaZ6n(&s8Y+d3oZjHS=L>)XN)6?dcS?v-7K-7$m8a(h_4-t!XTm<0a!iC&i8 zu%v@j#^C&p6`j96=x+{KeWKVKNquXlm+kkg>^$y?8P^xp^xn8cD+VDnk_O^p|2p1l z68QIrd$cf8og@uOJzmm1t$#My6HBq(+8eh2fmK~>hpF|&O7VTJq8E3Oyp$BXp@3AE;!C|r;0Avjb%wH$(A6ZQAA&b8vVY&Y*sh9sNPc#+Ggi>= zS10iw%7S$27#+RQMrRj;1=@U zCAMTb&wqbRn^qmTY-aPR%LXWf0#efqlBR{EAz47G#;a$yj63QZ?76%7bpf^up^;QZ zoKLIn$@^aq!^zC;3tBIo(QKg0NGTPJhGa1*dtY2_Z}R7OQ-5i5H)`Z7j*WxwgSD}I zeu4Qi>A3&Ytmz~TNz+6M@lezloI}^lYMy$+|9}5KhqU#^ubi2?ml|w0tX<%50L0Uo zN0-?y(eKkpGmV6BNb=EUl7^%xF-NbI^-ry^EolhgL-(*V^4!Wc)uJvE_X^<<3uEoV zf2n1ANB_ms8Zn3zm*J2UH8k=Zq5=63NJCOAFquQinOFu3pXskHeY?_e@lPZ@M$c6Q=e?duTExA8;bzq{QOC^q%Il|1*8-YMNQ6gzHLY1k3346 zdoP<(fBwYU^Cs4kG^ErdQUq2;707yVj$)GLvZ)PNW90sfr^XX~a6yY%U@2K&=zsYG z3tF(iNdKW0o`w9{#ttlT?t~iXJd$RB;vB^!yG(mk85_y3Z|uO;qxPqGmG;p6^P902 zWe`AWd^M?zY0drfnsD9d|FI>WxEOcHeeJaRbH-PnGY-OjBn_#H2Gky^A!$euJGPpoWc4Ss&T z{i2CAXOFAW^j{J}29k!9V(~?jYA^(&F!00iC-K2}y8LAD=i9pp{t6_ zDU_iYEoemhhxmVb5J_fgJdx6-z<&?Af4|G|qph7^t7wN{ez(hk!;+=|3MNoC_P@0l j3p^HhEbv(1(_j7vksBP|fveg100000NkvXXu0mjf_Ve;p diff --git a/Telegram/Telegram-iOS/IconDefault-Small-40@3x.png b/Telegram/Telegram-iOS/IconDefault-Small-40@3x.png index 9525324b1e6c9cb9a49f8718c8154361c7756a3f..bc3738e3aa56eba8cc626acff7beb51b94dad9ab 100644 GIT binary patch literal 11254 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 literal 4612 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 diff --git a/Telegram/Telegram-iOS/IconDefault-Small@2x.png b/Telegram/Telegram-iOS/IconDefault-Small@2x.png index b9f52c5932e488da9859ccd48bc2096834825cc6..b54ab402a80675265572ff9bfe79d55f03d2cfae 100644 GIT binary patch delta 3228 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 O0000rgopKUYdvu&3?+qP|+v9m^OJ6UtM zoY@}R^G^3k?$*0schBs`Rk?NQS0}g6{oj+dzL!6$*0H)rQQcfVAI;>IL=J+<9f5T% z;p|jf>ft@{e{YQbctzy<%fmm=^!;UF{PW)%q7UzmBY@c9tbe=5pZTyo`L7Mp?=K6X z8AwA)nyhX9b3^3A_C%dOb4cqp__Mcdi%wn|n!Ge937e!ay8E|ni#4$HL9OeEWFOxh zowOt{X-RM&QG%o)%_L%v?}=gH!Kypmmi+#{D<>`vkS1clJpY5G!Rgk-0j`_O=AW&L zPFUm@nz$%H3V&m|57B^lL=I*!99JBey7-WE{l?JvTHl0PKMCO)62c}aWoSSVFv;Jr zF^r4$t1i3#X;pxu$Jdam#*s86RZBwHB#koi%s;IRu<tEK)U<3aJ{BhNN(cl;@DL z#3Njd0z;_#;I`1{iUCau35_ZrAd3_ZlJXoFjSvE)lz(t*u-LkF-V~0FEOSf3COHi8 zpw1i^l7EKOROd|>Rrk8Jeo=8mDJjfQ_DQO2FDF#E@7?78v^flpD0N|nWPqeWjly+z zc~T=vTo?h;kVVT)(vZ}(YrGZqSTvpGMY&w=r`2ww`P|&4qzcr1t3GsfNxu+|k&?tC zRSQWPa?pIg!u8tjpe3mDn*IG^#dwYje5*ceR)3cxuF?*Gn)*mp3rJN6kW7*^B=qm) zN~tXxOB;To8e1mv`g2gmtZqjj%{D?;lETRQvamU-r0=mUzB*4bZ@Rqkj5uX7AC(O2 ze%>6uVt((H^Lt56eI$fU(kS!OO4nyiAtjzE>YEWIDGo|=Xvi;`!-jQlU+2AIUhn1e zdVfe7Qj(PU?{asIBbv(QbYF_E?+SeP|4?O*YG*lt>IJ3wx2^LS*8NS5>+-oh#p~*M zJr8g4)*HOu+3EYGy?3uyvN`#G@^M43m@808zb@uoIj`rkIo-wR`(?c!)Cb%#!`C|= z-M&^q5=>@tI-fWW#p`M1R`Oh$&3B(8%{OsB3Zmbr9R7j(%iCn@RH)dO2y@x1ABM>Dsr9^jlu zH+nL9UHLg;#9a9b^do6V2*vd4?man^9MqMwotyet)NEDSBID+F^X!Y;yy#?tGKNlCbc zq#-Yz<;)p{?(w zzENSG2SE-eX~>_Kb?1%h-n7Dn#067rq^xPAEQHCoc6#&0t-n?GATQlTBkiM-Vcn1G z{O3=xUNF^4(vT1ilQbknSpvy|Mfvt~)lB=|Q&UM{{V~2}n=WT4r8{4UnvP)Yqh{b4Z$RNFAO3ZRcy-6q8STAMW;Ytf?&EOItk! z>Tc_gv(P?ukY@Udr0FDoS%2beQ1oBSx3B0!K<_L$Xi&ziF4y0yy3hWy{p_zgARHqh zY?6GcpCL7!_jUVQ+jYLa`TiW6ZeE)6kE-sXz7yWzilfR|nrJ{t*pxFgU$l|J{Cc6i z$rBgDTfGVODKq7Taf3jIhL@_^{>6}`j!`7SsUJ5%r+wT^(vT*?^`tQ7bH+?-cSY0t z`>Pa;`0WDQDIcj5NmVo;jm|#8jija~Qko<5yM;D(-QT)=tGHun_sJis7lV>cA%)S~ zM?aE=6oK29cH@cz{A<7ucPOWex*s%`U{d%%U zi6b=Rwbc#`Jm|l|-qsiW!vf2R@7169zATg=Ap}S!Ng5o-kVEyLkTkLZVgj{UavzvZ^Kx3~~H t692d*l+5Bw86XHIckEAxj@3PA>wi_0l+wtc9m)Uz002ovPDHLkV1mvQ_0s?V diff --git a/Telegram/Telegram-iOS/IconDefault-Small@3x.png b/Telegram/Telegram-iOS/IconDefault-Small@3x.png index 95b278c284fd91c76bdd5d98653d827f0ddfddba..0b4de296fb910ac30adb159920231a6505dc07e2 100644 GIT binary patch literal 6630 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 delta 3204 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 We need this so that you can take and share photos and videos. NSContactsUsageDescription - Telegram stores your contacts heavily encrypted in the cloud to let you connect with your friends across all your devices. + Ghostgram stores your contacts heavily encrypted in the cloud to let you connect with your friends across all your devices. NSFaceIDUsageDescription You can use Face ID to unlock the app. NSLocationAlwaysUsageDescription - When you send your location to your friends, Telegram needs access to show them a map. You also need this to send locations from an Apple Watch. + When you send your location to your friends, Ghostgram needs access to show them a map. You also need this to send locations from an Apple Watch. NSLocationWhenInUseUsageDescription - When you send your location to your friends, Telegram needs access to show them a map. + When you send your location to your friends, Ghostgram needs access to show them a map. NSMicrophoneUsageDescription We need this so that you can record and share voice messages and videos with sound. NSMotionUsageDescription - When you send your location to your friends, Telegram needs access to show them a map. + When you send your location to your friends, Ghostgram needs access to show them a map. NSPhotoLibraryAddUsageDescription We need this so that you can share photos and videos from your photo library. NSPhotoLibraryUsageDescription @@ -188,7 +188,7 @@ public.data UTTypeDescription - Telegram iOS Color Theme File + Ghostgram iOS Color Theme File UTTypeIconFiles BlueIcon@3x.png diff --git a/Telegram/Telegram-iOS/Resources/Cocoon.tgs b/Telegram/Telegram-iOS/Resources/Cocoon.tgs new file mode 100644 index 0000000000000000000000000000000000000000..9eba8213addbee4b4b0205df492ea18da948319e GIT binary patch literal 2164 zcmV-)2#fb0iwFP!000021MQq!Z{xfXhW|>SYl`6T%-j|&P@pJ^ws(P1xLId6UFQJX z-`6e{``YT|3aC`cS?fA28A%o*tgBDAv1o?>P8!yIt)t(VNfJJ-Sr47~o$D z1FV;yR-5e6guQ=!$GPu5RoA8@_}Aao>np(U+YS4D#3-MuC;C`&q>uRdMO4o(TyC`U zg8tP4{kAXtk`nj_3jNS?`IPhSly1A@>wiCR@*Nv>w$T^t&W0{u`q$p04+LoZn}R_0$0{X<|KU$1qcP&(b0Mlvz3!OI z(>@mXAPzp;SY3CFh2@EfZNS>a%FQ(>Ufl>#K3jqLi}L>_c9__22l+Xh{gf!~hiz?3 z?IkyTYDU%j4Yry4-QHnyOFbyJpKiOxnqM5ZcatJ1W!^;%3vq?enDhg=pvrPcY z{)JI-;HJrGeaqcw{U83~Zr1ds-R9}zYQV<3_0O^~_tQf|{&Zi^b8NUCK~EE$U?4MmVj2dlnrz!dnHM*FPsEK55<3=6QsMCea0Q6YXvOZHXSLu9cMHYUKIr+b0Heo-1bm1Ztk6WkR)mV5Gggre}rd z*)jaU*_JZhKQPI+LpS8m{61q-N(06!1s!AeGaT*X4K+!y8e(uAvsXPg>(rv?ss}{$ zASDoKU9_1U6lVa#B=VO+A_PvrE)7m9u!S&)5YV)e?0^W>u*r(rKn4@=LNg+!je$T* z8UrAc!1XeUw`m)X5%Ge;iIjiWv80wgXI{;`vgwj|)$H!v^U8-;<5d|;YC2*v?b>me>E1gGLMqexpiq5%cIn!^ZA9hf8 zQS>uM^fRwUzcOspywM-?)^?D7BL^|krO>Z7(qd5wB$_Xk3<{xRC8$7j^wOh(Me^)r zTW!76Dv8=>`gobVL5c;Ia!4(WLbe@pIvE47j*Bj}kgFZ)!GxqUu?RpkAz+n8Tabo` z!+2R9#)#$^5_D-M+DtSOC0#uS3Z^^&O~(V!pN%rw0m>-jt)OMhK7g{3gB0;%P=>66 zXa#h@z!F$2 zsn<*?ht*nZD`Rq_(nA@blhL}@T!;!F}q6y&@{kO!c=K#GpVjfQQxtWpkZw2`w6<`)7S4)SQ)%j&F}cn-{xhvOp{&bM6Cc~!Cm#0#H+ zlz1Kwk%XXNt{DRzuEheiWNFpF@Gq2hG$#jn3U5Z)jItf?7=6J8XQoWDBZ=nRYmqEA z+bxPx8y0F~rx1Q6j%n6qfM%E$WDFw6iwOn?l#Z^s491svFd`Cq9q6|r{0?bHD-B5n z#$9A=Wd)`!d*e8q1)s%M5;%_Mmlnz<9m@=ciyXVt0GcqU!a z>3A90k`FUoX1dID>F@Ah&Q%hw@0i8O(0Dlk{;lLW)=}p`?ybt)e?sN_Qjfv?N2s~f zrR{hM*{l=^0M_vaU{PVRjb^7un&;CRmAk(_}#-sQJHmqxPq%Bz-_gMNFI qcV8TRP+`7Q7>)71^77>~tLbML?c1lS%Vzt78~uL~d4x7iTL1tEAsmGO literal 0 HcmV?d00001 diff --git a/Telegram/Telegram-iOS/en.lproj/InfoPlist.strings b/Telegram/Telegram-iOS/en.lproj/InfoPlist.strings index ca338663..0daf084e 100644 --- a/Telegram/Telegram-iOS/en.lproj/InfoPlist.strings +++ b/Telegram/Telegram-iOS/en.lproj/InfoPlist.strings @@ -1,12 +1,16 @@ /* Localized versions of Info.plist keys */ -"NSContactsUsageDescription" = "Telegram will continuously upload your contacts to its heavily encrypted cloud servers to let you connect with your friends across all your devices."; -"NSLocationWhenInUseUsageDescription" = "When you send your location to your friends, Telegram needs access to show them a map."; -"NSLocationAlwaysAndWhenInUseUsageDescription" = "When you choose to share your Live Location with friends in a chat, Telegram needs background access to your location to keep them updated for the duration of the live sharing."; -"NSLocationAlwaysUsageDescription" = "When you choose to share your live location with friends in a chat, Telegram needs background access to your location to keep them updated for the duration of the live sharing. You also need this to send locations from an Apple Watch."; +/* GHOSTGRAM: App display name */ +"CFBundleDisplayName" = "Ghostgram"; +"CFBundleName" = "Ghostgram"; + +"NSContactsUsageDescription" = "Ghostgram will continuously upload your contacts to its heavily encrypted cloud servers to let you connect with your friends across all your devices."; +"NSLocationWhenInUseUsageDescription" = "When you send your location to your friends, Ghostgram needs access to show them a map."; +"NSLocationAlwaysAndWhenInUseUsageDescription" = "When you choose to share your Live Location with friends in a chat, Ghostgram needs background access to your location to keep them updated for the duration of the live sharing."; +"NSLocationAlwaysUsageDescription" = "When you choose to share your live location with friends in a chat, Ghostgram needs background access to your location to keep them updated for the duration of the live sharing. You also need this to send locations from an Apple Watch."; "NSCameraUsageDescription" = "We need this so that you can take and share photos and videos, as well as make video calls."; -"NSPhotoLibraryUsageDescription" = "We need this so that you can share photos and videos from your photo library."; -"NSPhotoLibraryAddUsageDescription" = "We need this so that you can save photos and videos to your photo library."; +"NSPhotoLibraryUsageDescription" = "We need this so that you can share photos and videos from your photo library and save the ones you capture."; +"NSPhotoLibraryAddUsageDescription" = "We need this so that you can save photos and videos to your photo library and save the ones you capture."; "NSMicrophoneUsageDescription" = "We need this so that you can record and share voice messages and videos with sound."; "NSSiriUsageDescription" = "You can use Siri to send messages."; "NSFaceIDUsageDescription" = "You can use Face ID to unlock the app."; diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 597ef1ad..16b14dc7 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -4856,12 +4856,9 @@ Sorry for the inconvenience."; "Conversation.SendMessage.SetReminder" = "Set a Reminder"; -"Conversation.SelectedMessages_1" = "%@ Selected"; -"Conversation.SelectedMessages_2" = "%@ Selected"; -"Conversation.SelectedMessages_3_10" = "%@ Selected"; -"Conversation.SelectedMessages_any" = "%@ Selected"; -"Conversation.SelectedMessages_many" = "%@ Selected"; -"Conversation.SelectedMessages_0" = "%@ Selected"; +"Conversation.SelectedMessagesFormat_1" = "{} Selected"; +"Conversation.SelectedMessagesFormat_any" = "{} Selected"; + "AccentColor.Title" = "Accent Color"; @@ -15459,6 +15456,8 @@ Error: %8$@"; "Notification.StarsGiftOffer.Expired" = "%1$@ didn't respond to your offer for %2$@ within %3$@ – your %4$@ have been refunded"; "Notification.StarsGiftOffer.Expired.Stars_1" = "%@ Star"; "Notification.StarsGiftOffer.Expired.Stars_any" = "%@ Stars"; +"Notification.StarsGiftOffer.Expired.Hours_1" = "%@ hours"; +"Notification.StarsGiftOffer.Expired.Hours_any" = "%@ hours"; "Notification.StarsGiftOffer.ExpiredYou" = "The offer from %1$@ to buy your %2$@ for %3$@ has expired"; "Notification.StarsGiftOffer.ExpiredYou.Stars_1" = "%@ Star"; "Notification.StarsGiftOffer.ExpiredYou.Stars_any" = "%@ Stars"; @@ -15623,3 +15622,50 @@ Error: %8$@"; "Chat.GiftPurchaseOffer.AcceptConfirmation.BadValue" = "The value of this gift is **%@** higher than the offer."; "Chat.GiftPurchaseOffer.AcceptConfirmation.Confirm" = "Confirm Sale"; +"Notification.PremiumGift.DaysTitle_1" = "%@ Day Premium"; +"Notification.PremiumGift.DaysTitle_any" = "%@ Days Premium"; + +"MediaEditor.Audio.Title" = "Audio"; +"MediaEditor.Audio.SavedMusic" = "SAVED MUSIC"; +"MediaEditor.Audio.ShowMore" = "Show More"; + +"WebApp.AddToAttachmentTitle" = "Add to Attachment Menu"; + +"Gift.Upgrade.Wearable.Title" = "Wearable"; +"Gift.Upgrade.Wearable.Text" = "Display gifts on your page and set them as profile covers or statuses."; +"Gift.Upgrade.ViewAllVariants" = "View all variants"; + +"CocoonInfo.Title" = "COCOON"; +"CocoonInfo.Description" = "**Cocoon** (**Co**nfidential **Co**mpute **O**pen **N**etwork) handles AI tasks **safely** and **efficiently**."; +"CocoonInfo.Private.Title" = "Private"; +"CocoonInfo.Private.Text" = "No third party can access any data, such as translations, inside Cocoon."; +"CocoonInfo.Efficient.Title" = "Efficient"; +"CocoonInfo.Efficient.Text" = "Cocoon has allowed Telegram to reduce translation costs by 6x."; +"CocoonInfo.ForEveryone.Title" = "For Everyone"; +"CocoonInfo.ForEveryone.Text" = "Any developer can use Cocoon for AI features. Learn more at [@cocoon](telegram) or [cocoon.org](web)."; +"CocoonInfo.IntergrateInfo" = "Want to integrate Cocoon into your projects?\nReach out at [t.me/cocoon?direct]()"; +"CocoonInfo.Understood" = "Understood"; + +"Conversation.Translation.CocoonInfo" = "Translations are powered by\n**#Cocoon**. [How does it work?]()"; + +"Conversation.EmojiStake.Won" = "%1$@ won %2$@"; +"Conversation.EmojiStake.WonYou" = "You won %@"; +"Conversation.EmojiStake.Lost" = "%1$@ lost %2$@"; +"Conversation.EmojiStake.LostYou" = "You lost %@"; + +"Conversation.Dice.Stake" = "Stake:"; +"Conversation.Dice.Change" = "change"; + +"EmojiStake.Title" = "Emoji Stake"; +"EmojiStake.Description" = "An experimental feature for Telegram Premium users."; +"EmojiStake.ResultsTitle" = "RESULTS AND RETURNS"; +"EmojiStake.StreakInfo" = "A streak resets after 3 # or a stake change."; +"EmojiStake.StakeTitle" = "STAKE"; +"EmojiStake.StakePlaceholder" = "Amount"; +"EmojiStake.Roll" = "Save and Roll"; + +"Conversation.Summary.Title" = "AI Summary"; +"Conversation.Summary.Text" = "Tap to see original text"; + +"Conversation.Summary.Limit.Title" = "AI Summary"; +"Conversation.Summary.Limit.Text" = "Summarize large messages with AI – unlimited with Telegram Premium."; diff --git a/Telegram/Telegram-iOS/ru.lproj/InfoPlist.strings b/Telegram/Telegram-iOS/ru.lproj/InfoPlist.strings index 08c349a3..18b5cac0 100644 --- a/Telegram/Telegram-iOS/ru.lproj/InfoPlist.strings +++ b/Telegram/Telegram-iOS/ru.lproj/InfoPlist.strings @@ -1,7 +1,11 @@ /* Localized versions of Info.plist keys */ -"NSContactsUsageDescription" = "Актуальная информация о ваших контактах будет храниться зашифрованной в облаке Telegram, чтобы вы могли связаться с друзьями с любого устройства."; -"NSLocationWhenInUseUsageDescription" = "Когда вы отправляете друзьям геопозицию, Telegram нужно разрешение, чтобы показать им карту."; +/* GHOSTGRAM: App display name */ +"CFBundleDisplayName" = "Ghostgram"; +"CFBundleName" = "Ghostgram"; + +"NSContactsUsageDescription" = "Актуальная информация о ваших контактах будет храниться зашифрованной в облаке Ghostgram, чтобы вы могли связаться с друзьями с любого устройства."; +"NSLocationWhenInUseUsageDescription" = "Когда вы отправляете друзьям геопозицию, Ghostgram нужно разрешение, чтобы показать им карту."; "NSLocationAlwaysAndWhenInUseUsageDescription" = "Фоновый доступ к геопозиции требуется, чтобы обновлять вашу геопозицию, когда вы транслируете её в чат с друзьями."; "NSLocationAlwaysUsageDescription" = "Фоновый доступ к геопозиции требуется, чтобы обновлять вашу геопозицию, когда вы транслируете её в чат с друзьями. Он также необходим для отправки геопозиции с Apple Watch."; "NSCameraUsageDescription" = "Это необходимо, чтобы вы могли делиться снятыми фотографиями и видео."; @@ -10,3 +14,4 @@ "NSMicrophoneUsageDescription" = "Это необходимо, чтобы вы могли делиться голосовыми сообщениями и видео со звуком."; "NSSiriUsageDescription" = "Вы можете использовать Siri для отправки сообщений."; "NSFaceIDUsageDescription" = "Вы можете разблокировать приложение с помощью Face ID."; + diff --git a/bazel-telegram-antidelete b/bazel-telegram-antidelete new file mode 120000 index 00000000..824f1c9e --- /dev/null +++ b/bazel-telegram-antidelete @@ -0,0 +1 @@ +/private/var/tmp/_bazel_ichmagmaus812/b1f80ab1863cefaee5ee828c8e6100cf/execroot/_main \ No newline at end of file diff --git a/build-system/Make/GenerateProfiles.py b/build-system/Make/GenerateProfiles.py index 0992d95a..df51eb6f 100644 --- a/build-system/Make/GenerateProfiles.py +++ b/build-system/Make/GenerateProfiles.py @@ -5,40 +5,165 @@ import shutil import tempfile import plistlib import argparse +import subprocess +import base64 from BuildEnvironment import run_executable_with_output, check_run_system -def get_certificate_base64(): - certificate_data = run_executable_with_output('security', arguments=['find-certificate', '-c', 'Apple Distribution: Telegram FZ-LLC (C67CF9S4VU)', '-p']) - certificate_data = certificate_data.replace('-----BEGIN CERTIFICATE-----', '') - certificate_data = certificate_data.replace('-----END CERTIFICATE-----', '') - certificate_data = certificate_data.replace('\n', '') - return certificate_data +def setup_temp_keychain(p12_path, p12_password=''): + """Create a temporary keychain and import the p12 certificate.""" + keychain_name = 'generate-profiles-temp.keychain' + keychain_password = 'temp123' + + # Delete if exists + run_executable_with_output('security', arguments=['delete-keychain', keychain_name], check_result=False) + + # Create keychain + run_executable_with_output('security', arguments=[ + 'create-keychain', '-p', keychain_password, keychain_name + ], check_result=True) + + # Add to search list + existing = run_executable_with_output('security', arguments=['list-keychains', '-d', 'user']) + run_executable_with_output('security', arguments=[ + 'list-keychains', '-d', 'user', '-s', keychain_name, existing.replace('"', '') + ], check_result=True) + + # Unlock and set settings + run_executable_with_output('security', arguments=['set-keychain-settings', keychain_name]) + run_executable_with_output('security', arguments=[ + 'unlock-keychain', '-p', keychain_password, keychain_name + ]) + + # Import p12 + run_executable_with_output('security', arguments=[ + 'import', p12_path, '-k', keychain_name, '-P', p12_password, + '-T', '/usr/bin/codesign', '-T', '/usr/bin/security' + ], check_result=True) + + # Set partition list for access + run_executable_with_output('security', arguments=[ + 'set-key-partition-list', '-S', 'apple-tool:,apple:', '-k', keychain_password, keychain_name + ], check_result=True) + + return keychain_name -def process_provisioning_profile(source, destination, certificate_data): +def cleanup_temp_keychain(keychain_name): + """Remove the temporary keychain.""" + run_executable_with_output('security', arguments=['delete-keychain', keychain_name], check_result=False) + + +def get_signing_identity_from_p12(p12_path, p12_password=''): + """Extract the common name (signing identity) from the p12 certificate.""" + proc = subprocess.Popen( + ['openssl', 'pkcs12', '-in', p12_path, '-passin', 'pass:' + p12_password, '-nokeys', '-legacy'], + stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + cert_pem, _ = proc.communicate() + + proc2 = subprocess.Popen( + ['openssl', 'x509', '-noout', '-subject', '-nameopt', 'oneline,-esc_msb'], + stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + subject, _ = proc2.communicate(cert_pem) + subject = subject.decode('utf-8').strip() + + # Parse CN from subject line like: subject= C = AE, O = ..., CN = Some Name + if 'CN = ' in subject: + cn = subject.split('CN = ')[-1].split(',')[0].strip() + return cn + + return None + + +def get_certificate_base64_from_p12(p12_path, p12_password=''): + """Extract the certificate as base64 from p12 file.""" + # Extract certificate in PEM format + proc = subprocess.Popen( + ['openssl', 'pkcs12', '-in', p12_path, '-passin', 'pass:' + p12_password, '-nokeys', '-legacy'], + stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + cert_pem, _ = proc.communicate() + + # Convert to DER format + proc2 = subprocess.Popen( + ['openssl', 'x509', '-outform', 'DER'], + stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + cert_der, _ = proc2.communicate(cert_pem) + + return base64.b64encode(cert_der).decode('utf-8') + + +def process_provisioning_profile(source, destination, certificate_data, signing_identity, keychain_name): parsed_plist = run_executable_with_output('security', arguments=['cms', '-D', '-i', source], check_result=True) parsed_plist_file = tempfile.mktemp() with open(parsed_plist_file, 'w+') as file: file.write(parsed_plist) - run_executable_with_output('plutil', arguments=['-remove', 'DeveloperCertificates.0', parsed_plist_file]) + # Remove all existing developer certificates + while True: + result = run_executable_with_output('plutil', arguments=['-remove', 'DeveloperCertificates.0', parsed_plist_file], check_result=False) + if result is None or 'Could not' in str(result) or result == '': + # Check if the removal actually failed by trying to extract + check = run_executable_with_output('plutil', arguments=['-extract', 'DeveloperCertificates.0', 'raw', parsed_plist_file], check_result=False) + if check is None or 'Could not' in str(check): + break + + # Insert the new certificate run_executable_with_output('plutil', arguments=['-insert', 'DeveloperCertificates.0', '-data', certificate_data, parsed_plist_file]) + + # Remove the DER-Encoded-Profile (signature) run_executable_with_output('plutil', arguments=['-remove', 'DER-Encoded-Profile', parsed_plist_file]) - run_executable_with_output('security', arguments=['cms', '-S', '-N', 'Apple Distribution: Telegram FZ-LLC (C67CF9S4VU)', '-i', parsed_plist_file, '-o', destination]) + # Sign with the certificate from the temporary keychain + run_executable_with_output('security', arguments=[ + 'cms', '-S', '-k', keychain_name, '-N', signing_identity, '-i', parsed_plist_file, '-o', destination + ], check_result=True) os.unlink(parsed_plist_file) -def generate_provisioning_profiles(source_path, destination_path): - certificate_data = get_certificate_base64() +def generate_provisioning_profiles(source_path, destination_path, certs_path): + p12_path = os.path.join(certs_path, 'SelfSigned.p12') - if not os.path.exists(destination_path): - print('{} does not exits'.format(destination_path)) + if not os.path.exists(p12_path): + print('{} does not exist'.format(p12_path)) sys.exit(1) - for file_name in os.listdir(source_path): - if file_name.endswith('.mobileprovision'): - process_provisioning_profile(source=source_path + '/' + file_name, destination=destination_path + '/' + file_name, certificate_data=certificate_data) + if not os.path.exists(destination_path): + print('{} does not exist'.format(destination_path)) + sys.exit(1) + + # Extract certificate info from p12 + p12_password = '' # fake-codesigning uses empty password + certificate_data = get_certificate_base64_from_p12(p12_path, p12_password) + signing_identity = get_signing_identity_from_p12(p12_path, p12_password) + + if not signing_identity: + print('Could not extract signing identity from {}'.format(p12_path)) + sys.exit(1) + + print('Using signing identity: {}'.format(signing_identity)) + + # Setup temporary keychain with the certificate + keychain_name = setup_temp_keychain(p12_path, p12_password) + + try: + for file_name in os.listdir(source_path): + if file_name.endswith('.mobileprovision'): + print('Processing {}'.format(file_name)) + process_provisioning_profile( + source=os.path.join(source_path, file_name), + destination=os.path.join(destination_path, file_name), + certificate_data=certificate_data, + signing_identity=signing_identity, + keychain_name=keychain_name + ) + print('Done. Generated {} profiles.'.format( + len([f for f in os.listdir(destination_path) if f.endswith('.mobileprovision')]) + )) + finally: + cleanup_temp_keychain(keychain_name) diff --git a/build-system/Make/Make.py b/build-system/Make/Make.py index 1898ad5c..93b6a56d 100644 --- a/build-system/Make/Make.py +++ b/build-system/Make/Make.py @@ -46,6 +46,7 @@ class BazelCommandLine: self.show_actions = False self.enable_sandbox = False self.disable_provisioning_profiles = False + self.profile_swift = False self.common_args = [ # https://docs.bazel.build/versions/master/command-line-reference.html @@ -143,6 +144,9 @@ class BazelCommandLine: def set_disable_provisioning_profiles(self): self.disable_provisioning_profiles = True + def set_profile_swift(self, value): + self.profile_swift = value + def set_configuration(self, configuration): if configuration == 'debug_arm64': self.configuration_args = [ @@ -300,6 +304,8 @@ class BazelCommandLine: ] combined_arguments += self.configuration_args + if self.profile_swift: + combined_arguments += ['--config=swift_profile'] print('TelegramBuild: running') print(subprocess.list2cmdline(combined_arguments)) @@ -369,17 +375,15 @@ class BazelCommandLine: print(subprocess.list2cmdline(combined_arguments)) call_executable(combined_arguments) - def get_spm_aspect_invocation(self): + def invoke_spm_build(self): combined_arguments = [ self.build_environment.bazel_path ] combined_arguments += self.get_startup_bazel_arguments() combined_arguments += ['build'] - if self.custom_target is not None: - combined_arguments += [self.custom_target] - else: - combined_arguments += ['Telegram/Telegram'] + # Build the generate_spm target directly to get the dependency tree JSON + combined_arguments += ['//Telegram:spm_build_root'] if self.continue_on_error: combined_arguments += ['--keep_going'] @@ -409,8 +413,6 @@ class BazelCommandLine: combined_arguments += self.configuration_args - combined_arguments += ['--aspects', '//build-system/bazel-utils:spm.bzl%spm_text_aspect'] - print(subprocess.list2cmdline(combined_arguments)) call_executable(combined_arguments) @@ -624,6 +626,7 @@ def build(bazel, arguments): bazel_command_line.set_continue_on_error(arguments.continueOnError) bazel_command_line.set_show_actions(arguments.showActions) bazel_command_line.set_enable_sandbox(arguments.sandbox) + bazel_command_line.set_profile_swift(arguments.profileSwift) bazel_command_line.set_split_swiftmodules(arguments.enableParallelSwiftmoduleGeneration) @@ -719,7 +722,7 @@ def query(bazel, arguments): bazel_command_line.invoke_query(query_args) -def get_spm_aspect_invocation(bazel, arguments): +def build_spm(bazel, arguments): bazel_command_line = BazelCommandLine( bazel=bazel, override_bazel_version=arguments.overrideBazelVersion, @@ -741,13 +744,12 @@ def get_spm_aspect_invocation(bazel, arguments): bazel_command_line.set_configuration(arguments.configuration) bazel_command_line.set_build_number(arguments.buildNumber) - bazel_command_line.set_custom_target(arguments.target) bazel_command_line.set_continue_on_error(False) bazel_command_line.set_show_actions(False) bazel_command_line.set_enable_sandbox(False) bazel_command_line.set_split_swiftmodules(False) - bazel_command_line.get_spm_aspect_invocation() + bazel_command_line.invoke_spm_build() def add_codesigning_common_arguments(current_parser: argparse.ArgumentParser): configuration_group = current_parser.add_mutually_exclusive_group(required=True) @@ -977,6 +979,12 @@ if __name__ == '__main__': help='Generate .swiftmodule files in parallel to building modules, can speed up compilation on multi-core ' 'systems. ' ) + buildParser.add_argument( + '--profileSwift', + action='store_true', + default=False, + help='Enable single-core Swift compile profiling flags.' + ) buildParser.add_argument( '--target', type=str, @@ -1068,6 +1076,13 @@ if __name__ == '__main__': type=str, help='Path to the destination directory.' ) + generate_profiles_build_parser.add_argument( + '--certsPath', + required=False, + type=str, + default='build-system/fake-codesigning/certs', + help='Path to the directory containing SelfSigned.p12 certificate.' + ) remote_upload_testflight_parser = subparsers.add_parser('remote-deploy-testflight', help='Build the app using a remote environment.') remote_upload_testflight_parser.add_argument( @@ -1188,13 +1203,7 @@ if __name__ == '__main__': metavar='query_string' ) - spm_parser = subparsers.add_parser('spm', help='Generate SPM package') - spm_parser.add_argument( - '--target', - type=str, - help='A custom bazel target name to build.', - metavar='target_name' - ) + spm_parser = subparsers.add_parser('spm', help='Generate SPM package (outputs bazel-bin/Telegram/spm_build_root_modules.json)') spm_parser.add_argument( '--buildNumber', required=False, @@ -1315,7 +1324,7 @@ if __name__ == '__main__': additional_codesigning_output_path=remote_input_path ) - GenerateProfiles.generate_provisioning_profiles(source_path=remote_input_path + '/profiles', destination_path=args.destination) + GenerateProfiles.generate_provisioning_profiles(source_path=remote_input_path + '/profiles', destination_path=args.destination, certs_path=args.certsPath) elif args.commandName == 'remote-deploy-testflight': env = os.environ if 'APPSTORE_CONNECT_USERNAME' not in env: @@ -1351,7 +1360,7 @@ if __name__ == '__main__': elif args.commandName == 'query': query(bazel=bazel_path, arguments=args) elif args.commandName == 'spm': - get_spm_aspect_invocation(bazel=bazel_path, arguments=args) + build_spm(bazel=bazel_path, arguments=args) else: raise Exception('Unknown command') except KeyboardInterrupt: diff --git a/build-system/Make/TartBuild.py b/build-system/Make/TartBuild.py index 449e9ce0..9f890ca3 100644 --- a/build-system/Make/TartBuild.py +++ b/build-system/Make/TartBuild.py @@ -604,7 +604,7 @@ def remote_build_tart(macos_version, bazel_cache_host, configuration, build_inpu else: guest_build_sh += '--cacheHost="$CACHE_HOST" \\' guest_build_sh += 'build \\' - guest_build_sh += '--lock \\' + #guest_build_sh += '--lock \\' guest_build_sh += '--buildNumber={} \\'.format(build_number) guest_build_sh += '--configuration={} \\'.format(configuration) guest_build_sh += '--configurationPath=$HOME/telegram-build-input/configuration.json \\' diff --git a/build-system/MakeProject/Package.resolved b/build-system/MakeProject/Package.resolved new file mode 100644 index 00000000..d9f1a9dc --- /dev/null +++ b/build-system/MakeProject/Package.resolved @@ -0,0 +1,50 @@ +{ + "pins" : [ + { + "identity" : "aexml", + "kind" : "remoteSourceControl", + "location" : "https://github.com/tadija/AEXML.git", + "state" : { + "revision" : "38f7d00b23ecd891e1ee656fa6aeebd6ba04ecc3", + "version" : "4.6.1" + } + }, + { + "identity" : "pathkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/kylef/PathKit.git", + "state" : { + "revision" : "3bfd2737b700b9a36565a8c94f4ad2b050a5e574", + "version" : "1.0.1" + } + }, + { + "identity" : "spectre", + "kind" : "remoteSourceControl", + "location" : "https://github.com/kylef/Spectre.git", + "state" : { + "revision" : "26cc5e9ae0947092c7139ef7ba612e34646086c7", + "version" : "0.10.1" + } + }, + { + "identity" : "swift-argument-parser", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-argument-parser", + "state" : { + "revision" : "309a47b2b1d9b5e991f36961c983ecec72275be3", + "version" : "1.6.1" + } + }, + { + "identity" : "xcodeproj", + "kind" : "remoteSourceControl", + "location" : "https://github.com/tuist/XcodeProj.git", + "state" : { + "revision" : "dc3b87a4e69f9cd06c6cb16199f5d0472e57ef6b", + "version" : "8.24.3" + } + } + ], + "version" : 2 +} diff --git a/build-system/MakeProject/Package.swift b/build-system/MakeProject/Package.swift new file mode 100644 index 00000000..8e81f4a7 --- /dev/null +++ b/build-system/MakeProject/Package.swift @@ -0,0 +1,26 @@ +// swift-tools-version:5.8 + +import PackageDescription + +let package = Package( + name: "MakeProject", + platforms: [ + .macOS(.v13) + ], + products: [ + .executable(name: "MakeProject", targets: ["MakeProject"]) + ], + dependencies: [ + .package(url: "https://github.com/tuist/XcodeProj.git", from: "8.15.0"), + .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.2.0"), + ], + targets: [ + .executableTarget( + name: "MakeProject", + dependencies: [ + .product(name: "XcodeProj", package: "XcodeProj"), + .product(name: "ArgumentParser", package: "swift-argument-parser"), + ] + ) + ] +) diff --git a/build-system/MakeProject/Sources/MakeProject/ModuleJSON.swift b/build-system/MakeProject/Sources/MakeProject/ModuleJSON.swift new file mode 100644 index 00000000..fa47427c --- /dev/null +++ b/build-system/MakeProject/Sources/MakeProject/ModuleJSON.swift @@ -0,0 +1,54 @@ +import Foundation + +struct ModuleDefinition: Codable { + let name: String + let moduleName: String? + let type: String + let path: String + let sources: [String] + let deps: [String]? + let copts: [String]? + let cxxopts: [String]? + let defines: [String]? + let includes: [String]? + let sdkFrameworks: [String]? + let sdkDylibs: [String]? + let hdrs: [String]? + let textualHdrs: [String]? + let weakSdkFrameworks: [String]? + + enum CodingKeys: String, CodingKey { + case name + case moduleName = "module_name" + case type + case path + case sources + case deps + case copts + case cxxopts + case defines + case includes + case sdkFrameworks = "sdk_frameworks" + case sdkDylibs = "sdk_dylibs" + case hdrs + case textualHdrs = "textual_hdrs" + case weakSdkFrameworks = "weak_sdk_frameworks" + } +} + +enum ModuleType: String { + case swiftLibrary = "swift_library" + case objcLibrary = "objc_library" + case ccLibrary = "cc_library" + case xcframework = "apple_static_xcframework_import" + + init?(from definition: ModuleDefinition) { + self.init(rawValue: definition.type) + } +} + +func loadModules(from path: String) throws -> [String: ModuleDefinition] { + let url = URL(fileURLWithPath: path) + let data = try Data(contentsOf: url) + return try JSONDecoder().decode([String: ModuleDefinition].self, from: data) +} diff --git a/build-system/MakeProject/Sources/MakeProject/ProjectGenerator.swift b/build-system/MakeProject/Sources/MakeProject/ProjectGenerator.swift new file mode 100644 index 00000000..92f965af --- /dev/null +++ b/build-system/MakeProject/Sources/MakeProject/ProjectGenerator.swift @@ -0,0 +1,147 @@ +import Foundation +import PathKit +import XcodeProj + +class ProjectGenerator { + let modulesPath: Path + let outputDir: Path + let projectRoot: Path + + init(modulesPath: Path, outputDir: Path, projectRoot: Path) { + self.modulesPath = modulesPath + self.outputDir = outputDir + self.projectRoot = projectRoot + } + + func generate() throws { + print("Loading modules from \(modulesPath)...") + let modules = try loadModules(from: modulesPath.string) + print("Loaded \(modules.count) modules") + + // Filter out empty modules, but keep: + // - Modules with source files (excluding .a) + // - Static library modules (only .a files) + // - XCFramework imports + let validModules = modules.filter { name, module in + let nonStaticSources = module.sources.filter { !$0.hasSuffix(".a") } + let staticLibs = module.sources.filter { $0.hasSuffix(".a") } + return !nonStaticSources.isEmpty || + !staticLibs.isEmpty || + module.type == "apple_static_xcframework_import" + } + print("Processing \(validModules.count) non-empty modules") + + // Setup output directory + try outputDir.mkpath() + + // Create symlink manager + let symlinkManager = SymlinkManager(outputDir: outputDir, projectRoot: projectRoot) + symlinkManager.scanExistingFiles() + + // Create project + let projectPath = outputDir + "Telegram.xcodeproj" + let pbxproj = PBXProj() + + // Create main group + let mainGroup = PBXGroup(children: [], sourceTree: .group) + pbxproj.add(object: mainGroup) + + // Create project-level build configurations + let projectDebugSettings: BuildSettings = [ + "ALWAYS_SEARCH_USER_PATHS": "NO", + "CLANG_CXX_LANGUAGE_STANDARD": "c++17", + "CLANG_ENABLE_MODULES": "YES", + "CLANG_ENABLE_OBJC_ARC": "YES", + "CLANG_ENABLE_EXPLICIT_MODULES": "NO", // Disable explicit module builds for ObjC-Swift interop + "SWIFT_ENABLE_EXPLICIT_MODULES": "NO", // Disable explicit module builds for Swift + "ENABLE_STRICT_OBJC_MSGSEND": "YES", + "GCC_NO_COMMON_BLOCKS": "YES", + "IPHONEOS_DEPLOYMENT_TARGET": "13.0", + "MTL_ENABLE_DEBUG_INFO": "INCLUDE_SOURCE", + "ONLY_ACTIVE_ARCH": "YES", + "SDKROOT": "iphoneos", + "SWIFT_VERSION": "5.0", + "TARGETED_DEVICE_FAMILY": "1,2", + "DEBUG_INFORMATION_FORMAT": "dwarf", + "ENABLE_BITCODE": "NO", + ] + + var projectReleaseSettings = projectDebugSettings + projectReleaseSettings["DEBUG_INFORMATION_FORMAT"] = "dwarf-with-dsym" + projectReleaseSettings["MTL_ENABLE_DEBUG_INFO"] = "NO" + projectReleaseSettings["ONLY_ACTIVE_ARCH"] = "NO" + + let projectDebugConfig = XCBuildConfiguration(name: "Debug", buildSettings: projectDebugSettings) + let projectReleaseConfig = XCBuildConfiguration(name: "Release", buildSettings: projectReleaseSettings) + pbxproj.add(object: projectDebugConfig) + pbxproj.add(object: projectReleaseConfig) + + let projectConfigList = XCConfigurationList( + buildConfigurations: [projectDebugConfig, projectReleaseConfig], + defaultConfigurationName: "Release" + ) + pbxproj.add(object: projectConfigList) + + // Create project + let project = PBXProject( + name: "Telegram", + buildConfigurationList: projectConfigList, + compatibilityVersion: "Xcode 14.0", + preferredProjectObjectVersion: 56, + minimizedProjectReferenceProxies: 0, + mainGroup: mainGroup + ) + pbxproj.add(object: project) + + // Create target builder + let targetBuilder = TargetBuilder( + project: project, + pbxproj: pbxproj, + mainGroup: mainGroup, + outputDir: outputDir, + symlinkManager: symlinkManager + ) + + // Build targets + print("Creating targets...") + var builtCount = 0 + for (name, module) in validModules.sorted(by: { $0.key < $1.key }) { + do { + if let _ = try targetBuilder.buildTarget(for: module, allModules: validModules) { + builtCount += 1 + if builtCount % 50 == 0 { + print(" Created \(builtCount) targets...") + } + } + } catch { + print("Warning: Failed to build target \(name): \(error)") + } + } + print("Created \(builtCount) targets") + + // Wire up dependencies + print("Wiring up dependencies...") + try targetBuilder.wireUpDependencies(modules: validModules) + + // Write project + print("Writing project to \(projectPath)...") + pbxproj.rootObject = project + let xcodeproj = XcodeProj(workspace: XCWorkspace(), pbxproj: pbxproj) + try xcodeproj.write(path: projectPath) + + // Generate scheme for main target + if let telegramTarget = targetBuilder.getTarget(named: "TelegramUI") { + print("Generating scheme...") + let schemeGenerator = SchemeGenerator(projectPath: projectPath, pbxproj: pbxproj) + try schemeGenerator.generateScheme(for: telegramTarget, named: "TelegramUI") + } else { + print("Warning: Could not find TelegramUI target for scheme") + } + + // Clean up stale files + print("Cleaning up stale symlinks...") + symlinkManager.cleanupStaleFiles() + + print("Done! Project written to \(projectPath)") + } +} diff --git a/build-system/MakeProject/Sources/MakeProject/SchemeGenerator.swift b/build-system/MakeProject/Sources/MakeProject/SchemeGenerator.swift new file mode 100644 index 00000000..1c1b7632 --- /dev/null +++ b/build-system/MakeProject/Sources/MakeProject/SchemeGenerator.swift @@ -0,0 +1,74 @@ +import Foundation +import PathKit +import XcodeProj + +class SchemeGenerator { + let projectPath: Path + let pbxproj: PBXProj + + init(projectPath: Path, pbxproj: PBXProj) { + self.projectPath = projectPath + self.pbxproj = pbxproj + } + + func generateScheme(for target: PBXNativeTarget, named schemeName: String) throws { + let schemesDir = projectPath + "xcshareddata" + "xcschemes" + try schemesDir.mkpath() + + let buildableReference = XCScheme.BuildableReference( + referencedContainer: "container:Telegram.xcodeproj", + blueprint: target, + buildableName: "\(target.name).framework", + blueprintName: target.name + ) + + let buildAction = XCScheme.BuildAction( + buildActionEntries: [ + XCScheme.BuildAction.Entry( + buildableReference: buildableReference, + buildFor: [.running, .testing, .profiling, .archiving, .analyzing] + ) + ], + parallelizeBuild: true, + buildImplicitDependencies: true + ) + + let launchAction = XCScheme.LaunchAction( + runnable: nil, + buildConfiguration: "Debug" + ) + + let testAction = XCScheme.TestAction( + buildConfiguration: "Debug", + macroExpansion: buildableReference + ) + + let profileAction = XCScheme.ProfileAction( + runnable: nil, + buildConfiguration: "Release", + macroExpansion: buildableReference + ) + + let analyzeAction = XCScheme.AnalyzeAction(buildConfiguration: "Debug") + + let archiveAction = XCScheme.ArchiveAction( + buildConfiguration: "Release", + revealArchiveInOrganizer: true + ) + + let scheme = XCScheme( + name: schemeName, + lastUpgradeVersion: nil, + version: nil, + buildAction: buildAction, + testAction: testAction, + launchAction: launchAction, + profileAction: profileAction, + analyzeAction: analyzeAction, + archiveAction: archiveAction + ) + + let schemePath = schemesDir + "\(schemeName).xcscheme" + try scheme.write(path: schemePath, override: true) + } +} diff --git a/build-system/MakeProject/Sources/MakeProject/SymlinkManager.swift b/build-system/MakeProject/Sources/MakeProject/SymlinkManager.swift new file mode 100644 index 00000000..1ee9da3d --- /dev/null +++ b/build-system/MakeProject/Sources/MakeProject/SymlinkManager.swift @@ -0,0 +1,116 @@ +import Foundation +import PathKit + +class SymlinkManager { + let outputDir: Path + let projectRoot: Path + private var previousFiles: Set = [] + private var currentFiles: Set = [] + + init(outputDir: Path, projectRoot: Path) { + self.outputDir = outputDir + self.projectRoot = projectRoot + } + + func scanExistingFiles() { + previousFiles = [] + scanDirectory(outputDir) + } + + private func scanDirectory(_ path: Path) { + guard path.exists else { return } + do { + for item in try path.children() { + let name = item.lastComponent + // Skip build artifacts and xcodeproj bundles + if name == ".build" || name.hasSuffix(".xcodeproj") { continue } + previousFiles.insert(item) + if item.isDirectory && !item.isSymlink { + scanDirectory(item) + } + } + } catch { + print("Warning: Could not scan \(path): \(error)") + } + } + + func createDirectory(_ path: Path) throws { + currentFiles.insert(path) + var parent = path.parent() + while parent != outputDir && parent.string.count > outputDir.string.count { + currentFiles.insert(parent) + parent = parent.parent() + } + if !path.exists { + try path.mkpath() + } + } + + func createSymlink(from source: Path, to target: Path) throws { + currentFiles.insert(target) + var parent = target.parent() + while parent != outputDir && parent.string.count > outputDir.string.count { + currentFiles.insert(parent) + parent = parent.parent() + } + + // Calculate relative path from target back to source + let targetDir = target.parent() + let depth = targetDir.components.count - outputDir.components.count + 1 + let relativePrefix = Array(repeating: "..", count: depth).joined(separator: "/") + let relativePath = Path(relativePrefix) + source + + if target.isSymlink { + let existingTarget = try? target.symlinkDestination() + if existingTarget == relativePath { + return // Already correct + } + try target.delete() + } else if target.exists { + try target.delete() + } + + try targetDir.mkpath() + try FileManager.default.createSymbolicLink( + atPath: target.string, + withDestinationPath: relativePath.string + ) + } + + func cleanupStaleFiles() { + let staleFiles = previousFiles.subtracting(currentFiles) + let sortedStale = staleFiles.sorted { $0.components.count > $1.components.count } + + for path in sortedStale { + do { + if path.isSymlink || path.isFile { + try path.delete() + } else if path.isDirectory { + if (try? path.children().isEmpty) == true { + try path.delete() + } + } + } catch { + print("Warning: Could not remove \(path): \(error)") + } + } + } + + func markFile(_ path: Path) { + currentFiles.insert(path) + } +} + +extension Path { + var isSymlink: Bool { + var isDir: ObjCBool = false + let exists = FileManager.default.fileExists(atPath: self.string, isDirectory: &isDir) + guard exists else { return false } + do { + let attrs = try FileManager.default.attributesOfItem(atPath: self.string) + return attrs[.type] as? FileAttributeType == .typeSymbolicLink + } catch { + return false + } + } +} diff --git a/build-system/MakeProject/Sources/MakeProject/TargetBuilder.swift b/build-system/MakeProject/Sources/MakeProject/TargetBuilder.swift new file mode 100644 index 00000000..8252f47c --- /dev/null +++ b/build-system/MakeProject/Sources/MakeProject/TargetBuilder.swift @@ -0,0 +1,792 @@ +import Foundation +import PathKit +import XcodeProj + +class TargetBuilder { + let project: PBXProject + let pbxproj: PBXProj + let mainGroup: PBXGroup + let outputDir: Path + let symlinkManager: SymlinkManager + + private var targetsByName: [String: PBXNativeTarget] = [:] + private var groupsByPath: [String: PBXGroup] = [:] + + init(project: PBXProject, pbxproj: PBXProj, mainGroup: PBXGroup, outputDir: Path, symlinkManager: SymlinkManager) { + self.project = project + self.pbxproj = pbxproj + self.mainGroup = mainGroup + self.outputDir = outputDir + self.symlinkManager = symlinkManager + } + + // Track which modules are header-only (have no linkable code) + private var headerOnlyModules: Set = [] + // Track which modules are static library collections (.a files) + private var staticLibraryModules: [String: [String]] = [:] // module name -> list of .a file paths + + func isHeaderOnlyModule(_ name: String) -> Bool { + return headerOnlyModules.contains(name) + } + + func isStaticLibraryModule(_ name: String) -> Bool { + return staticLibraryModules[name] != nil + } + + func getStaticLibraries(for name: String) -> [String] { + return staticLibraryModules[name] ?? [] + } + + func buildTarget(for module: ModuleDefinition, allModules: [String: ModuleDefinition]) throws -> PBXNativeTarget? { + guard let moduleType = ModuleType(from: module) else { + print("Warning: Unknown module type \(module.type) for \(module.name)") + return nil + } + + // Check for static library modules (only .a files) + let staticLibs = module.sources.filter { $0.hasSuffix(".a") } + let nonStaticLibs = module.sources.filter { !$0.hasSuffix(".a") } + let isStaticLibOnly = !staticLibs.isEmpty && nonStaticLibs.allSatisfy { $0.hasSuffix(".h") || $0.hasSuffix(".hpp") } + + if isStaticLibOnly && moduleType != .xcframework { + // This is a static library module - track its .a files but don't create a framework + staticLibraryModules[module.name] = staticLibs + return nil // Don't create a target, just track the static libs + } + + // Check if module is header-only (only header files, no real sources) + let sourceFiles = module.sources.filter { source in + !source.hasSuffix(".a") && !source.hasSuffix(".h") && !source.hasSuffix(".hpp") + } + let isHeaderOnly = sourceFiles.isEmpty && moduleType != .xcframework + + // Skip modules with no sources at all + if module.sources.isEmpty && moduleType != .xcframework { + return nil + } + + switch moduleType { + case .xcframework: + return try buildXCFrameworkTarget(for: module) + case .swiftLibrary, .objcLibrary, .ccLibrary: + if isHeaderOnly { + headerOnlyModules.insert(module.name) + return try buildHeaderOnlyFrameworkTarget(for: module) + } else { + return try buildFrameworkTarget(for: module, moduleType: moduleType) + } + } + } + + private func buildXCFrameworkTarget(for module: ModuleDefinition) throws -> PBXNativeTarget { + // For xcframeworks, we create a reference but no build phases + let target = PBXNativeTarget( + name: module.name, + buildConfigurationList: createConfigurationList(for: module, isXCFramework: true), + buildPhases: [], + productName: module.name, + productType: .framework + ) + pbxproj.add(object: target) + project.targets.append(target) + targetsByName[module.name] = target + return target + } + + private func buildFrameworkTarget(for module: ModuleDefinition, moduleType: ModuleType) throws -> PBXNativeTarget { + // Create group for module + let moduleGroup = try getOrCreateGroup(for: module.path) + + // Create symlinks and file references + var sourceRefs: [PBXFileReference] = [] + var publicHeaderRefs: [PBXFileReference] = [] + var seenPublicHeaderNames: Set = [] // Track header filenames to avoid duplicates in headers build phase + + // Determine public header detection: + // 1. If hdrs is provided, use those (explicit public headers) + // 2. Otherwise, headers in includes directories are public + let explicitPublicHeaders = Set(module.hdrs ?? []) + + let allSourceFiles = module.sources + (module.hdrs ?? []) + (module.textualHdrs ?? []) + + for source in allSourceFiles { + if source.hasSuffix(".a") { continue } + + let sourcePath = Path(source) + let fileName = sourcePath.lastComponent + + // Calculate relative path within module + let relativeToModule: String + if source.hasPrefix(module.path + "/") { + relativeToModule = String(source.dropFirst(module.path.count + 1)) + } else if source.hasPrefix("bazel-out/") { + // Generated file + if let range = source.range(of: module.path + "/") { + relativeToModule = String(source[range.upperBound...]) + } else { + relativeToModule = fileName + } + } else { + relativeToModule = fileName + } + + // Create symlink + let symlinkPath = outputDir + module.path + relativeToModule + try symlinkManager.createDirectory(symlinkPath.parent()) + try symlinkManager.createSymlink(from: Path(source), to: symlinkPath) + + // Create file reference + let fileRef = try getOrCreateFileReference( + path: relativeToModule, + in: moduleGroup, + modulePath: module.path, + fileName: fileName + ) + + let isHeader = source.hasSuffix(".h") || source.hasSuffix(".hpp") + + // Determine if this is a public header: + // 1. Explicitly in hdrs array + // 2. Located in an includes directory + let isPublicHeader: Bool + if !isHeader { + isPublicHeader = false + } else if !explicitPublicHeaders.isEmpty { + // If hdrs is provided, only those are public + isPublicHeader = explicitPublicHeaders.contains(source) + } else { + // Headers in include directories are public (handles bazel-out paths) + isPublicHeader = isInIncludesDirectory(source: source, modulePath: module.path, includes: module.includes) + } + + if isPublicHeader { + // Skip duplicate header filenames to avoid "multiple commands produce" errors + if !seenPublicHeaderNames.contains(fileName) { + seenPublicHeaderNames.insert(fileName) + publicHeaderRefs.append(fileRef) + } + } else if !source.hasSuffix(".inc") && !isHeader { + // Source files (not headers) + sourceRefs.append(fileRef) + } + // Private headers are not added to any build phase + } + + // Build phases + var buildPhases: [PBXBuildPhase] = [] + + // Sources build phase + let sourcesBuildPhase = PBXSourcesBuildPhase( + files: sourceRefs.map { ref in + let buildFile = PBXBuildFile(file: ref) + pbxproj.add(object: buildFile) + return buildFile + } + ) + pbxproj.add(object: sourcesBuildPhase) + buildPhases.append(sourcesBuildPhase) + + // Generate modulemap for ObjC/C++ modules (SPM-style explicit headers) + var modulemapPath: Path? = nil + if moduleType == .objcLibrary || moduleType == .ccLibrary { + modulemapPath = try generateModulemap(for: module) + + // Headers build phase with public headers - needed for ObjC #import to work + if !publicHeaderRefs.isEmpty { + let headersBuildPhase = PBXHeadersBuildPhase( + files: publicHeaderRefs.map { ref in + let buildFile = PBXBuildFile(file: ref, settings: ["ATTRIBUTES": ["Public"]]) + pbxproj.add(object: buildFile) + return buildFile + } + ) + pbxproj.add(object: headersBuildPhase) + buildPhases.append(headersBuildPhase) + } + } + + // Frameworks build phase + let frameworksBuildPhase = PBXFrameworksBuildPhase(files: []) + pbxproj.add(object: frameworksBuildPhase) + buildPhases.append(frameworksBuildPhase) + + // Create target with custom modulemap if generated + let configList = createConfigurationList(for: module, isXCFramework: false, modulemapPath: modulemapPath) + + let target = PBXNativeTarget( + name: module.name, + buildConfigurationList: configList, + buildPhases: buildPhases, + productName: module.name, + productType: .framework + ) + pbxproj.add(object: target) + project.targets.append(target) + targetsByName[module.name] = target + + return target + } + + /// Build a framework target for header-only modules (just headers + modulemap, no sources) + private func buildHeaderOnlyFrameworkTarget(for module: ModuleDefinition) throws -> PBXNativeTarget { + // Create group for module + let moduleGroup = try getOrCreateGroup(for: module.path) + + // Symlink header files and create file references + var publicHeaderRefs: [PBXFileReference] = [] + var seenPublicHeaderNames: Set = [] // Track header filenames to avoid duplicates + let allHeaders = module.sources.filter { $0.hasSuffix(".h") || $0.hasSuffix(".hpp") } + (module.hdrs ?? []) + (module.textualHdrs ?? []) + + for source in allHeaders { + let sourcePath = Path(source) + let fileName = sourcePath.lastComponent + let relativeToModule = relativePathInModule(source: source, modulePath: module.path) + + // Create parent group + let parentPath = (Path(module.path) + Path(relativeToModule).parent()).string + let parentGroup = try getOrCreateGroup(for: parentPath) + + // Create symlink + let symlinkPath = outputDir + module.path + relativeToModule + try symlinkManager.createDirectory(symlinkPath.parent()) + try symlinkManager.createSymlink(from: Path(source), to: symlinkPath) + + // Create file reference + let fileType = lastKnownFileType(for: fileName) + let fileRef = PBXFileReference( + sourceTree: .group, + name: fileName, + lastKnownFileType: fileType, + path: fileName + ) + pbxproj.add(object: fileRef) + parentGroup.children.append(fileRef) + + // Check if it's a public header (in includes directories) + // Use helper that properly handles bazel-out paths + let isPublic = isInIncludesDirectory(source: source, modulePath: module.path, includes: module.includes) + if isPublic { + // Skip duplicate header filenames to avoid "multiple commands produce" errors + if !seenPublicHeaderNames.contains(fileName) { + seenPublicHeaderNames.insert(fileName) + publicHeaderRefs.append(fileRef) + } + } + } + + // Generate modulemap for this header-only module + let modulemapPath = try generateModulemap(for: module) + + // Create build phases + var buildPhases: [PBXBuildPhase] = [] + + // Headers build phase with public headers - needed for ObjC #import to work + if !publicHeaderRefs.isEmpty { + let headersBuildPhase = PBXHeadersBuildPhase( + files: publicHeaderRefs.map { ref in + let buildFile = PBXBuildFile(file: ref, settings: ["ATTRIBUTES": ["Public"]]) + pbxproj.add(object: buildFile) + return buildFile + } + ) + pbxproj.add(object: headersBuildPhase) + buildPhases.append(headersBuildPhase) + } + + // Empty frameworks phase (needed for Xcode, but won't link anything) + let frameworksBuildPhase = PBXFrameworksBuildPhase(files: []) + pbxproj.add(object: frameworksBuildPhase) + buildPhases.append(frameworksBuildPhase) + + // Create target with custom modulemap + let configList = createConfigurationList(for: module, isXCFramework: false, modulemapPath: modulemapPath) + + let target = PBXNativeTarget( + name: module.name, + buildConfigurationList: configList, + buildPhases: buildPhases, + productName: module.name, + productType: .framework + ) + pbxproj.add(object: target) + project.targets.append(target) + targetsByName[module.name] = target + + return target + } + + /// Collect all transitive dependencies for a module + private func collectAllDependencies(for moduleName: String, modules: [String: ModuleDefinition], visited: inout Set) -> Set { + guard !visited.contains(moduleName) else { return [] } + visited.insert(moduleName) + + guard let module = modules[moduleName], let deps = module.deps else { + return [] + } + + var allDeps = Set(deps) + for depName in deps { + allDeps.formUnion(collectAllDependencies(for: depName, modules: modules, visited: &visited)) + } + return allDeps + } + + func wireUpDependencies(modules: [String: ModuleDefinition]) throws { + for (name, module) in modules { + guard let target = targetsByName[name], + let deps = module.deps else { continue } + + // Find frameworks build phase + guard let frameworksPhase = target.buildPhases.compactMap({ $0 as? PBXFrameworksBuildPhase }).first else { + continue + } + + // Track all frameworks we've added to avoid duplicates + var linkedFrameworks: Set = [] + // Track library search paths for static libraries + var staticLibSearchPaths: Set = [] + + // Add target dependency (only for direct deps) + for depName in deps { + if let depTarget = targetsByName[depName] { + let dependency = PBXTargetDependency(target: depTarget) + pbxproj.add(object: dependency) + target.dependencies.append(dependency) + } + } + + // Collect all transitive dependencies to link + var visited = Set() + let allDeps = collectAllDependencies(for: name, modules: modules, visited: &visited) + + // Collect header paths from ALL dependencies (direct and transitive) + var depHeaderPaths: [String] = [] + for depName in allDeps { + if let depModule = modules[depName] { + depHeaderPaths.append(contentsOf: exportedHeaderPaths(for: depModule)) + } + } + + // Link all dependencies (direct and transitive) that have targets + for depName in allDeps { + // Skip if already linked + guard !linkedFrameworks.contains(depName) else { continue } + + // Check if this is a static library module - link .a files directly + if isStaticLibraryModule(depName) { + // Link static libraries directly + for libPath in getStaticLibraries(for: depName) { + let libName = Path(libPath).lastComponent + // Create an absolute path to the project root then to the static library + // SRCROOT is xcode-files, so we need to go up one level to get to telegram-ios + let projectRoot = outputDir.parent() + let absoluteLibPath = (projectRoot + libPath).string + let libRef = PBXFileReference( + sourceTree: .absolute, + name: libName, + lastKnownFileType: "archive.ar", + path: absoluteLibPath + ) + pbxproj.add(object: libRef) + let buildFile = PBXBuildFile(file: libRef) + pbxproj.add(object: buildFile) + frameworksPhase.files?.append(buildFile) + + // Add the library's directory to LIBRARY_SEARCH_PATHS + let libDir = Path(absoluteLibPath).parent().string + if !staticLibSearchPaths.contains(libDir) { + staticLibSearchPaths.insert(libDir) + } + } + linkedFrameworks.insert(depName) + continue + } + + // Skip header-only modules (no framework to link) + if isHeaderOnlyModule(depName) { continue } + + // Regular framework dependency + guard targetsByName[depName] != nil else { continue } + + linkedFrameworks.insert(depName) + let frameworkRef = PBXFileReference( + sourceTree: .buildProductsDir, + name: "\(depName).framework", + lastKnownFileType: "wrapper.framework", + path: "\(depName).framework" + ) + pbxproj.add(object: frameworkRef) + let buildFile = PBXBuildFile(file: frameworkRef) + pbxproj.add(object: buildFile) + frameworksPhase.files?.append(buildFile) + } + + // Update build configurations with dependency paths + if !depHeaderPaths.isEmpty || !deps.isEmpty || !staticLibSearchPaths.isEmpty { + for config in target.buildConfigurationList?.buildConfigurations ?? [] { + // Add header search paths + if !depHeaderPaths.isEmpty { + let existing = config.buildSettings["HEADER_SEARCH_PATHS"] as? String ?? "$(inherited)" + config.buildSettings["HEADER_SEARCH_PATHS"] = existing + " " + depHeaderPaths.joined(separator: " ") + } + // Ensure framework and module search paths include built products + config.buildSettings["FRAMEWORK_SEARCH_PATHS"] = "$(inherited) $(BUILT_PRODUCTS_DIR)" + config.buildSettings["SWIFT_INCLUDE_PATHS"] = "$(inherited) $(BUILT_PRODUCTS_DIR)" + // Add library search paths for static libraries + if !staticLibSearchPaths.isEmpty { + let existing = config.buildSettings["LIBRARY_SEARCH_PATHS"] as? String ?? "$(inherited)" + config.buildSettings["LIBRARY_SEARCH_PATHS"] = existing + " " + staticLibSearchPaths.sorted().joined(separator: " ") + } + } + } + + // Collect SDK frameworks from this module and all transitive dependencies + var allSdkFrameworks: Set = [] + if let sdkFrameworks = module.sdkFrameworks { + allSdkFrameworks.formUnion(sdkFrameworks) + } + for depName in allDeps { + if let depModule = modules[depName], let depFrameworks = depModule.sdkFrameworks { + allSdkFrameworks.formUnion(depFrameworks) + } + } + + // Add SDK frameworks + for framework in allSdkFrameworks { + let fileRef = PBXFileReference( + sourceTree: .sdkRoot, + name: "\(framework).framework", + lastKnownFileType: "wrapper.framework", + path: "System/Library/Frameworks/\(framework).framework" + ) + pbxproj.add(object: fileRef) + let buildFile = PBXBuildFile(file: fileRef) + pbxproj.add(object: buildFile) + frameworksPhase.files?.append(buildFile) + } + + // Collect SDK dylibs from this module and all transitive dependencies + var allSdkDylibs: Set = [] + if let sdkDylibs = module.sdkDylibs { + allSdkDylibs.formUnion(sdkDylibs) + } + for depName in allDeps { + if let depModule = modules[depName], let depDylibs = depModule.sdkDylibs { + allSdkDylibs.formUnion(depDylibs) + } + } + + // Add SDK dylibs (system libraries like libz, libiconv, etc.) + for dylib in allSdkDylibs { + // Clean up the library name - remove 'lib' prefix if present + let libName = dylib.hasPrefix("lib") ? String(dylib.dropFirst(3)) : dylib + let fileRef = PBXFileReference( + sourceTree: .sdkRoot, + name: "lib\(libName).tbd", + lastKnownFileType: "sourcecode.text-based-dylib-definition", + path: "usr/lib/lib\(libName).tbd" + ) + pbxproj.add(object: fileRef) + let buildFile = PBXBuildFile(file: fileRef) + pbxproj.add(object: buildFile) + frameworksPhase.files?.append(buildFile) + } + } + } + + /// Returns the header search paths that this module exports to its dependents + private func exportedHeaderPaths(for module: ModuleDefinition) -> [String] { + var paths: [String] = [] + if let includes = module.includes, !includes.isEmpty { + for inc in includes { + if inc == "." { + paths.append("$(SRCROOT)/\(module.path)") + } else { + paths.append("$(SRCROOT)/\(module.path)/\(inc)") + } + } + } else { + // No includes specified, export module's own path + paths.append("$(SRCROOT)/\(module.path)") + } + return paths + } + + func getTarget(named name: String) -> PBXNativeTarget? { + return targetsByName[name] + } + + + private func createConfigurationList(for module: ModuleDefinition, isXCFramework: Bool, modulemapPath: Path? = nil) -> XCConfigurationList { + let debugSettings = createBuildSettings(for: module, isDebug: true, isXCFramework: isXCFramework, modulemapPath: modulemapPath) + let releaseSettings = createBuildSettings(for: module, isDebug: false, isXCFramework: isXCFramework, modulemapPath: modulemapPath) + + let debugConfig = XCBuildConfiguration(name: "Debug", buildSettings: debugSettings) + let releaseConfig = XCBuildConfiguration(name: "Release", buildSettings: releaseSettings) + + pbxproj.add(object: debugConfig) + pbxproj.add(object: releaseConfig) + + let configList = XCConfigurationList( + buildConfigurations: [debugConfig, releaseConfig], + defaultConfigurationName: "Release" + ) + pbxproj.add(object: configList) + + return configList + } + + private func createBuildSettings(for module: ModuleDefinition, isDebug: Bool, isXCFramework: Bool, modulemapPath: Path? = nil) -> BuildSettings { + var settings: BuildSettings = [ + "PRODUCT_NAME": "$(TARGET_NAME)", + "PRODUCT_BUNDLE_IDENTIFIER": "org.telegram.\(module.name)", + "INFOPLIST_FILE": "", + "SKIP_INSTALL": "YES", + "GENERATE_INFOPLIST_FILE": "YES", + ] + + let moduleType = ModuleType(from: module) + + // Swift settings + if moduleType == .swiftLibrary { + settings["SWIFT_VERSION"] = "5.0" + settings["DEFINES_MODULE"] = "YES" + + if let copts = module.copts, !copts.isEmpty { + let filtered = copts.filter { !$0.hasPrefix("-warnings") } + if !filtered.isEmpty { + settings["OTHER_SWIFT_FLAGS"] = "$(inherited) " + filtered.joined(separator: " ") + } + } + + if let defines = module.defines, !defines.isEmpty { + settings["SWIFT_ACTIVE_COMPILATION_CONDITIONS"] = "$(inherited) " + defines.joined(separator: " ") + } + } + + // C/ObjC settings + if moduleType == .objcLibrary || moduleType == .ccLibrary { + // Always suppress deprecated warnings (e.g., OSSpinLock) and don't treat warnings as errors + var cflags = ["-Wno-deprecated-declarations"] + if let copts = module.copts, !copts.isEmpty { + let filtered = copts.filter { !$0.hasPrefix("-warnings") && !$0.hasPrefix("-W") } + cflags.append(contentsOf: filtered) + } + settings["OTHER_CFLAGS"] = "$(inherited) " + cflags.joined(separator: " ") + settings["GCC_TREAT_WARNINGS_AS_ERRORS"] = "NO" + + if let cxxopts = module.cxxopts, !cxxopts.isEmpty { + let filtered = cxxopts.filter { !$0.hasPrefix("-std=") } + if !filtered.isEmpty { + settings["OTHER_CPLUSPLUSFLAGS"] = "$(inherited) " + filtered.joined(separator: " ") + } + } + + if let defines = module.defines, !defines.isEmpty { + settings["GCC_PREPROCESSOR_DEFINITIONS"] = "$(inherited) " + defines.joined(separator: " ") + } + + // Always include module's own path for header search + var headerPaths = ["$(SRCROOT)/\(module.path)"] + if let includes = module.includes { + for inc in includes where inc != "." { + headerPaths.append("$(SRCROOT)/\(module.path)/\(inc)") + } + } + settings["HEADER_SEARCH_PATHS"] = "$(inherited) " + headerPaths.joined(separator: " ") + + // Use custom modulemap if provided (SPM-style) + if let modmap = modulemapPath { + // Get relative path from SRCROOT + let relativePath = modmap.string.replacingOccurrences(of: outputDir.string + "/", with: "") + settings["MODULEMAP_FILE"] = "$(SRCROOT)/\(relativePath)" + } + settings["DEFINES_MODULE"] = "YES" + } + + return settings + } + + private func getOrCreateGroup(for path: String) throws -> PBXGroup { + if let existing = groupsByPath[path] { + return existing + } + + let components = path.split(separator: "/").map(String.init) + var currentGroup = mainGroup + var currentPath = "" + + for component in components { + currentPath = currentPath.isEmpty ? component : currentPath + "/" + component + + if let existing = groupsByPath[currentPath] { + currentGroup = existing + } else { + let newGroup = PBXGroup(children: [], sourceTree: .group, name: component, path: component) + pbxproj.add(object: newGroup) + currentGroup.children.append(newGroup) + groupsByPath[currentPath] = newGroup + currentGroup = newGroup + } + } + + return currentGroup + } + + private func getOrCreateFileReference(path: String, in group: PBXGroup, modulePath: String, fileName: String) throws -> PBXFileReference { + let pathComponents = path.split(separator: "/").map(String.init) + + var currentGroup = group + var currentPath = modulePath + + // Navigate/create intermediate groups + for component in pathComponents.dropLast() { + currentPath = currentPath + "/" + component + if let existing = groupsByPath[currentPath] { + currentGroup = existing + } else { + let newGroup = PBXGroup(children: [], sourceTree: .group, name: component, path: component) + pbxproj.add(object: newGroup) + currentGroup.children.append(newGroup) + groupsByPath[currentPath] = newGroup + currentGroup = newGroup + } + } + + // Create file reference + let fileType = lastKnownFileType(for: fileName) + let fileRef = PBXFileReference( + sourceTree: .group, + name: fileName, + lastKnownFileType: fileType, + path: fileName + ) + pbxproj.add(object: fileRef) + currentGroup.children.append(fileRef) + + return fileRef + } + + private func lastKnownFileType(for fileName: String) -> String { + let ext = (fileName as NSString).pathExtension.lowercased() + switch ext { + case "swift": return "sourcecode.swift" + case "m": return "sourcecode.c.objc" + case "mm": return "sourcecode.cpp.objcpp" + case "c": return "sourcecode.c.c" + case "cc", "cpp", "cxx": return "sourcecode.cpp.cpp" + case "h": return "sourcecode.c.h" + case "hpp": return "sourcecode.cpp.h" + case "metal": return "sourcecode.metal" + case "json": return "text.json" + case "plist": return "text.plist.xml" + default: return "text" + } + } + + /// Extract the relative path within a module from a source path + /// Handles both regular paths (submodules/Foo/...) and bazel-out paths (bazel-out/.../bin/submodules/Foo/...) + private func relativePathInModule(source: String, modulePath: String) -> String { + if source.hasPrefix(modulePath + "/") { + return String(source.dropFirst(modulePath.count + 1)) + } else if source.hasPrefix("bazel-out/") { + // Generated file - extract path after the module path portion + if let range = source.range(of: modulePath + "/") { + return String(source[range.upperBound...]) + } else { + return Path(source).lastComponent + } + } else { + return Path(source).lastComponent + } + } + + /// Check if a source file is in one of the includes directories + private func isInIncludesDirectory(source: String, modulePath: String, includes: [String]?) -> Bool { + guard let includes = includes, !includes.isEmpty else { + return false + } + let relative = relativePathInModule(source: source, modulePath: modulePath) + return includes.contains { inc in + if inc == "." { + return true // All files in module are public + } else { + return relative.hasPrefix(inc + "/") || relative == inc + } + } + } + + /// Generates an explicit module.modulemap for ObjC/C++ modules (SPM-style) + /// Returns the path to the modulemap, or nil if no public headers + func generateModulemap(for module: ModuleDefinition) throws -> Path? { + let moduleType = ModuleType(from: module) + guard moduleType == .objcLibrary || moduleType == .ccLibrary else { + return nil + } + + // Determine public header prefix from includes + let publicHeaderPrefix: String + if let includes = module.includes, !includes.isEmpty { + let firstInclude = includes[0] + publicHeaderPrefix = firstInclude == "." ? "" : firstInclude + } else { + publicHeaderPrefix = "" + } + + // Determine public headers + let explicitPublicHeaders = Set(module.hdrs ?? []) + + let allFiles = module.sources + (module.hdrs ?? []) + (module.textualHdrs ?? []) + let allHeaders = allFiles.filter { $0.hasSuffix(".h") || $0.hasSuffix(".hpp") } + + // Determine which headers are public + let publicHeaders: [String] + if !explicitPublicHeaders.isEmpty { + publicHeaders = allHeaders.filter { explicitPublicHeaders.contains($0) } + } else if module.includes != nil && !module.includes!.isEmpty { + // Use helper that properly handles bazel-out paths + publicHeaders = allHeaders.filter { header in + isInIncludesDirectory(source: header, modulePath: module.path, includes: module.includes) + } + } else { + publicHeaders = [] + } + + guard !publicHeaders.isEmpty else { + return nil + } + + // Generate explicit modulemap content (SPM-style) + var content = "// module.modulemap for \(module.name)\n" + content += "// Auto-generated - do not edit\n\n" + content += "module \(module.name) {\n" + + for header in publicHeaders { + // Calculate the symlinked path - matches the symlink creation in buildFrameworkTarget + // The symlink is at: outputDir + module.path + relativeToModule + let relativeToModule = relativePathInModule(source: header, modulePath: module.path) + let symlinkPath = outputDir + module.path + relativeToModule + content += " header \"\(symlinkPath.string)\"\n" + } + + content += " export *\n" + content += "}\n" + + // Write modulemap to the public headers directory + let modulemapDir: Path + if !publicHeaderPrefix.isEmpty { + modulemapDir = outputDir + module.path + publicHeaderPrefix + } else { + modulemapDir = outputDir + module.path + } + let modulemapPath = modulemapDir + "module.modulemap" + + try modulemapDir.mkpath() + try modulemapPath.write(content) + + // Track this file so it doesn't get cleaned up + symlinkManager.markFile(modulemapPath) + + return modulemapPath + } +} diff --git a/build-system/MakeProject/Sources/MakeProject/main.swift b/build-system/MakeProject/Sources/MakeProject/main.swift new file mode 100644 index 00000000..31fd3656 --- /dev/null +++ b/build-system/MakeProject/Sources/MakeProject/main.swift @@ -0,0 +1,51 @@ +import Foundation +import ArgumentParser +import PathKit + +struct MakeProject: ParsableCommand { + static let configuration = CommandConfiguration( + commandName: "MakeProject", + abstract: "Generate Xcode project from Bazel module definitions" + ) + + @Option(name: .long, help: "Path to modules JSON file") + var modulesJson: String = "bazel-bin/Telegram/spm_build_root_modules.json" + + @Option(name: .long, help: "Output directory for generated project") + var output: String = "xcode-files" + + func run() throws { + // Determine project root (where we find the modules JSON) + let currentDir = Path.current + var projectRoot = currentDir + + // Walk up to find project root (contains bazel-bin or the modules file) + var searchDir = currentDir + for _ in 0..<5 { + if (searchDir + modulesJson).exists { + projectRoot = searchDir + break + } + searchDir = searchDir.parent() + } + + let modulesPath = projectRoot + modulesJson + let outputDir = projectRoot + output + + guard modulesPath.exists else { + print("Error: Modules JSON not found at \(modulesPath)") + print("Run 'bazel build //Telegram:spm_build_root' first") + throw ExitCode.failure + } + + let generator = ProjectGenerator( + modulesPath: modulesPath, + outputDir: outputDir, + projectRoot: projectRoot + ) + + try generator.generate() + } +} + +MakeProject.main() diff --git a/build-system/appcenter-configuration.json b/build-system/appcenter-configuration.json index 5623b6d4..b86e141c 100755 --- a/build-system/appcenter-configuration.json +++ b/build-system/appcenter-configuration.json @@ -1,7 +1,7 @@ { "bundle_id": "ph.telegra.Telegraph", "api_id": "8", - "api_hash": "YOUR_API_HASH", + "api_hash": "7245de8e747a0d6fbe11f7cc14fcc0bb", "team_id": "C67CF9S4VU", "app_center_id": "4c816ed0-df83-423c-846b-a0a8467dc7d2", "is_internal_build": "false", diff --git a/build-system/appstore-configuration.json b/build-system/appstore-configuration.json index 79702e8a..9dea3548 100755 --- a/build-system/appstore-configuration.json +++ b/build-system/appstore-configuration.json @@ -1,7 +1,7 @@ { "bundle_id": "ph.telegra.Telegraph", "api_id": "8", - "api_hash": "YOUR_API_HASH", + "api_hash": "7245de8e747a0d6fbe11f7cc14fcc0bb", "team_id": "C67CF9S4VU", "app_center_id": "0", "is_internal_build": "false", diff --git a/build-system/example-configuration/provisioning/BroadcastUpload.mobileprovision b/build-system/example-configuration/provisioning/BroadcastUpload.mobileprovision new file mode 100644 index 0000000000000000000000000000000000000000..90bc0e0e03f08a3cfe9f94470216ecd2b10b3bea GIT binary patch literal 4657 zcmcgwdw3I77H`s0EVZDrA|hbz;)|MQCP|Z~wAGo+G>>NToIF)jQxmS42e-ANYQuu!y>g4|H`^K=G}gqU&Ro^}U~a)0Q;4xUPR_zpvjn_s;#D z`@83yd(LkH4L3H;U20qUMT=^pdfhNItX8SuW~ixgLi^-;^^6I1P|X6>y5Tv)4dv!- zJ*O7ZeCrS+$y`S0ZG$_YwpK=TudoOVCi3T$Y#xT~$TEvYDCw^p0a=R2s%st!7nt}K+b9w{R- zl2UX6V;TVHh%50nu+6CT@6iFXagJ8{CN=e>m|`E;&W^dLG}EK448g3dNPuh~$?yy- zQR!Ax%208d0xSa}4<388V=Klj@ElDk;K24cBPd*gV1!JfYUZqj`~rY3cDaW&2#avPmJjdMti@Hk6D8Po$gocc$9Jm3WT?a zWW1$_-U~%K10`Pwz=Qr&0WTpwlywDBL?$e}tDnT9q(2aM`vTsArGz*}-$h7-50V6G z2xFu@Pmo9vz}+OSCrU)Y9n64fVr05zE*~cGP6DH?2pqr>kUiconvYontk24q06cDU zh!G(OQ6Y2A&7py4sK7_{j%3V_T0D#8XipaM_~XDWnhyusLN4W z26CY`6U6wEWYQ%P#ke?xI^w=4Vxhba(HORQ_%Ih3U?X`wNV`DV9s_WDMrItH)}Y58 zvV;ZB{|47u?x&(8%fm+?W`Y*mze6 z>lC?xs1q|foDmF#Oi7=C!DC^$Rt@ z5k>-I^B4;P=7T(l*QN!@jv*|A5SB3PEN$LP6KKY2vB*{cN4$Z4G*93tI1I|- z{iv75kbDFOCU^up$kjpIAiy{x5rY{3S2!a+GzdBSxj|7c3@Ca>R&uzE10uy_Gn6qN zmK{Mq>=Z;@jP)4?%mUlRNxGyUWIF8t?yz;D7H2^l&jgBoIVA9eKcCD=5E(3)kyI)Z zq!4NWHyZ zpXxOE>~x?#T z-RRj5cA%WhNJ3_;Wr3C|cO*DoVJa>8r&-Aios&4FIO3)Q!DFRNPOJvGspmkC9dE`X zD-IgI?@_L1&?tMBQ8WrAr!*ALXY%9Z5)?1jL=8laPEwozf=h!SX?~i{xHjmRTT7st zbK`zfGATx=T~m|h+FF@|7er3_aW$bER}(PQ4M9Ip6LvWaBpgFf0>v^w0F5c7>PbiB z%E3fbno{UwE6`YeB_lDdKl6r+nDy|KlE_Hu>aNxJ9Z+hV+h5~?8V^)xk{~_DW!aA# z8F%{swso*mR;6NpRO577Y7LA~;L;44an{`I9!^jgmXZ4OMnjMGz+gqidrpp+IHO9Ds?lkBVM#u#HaNFRh2(Owtunki*g=p;Yx?DyNPE*u;AxB!!lQ`G@KH2bI)9Rx!< zGi=i9VRQ7T7JcpTm)^dtYO6i4YC>IoSv92&Dyt@`%PLjf-cye4SXsE^oTs=?KHjtY zvK@EWO?R!j?47BxX!{wPw(D=OaXxEoZwZ~?Uy5O(7UYpJCxpL~V((;D)dxQlu z_HI~n*&rZE-g=g!7 zN_o=We|nwVFIOo;e}+()pA^!^Q@ z8%_JZ{9y5G)26J6oqX9N?_IY0&{^}A%$|MtJ^$F*w0L^`*~`|i-g)E}P4_F4UOMXy z&)kQvP}i&KRM&MwUC?9@$XQcVs_z;a>eUm6p%W_O<16Et(2T}I+8b39CQPhvs6N#< zHh}kawcZ8k1iY)NyJg3F_ja7OwYPcNN&8-vZr!`)<~gta{`w6!U9;(``P%~nzIQhy zUbmLFtew(!TasP-2iu%;kV76?G_*wLcx=PvkKVrY{r$ul|5n>i3ak%?AB-IJ>iILY zhg@^c#-(WXO6B#P*^e&SeEnDD0oPyXg_oN5EN)x2>+gFP=U1$K_PEFT!uP_Bo;RoO zJ3;(inO}JB#mg>yVDCjMH*zm)?wLs&_J4(L`}oaQGMjhIJ^zW0a(UCdM-}D#1*@+8 zFn8+%uYQHB`;5AC@vX<6w>~jX$GVw)F6HgD+h5$U=!GZGdjme_)akoU?3wql=Lw=(}P${yq2 z^6Sc~6QSwV3*J-@H&+J}RnTv%gQf;}TElPd-Sy7&iKo4+zUZ}CPagL4%lNaqH=PD$ z8=F9|8&qY}VGVHnS2=*(*gWaLupC0iEvwcQ4a`XOomv}G5$*YDe+ZNitaDUd~dSb>| zr@T1%mh6)KO*ab<9esM5LEb%bPRntRuYKKQpS-6q|AL0*Y0*{AyVkGrmlw^uE7;O| zdW(I*Ro(mE_3-KY5;q)v?v^Rn1!r!X@zHbXl~3>Z>b`sLzrQtUJglHy`tlY>Id++z%`%9qJ-91a!I@f-Ds%l#M_SMkpc9jb5g?hTX`e$~u&+BS~S~jS*uU@pe zv);SMx}uWj)uW6ga|K~}AFPG?)QpfW(44?7?~9Oj&0yb(;px_MF~5yW1aP&;bF!ii zL{P8GrtZ_|bciVOj82DM$p9YL}oef+AaW6U$R@o=Z~-@YY3x4P1s}q=3X^xJuI?B}r7na?`ZUWVYEY zVPiDXYECz~Z;Y2sV#cgulrlw)27_Teq=z*yq|uv5*gRyg3=Iq5|r%- zhSCI?EBnd`EE)`A7>1$%o_3;gGK}I>+y|xeJV(bvVoGl`x$Gf6<%kODyj^kONy;0k z5Wy-^2-XsYAe0!0)Iu=;kA`y)!Y2*xY$}X8VjN76IKg^wLhplo4u6z@qI^UoLPQx4Ca?zi zV9<*pA{mNQ91)0jlDQ~O1m&^~EQ2)%UUo+#jnIZ&Wh|I*dkRTcb~Nonf_NizXb{0z z77rj8*b*$*z-JWkm1Squ6lJ{j{+dH5n=L{jCv%lB$<%n!%j0@C<$QUa`?@Ek14S1X*GI2v?K+q>;k~0*2sJ}3n;s+^%hoOpYCd#Jak&HbF19+?eo23zb ziPABU4|SR$CX^+!K9Q)>;wb8-LrKI&1>K@4?hNp8F5+PmWdpc&k-RGf;I4wqxCb23 zfGcK;3!E=x3i#aKV$O$FbDYZ?4_Rm|I*>;)l_VXYaZ(=e1xFP=i)QhX5^YmN))+;K0A5b!Y)BbL z5Q;Dn7+c0zkgyOGK!Q#kxa=6hG6-QyG_DQp2FcQugJ}XSIBYiA0pLh5Vn@pajsj;; z7Pq6pG=`KDI55E@*h!ua;s*)F5s4Tn0Jy@L@X-;-Yv)EpgWypN?xN)OnLHxJ6bqDz zj?3<781@RHKE;NN9*e*Za*{qP2!#O`fV-UosLfl^(S=AgEXM?%2$!=Z2_mC43zEwf zqEsjr^D0TZ#pHBwgRVl{BxZB&O5Q-S0A7nbOF3_m?@zeBq?{aK2JiuW0_QkOhBUbB zhTMQT{m1jABFU;w;7;$R2$jw(w*nXD(2_t)G4sgH}KK;{J5 z8xsw>Xw^t&iMUbc@TG+SHi||Fu@uNS`T?BE6AoH8sN@s*oFkBmcp`GD8j#ai$6wepRQ{*xfFSle3WG}C&5nA`OTyb5izQHRZC;fXRp`TI`Ff;@~|D+^bavW$khM)wBW#~y)txZqIq{_+E zQ=U@NSv3$>VWl83>aX!S#!LoyO34(YeDlO+?k43K=MT5I-{O7a!I0o;PRX%vWH4$5DH_ty84FAsjj*0+q~yzbG&-)lo2Dj@fm==lWpPXZTXkdd zczAl-*x&YghYZy_3Q)a0w!NcW)eZzdbZYZUfT|Zr{tI9WWbW>Wbau>uKyrCTjRD0i zaj8;60S~GFDJbgmry{%ro&}xJfcDOq9>%@)u*>1Yv9WIyV6uBwdn61km~*Ngf+4*H zHX97EB{}C*L+kK=eSBTj*XppUtF5E1I;{<=tERWtRjRh5-&v^LRJnWQA@0xbKmXi@ zgLk{kyKma?#;jDb|GT>$HvEF$_{wu<`%bLYEiBHt>aim)FJPbl!K`()Ydhbu3Kz{g zx^wH#Hg8&UIDKifl)UzmUCVdnKEB6z#%E8zPWfhWhQ-_iXI_Bc(9X4U$BR#Gs@%A= z8+uvT7)qVfw{F4W^_#3$)Gw@D>-g#6MfdM8&fjrW{fOg;Ew*L#s@WGTe|pyLje-5Y zQtH!>e)Q?IL+}3K{YTvQZ@$U?ELaD5N`rTU_?v!i|5YUsSi_}h)~d}vp`_6~NcRz1;ogdi|jsGe!=h&6= zbf?_B^0u{T@dwH)hl}ssaQ`iz)IGl6q%T=-d45gbx+BjVT~pq;?dfwL9gg1%cL!dZ zd;C1{vO0gs_pV=e_1>e`Y`TqmQL|@$+W6ro=!5US_EO>ggG;Y`TwAa2T6REDuDs}` zpZuwG$KGR~Alv^+{c_D63oqZ1S*BgymH0dBa5xe9tu1z zEdR^?k#u+Y3(c0Rcg+3U@0NJZIr!Q9neXl2b^P`xZ{2o1TyMVzJh{6XuPyaIzTu5C zroO;$ud5bAbDIymrvvV74yLJ~Z#4%!o$&0=Z&WM6v+sEhee1f-&%C<%K4e9A-$EWL zcK3i}cdF{<(>r1M|Kfn$-8=ESM9=B%y)eD~{}Hv8I@s0K)>&7zZDH=+a`6Wy?MJVD zd~E$?xkb~?VrMLM%AvIn&OY;=t;agH#@l}RBl@Ecg|~iw1BZ9lUzj^nnzqY#)0@fO zgC|}e+WX@VTz~qYbVPaPmiu0LB6rAM+fxzFS)joXp@ZjxtzVOt|xOUFB9zCn?^QEsbqHFJ&^F~(w z_JK3jA8=my?0yJ+@y*+jzb=07DRhrzmgD@bg!A6TUvSQg=+zH+-d;K%d1U5`2fEhJ zSbNQm4Z91Mdzb9&eDJQ7k6*QA`@V#JPBN3pY$nNMX0ns4 z$`KJ%RFp%XAczGytfJPV1r;lzr3xt8ic-X*wiVCXYQ42z_Q*ybYTNfT@8!LG-}nFh z@9+Qre}NV?HP2gWU;6Px#n}3di=ajI3Wd4_YHk|Seo{mIj4^dk4S{0gqB)Bi%PspY z7ZfvGYd7BazEg2t}1w$nmrw zm0WLxR^MO9gXT~QK%vfTvD(TqUy zsaAJRVv}rwlz>UrTvnp9l0`L4HOhq>rf^W;9MIE(SD=$@vC5Ye1hOKaD#2+4B(+ax`++HeuQy{5Lm>moV$m>VniTFdHg|N(K-M-ht8q|%!LRX(M3?g zA>#yG0&ow3Y4HKP=n3V(Ha@srvnNLgOpl`p8>|juFvuWZ1d-#mAmg`j0{~9hoqRMK zg2=GB;9-$qEL`MbT4yRAKx|=FjT0Eo^kTTi4Y_Te5Dta7AdmZT8S_Qait;|63x#>Y zA1vB}5N9XSAq@A4vK1VIGaDv5L&1t`0}dJWMV-C5m?PDnaKk>V;yT0!qYQ(2VHA+~ za#rvgf!(rbFBw9#tEYXymX(d>Y&0#h#Q;GMaJ-AdG)}TtT`B;$+~*PFPA{WDI8VDH z+Hcol5f)EIv_*YZ6B7wmn|C9goGH$kNNq1o=ACqiNvQjhJux+ahjVJ9(5ESoDjISl zb|XalQ$)(m<0XpkN1T*D23tv=lQ%@{UM|80dzq-L1!)(|IN|{A$ceO5Zwq-HVQVDI zy8Q;P+v&=u-AF0TI$ROInL+C@+N(qTcQ67bJ*bgam+_lR5iYneqOzMkSf3Zq$9xE7CsnwuUzgOCGJc~G%X;#ekb%(xxFOjmFqA2b>Up*| z=0XilR}@7cW6H0iv3Nur@MRDi>^8B6Km;V3*+mGHRpJ?42+jkz97|ha8G~UGH{d8E zqYMa`AM(OJy9y*b3Nti}TceeR_x`+_})jAIBe24S!s#Fs!} zIf?-m9E_ge>cAclUJW1zsq=AZv&QL(@ z%JP~xIXuA+*{1xo=joaOL2xkc`om$D z6zee?>^9cq$VCi%D(x(0v;+g-0}*>6?aFiQQKyR#V|}z9(`%v_%bJsf*3qL)>y3U# zBG?{wMT-cBvNlgTldx;7aeJYX^L@I4vp|(_j^ubvNu##gaLDZCtg=VNYQyb@k~MEH zu?8!X*BOg;Ka^6phjRtBT8L+90B;Z47?Z|UVn`^N>h)(l1I{d=iSR_0NM}V?nAfU8 zB^{B%BRZAMoydC8ek6$V1#i;U4&Y=4w^1sSl!<22Hg7!G8x-Ruub4m!hIXNl$q(d7 zx=-%v0;Gdgu~OkI->j_|sM8WhXF$CkEsMjwVW>G&y3BA{SF+W*K2kzYP{=#@REIRw zI6A5gq~rS=$dITwEs;ur=5vB1D#?Vz_E+nDvqcgLv}&+*RC*NXdc*yX8maId2~yxW zvZTz=qDV5Je~l{n!(hH3g8D9X6tV(c>1zpzrj-3Tmj7RvNHm%G@0?WA?S^bWkwFEK z7P2*sSL{f#oJ3bz^ABq!(usn=N~J+JjRcSpas|E`=w{1EpB-(;gA^wW;Wt#SX3>!L z3@s@oQcNpJj?2lT$|Xrotce`qo6@5F=oeqj54zGBh<}s-(TrlWBt9lNPoD``w#Z4Mg zNrHZ;)FnttY0{W0YDA2>Mj|MagLAP(H8?eQkFOj2 zY%Dj3P`M!omFvSB8|oGH;2i)>tiBBtE#M_EUu}krO%1`uh6xY|GDo+fU=SDBc%d>g zceZ}ZDXr&@diXr`RA_Pq+A?8$0CV*O95y$G4n8!%X4BOAU;vsmZK4KJLmIQ%sMV^? zv1t>vwaXt{d|A;}Yp`NWT|-$hsSYYD#@Ck>in^meKdWO+@!kdd*?+wM(u*ti-Rm&k zcjL;xPL0RfFWj+5dndQ*)fZ29f3j3{R({%*zd!uSEas(kQUj-M@XcLh64MN`JEOnk}~7dvAGbOL&{{_$O~K ze&w`DYvbpweEiLoFHSh~{3~Y7n*6{!2bvd8Z@6^%=9>j0~ zDC!iOIw2Eu5(wm(lN5@t8yg$y$1Z}-sjPoiSvO-Qj8fhwxO~5tD&h8oY&QQ z7o-#LuA=VFeQ!S4v1E5w%W3BxKPKFDbnUO^9J~7Vt#{nEWBo;Yg1!E~Z%w{xD?hSf zQrq1rX2W&%ISb%%4=?P$LgRd5>*~GtEPd-k{K7Bm9p?r&hawL}ryW~1LpAQU1>2V* z`E}B(2lDS-zw7o-%f0R=6PMgzera*r^25&`T`aHKaOmtOx+4#&o4l`2KYk8>S(&@! z@)gUk+IjSvHQU)gD<7Dd(0%wR^1Jt6e>t~n-@Ii{b(G6H&fhCZ%Pzk0m+uzt+Ij3# zc;mmwUoXDvtR&0an0g{$=sQq$HiS^o$3Y!S*gHG^O`D$2%_ z8`acLVu0AxGW>T%^U3usYHH(8;kBANIA%;;V_8wRbLwPy_j+;e+NZXy+Phu+>Na^i zd)YZ%z7|JUJofAv&!u0PV7zqQrSb(=9i8>hbrX*qJQnCa-u>CB*IzYp0k>t`3)ktk zt)6l}^}J~#`M1-L99psbz}N2wzJ6rG$`75Ddd*GIO;FaN-N&(6f&(w-3Y+vhH6yR7r^M$eIJXWiQQ40-H}cVBzb zMElG0JyRwhJd=#w+SfdTPVJtXG@Y{hI#r-^nM!jzvt>$hek}jhz3*5$rnEjeb*O3l E2SRNS7ytkO literal 0 HcmV?d00001 diff --git a/build-system/example-configuration/provisioning/NotificationService.mobileprovision b/build-system/example-configuration/provisioning/NotificationService.mobileprovision new file mode 100644 index 0000000000000000000000000000000000000000..a0f5e98ae38936a8f312f1d07428043557d96fd7 GIT binary patch literal 4669 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 literal 0 HcmV?d00001 diff --git a/build-system/example-configuration/provisioning/Share.mobileprovision b/build-system/example-configuration/provisioning/Share.mobileprovision new file mode 100644 index 0000000000000000000000000000000000000000..636d4658e044082788ed23fe9d47708b42ee2990 GIT binary patch literal 4627 zcmcgwd2|zX9&dVKTM?06K}4t(MX6~fM{__aZzeOjC+Fn!VvDO4qqJWp0cR%Pnd8ofd1QTe>J;$)-uh7{8C z)R0t^6vuRmf<>fMwCLS9K}{Vp$EHzz#){EE5g5xv)Z|HXGb**yu||k_OR?g-DyXfV+xt+EofFT~M&wTXOpW z+~-LYuoCP>XuA)A1zgYC4Fnb1d9GRp~O)CLh9qZ%isMe2mbBr})L)8{r5JezO z+^wRpa6l-zl86zuYZ*AT(w`ToDw`2QeUmh0zm~ z4(tE{#$X=rO9QybsIgohWHT^*oHAt^gihjdO>pLfU@rN2{M)MrdZMy;naDow%U zhT`&0e>y9d^WhW);GJG0tyLI{GzrDxR(H}-vZM$_fFn{wA|=@ToKog1s)#rqP|1w; zNXm)k5HHSUoiSr4fRjnw7?o+oWH6a9I>TP8R|prKLIlmKJNaxfQ_7H3Uw+;^aCN9F zRythI4|^*H`n1SWNzkvy+TzGG7#;_exiZq$rA)Q2kG9YgH1aMk-X#u?imvL&==$-< z7#1F;L{iFAT$&dJDH#!&T(!fu^oV?xk_`=u${YfdZDhtpM|*l#gycDvEJ~AFfz#*U?aR{>z0CbJQ$rMRb(NR--s+JCsloJuABq>JX?SM~t#57N} z|Dw7W66Rz{F_z|&)wilq8|7k*!&75;jop<}AwW`&QLvvDGUn?4E$c+KjZ}8B-4K%6 zZ2DROLn<&yib&gP>bZwWi4;xoy-KyJM>e_`p=n;P8LjF$LMyw&T1rEPby7{3Qb|>+ zNK~rRQVOX$rd6o4N=hx)#(w;)ZhUjZ!Jsg(iee8-ud(W^RTP&NAnS9Ojdk#*g_uW2eX=z#G(Vo<0Q}=JW?e3db4eW|s;md}W zUA|@Bmc*A2t0sQ4`yJ9giBZmCo;>Xm?8bVgo;i|vc~xQM>}Kd-YK1#|X2;U$vu;|| zb9MRB!ZPDsyJkMVUNvp~HRV0VJ$nDG{R>aOWZv#cTUR&-pAgHf`~UOxxEJ4l`{QRV zkKer7@P4${y64-4_S2Lv-m_97=AMEQ#};0{(Kxu{j-wm>cWaIudw<~I=@V`XU$Fey z_m=NH`K-BDPoF;Vkq>sY3`}XbV(I#ucl~~wbj};CuP=DhIs2Jg>l-9>lDpTH%YHH~X%tbS@}ZUpCbwcZ8k1iUM$yKm=v zk9I8@oY!{x`9}`$oA=-L=a~nt+py{0bzA;)>2|Nx{qCmNA!B*lnh72E$LTeHG|jvc zKIzGYxvLeH=Qgd}@xbDvpW_!FuQ#9XUGED#5uAKr$yC`%>#n?KF_O7mJhUtG(T$I9 z_^NER|21;?O}c#p9ZUE8eg8my#hTq`Ki3<0RNm}-Ys!)HxIdKH%dfg&>9vpTzkby{ z%s-`%OpB;K{|b5fOKfZJJk{7zl*c z&Z#b18s(=q{%-ozDO0v?!(A7Czs=E^&F*#GHvmJK<`xj_MoC$7YNI^*n;0N8w~hP= z(Q;~in>@PqxA0m`A8cx>Yb;CZ9-H#{u}faM{qW0|ZB-5~IzFSvqYCW$G*>#HmAuq? z#?x~rx)-#6Vqa1m_d#i=ezlYd+b`WY@PWcUf1Pgk%@_F__eBhkPFu6_i2Cb}SMMmD zCf#^^-AC(S{kO+&*}RebrgdB|I@r1Ooc^~@K7MQob{%To{>6uzzKd%AayYj}GPBwG z;nKrT*;a0l-?9D4t*36=NuT|P>_Fz5k3;utR?I(m+qAb3%qKlP!FE4!C&+_BG__k33x9vc4zDbfbd literal 0 HcmV?d00001 diff --git a/build-system/example-configuration/provisioning/Telegram.mobileprovision b/build-system/example-configuration/provisioning/Telegram.mobileprovision new file mode 100644 index 0000000000000000000000000000000000000000..534e1305d53eff717188429fcc785748caecca80 GIT binary patch literal 6173 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 E0Ap|cy8r+H literal 0 HcmV?d00001 diff --git a/build-system/example-configuration/provisioning/WatchExtension.mobileprovision b/build-system/example-configuration/provisioning/WatchExtension.mobileprovision new file mode 100644 index 0000000000000000000000000000000000000000..8744004281be19e0798a5016b04b719d1d793726 GIT binary patch literal 4684 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 F{{W9V2s;1( literal 0 HcmV?d00001 diff --git a/build-system/example-configuration/variables.bzl b/build-system/example-configuration/variables.bzl index c035abfc..ed0fce46 100644 --- a/build-system/example-configuration/variables.bzl +++ b/build-system/example-configuration/variables.bzl @@ -1,7 +1,7 @@ telegram_bundle_id = "ph.telegra.Telegraph" telegram_api_id = "8" -telegram_api_hash = "YOUR_API_HASH" +telegram_api_hash = "7245de8e747a0d6fbe11f7cc14fcc0bb" telegram_team_id = "C67CF9S4VU" telegram_app_center_id = "0" telegram_is_internal_build = "false" diff --git a/build-system/fake-codesigning/profiles/BroadcastUpload.mobileprovision b/build-system/fake-codesigning/profiles/BroadcastUpload.mobileprovision new file mode 100644 index 0000000000000000000000000000000000000000..76d68f793208fa47034c9f96937737fe05de730a GIT binary patch literal 7398 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 OeT!Zu-EDp!bPVL z!E%~bK9|h)W+}Qm-_Zd?2fJdq!|6}#tvKk@B1b2O8v1Zs943V!Rw&cUa9fwLmA*dG zLQm4j+cNPsafmV6D$LPVW{&?Pjv*6aTBKw=ok{beAfqB8+f(WDje3#K(egpcD0>HJ zZkX_*Bb^->qmuudYgHUGWO-DbKu2;sD;5W}kHpLH>0G8l7mfM~UsS>#lsIX`C)lba zeaI|Bi!zZC5;BV8()rO`#wbpx3Kj$$iBoI}2C zYMH9akXMfdcp<~`KTW_I84Ssk5D5L0Q9SK!miFAWWe-zcAN+(B&u{58oSghRL$W+5RJST)dJk1pNlkfMiS0lWAMk1Jbi#T4QLwQ>lUU z@{z^xbmaA#v5KF=@vAjD4XljDWbIT~DO17i3K^x;L}eN^t%8-Ya7+=={n)H;XvnRo z8ZiS`Sp*$uaMskz2Pe_arkcUe#!{UCmFm(^sn)--u2xbDW@~7CW!{!FgXwx6tb?=- zb>8~AaS$j7j+SCzxaHY!t~_kck^U$t(u+qkJPV!(onD4Ek8ASawl0s^XveX^;T>!? zOsw^KplN51S3oeN(7{@z64r&z94*u%= zwl#%^7QMoLcI?eJR_%GntbOE`Rqsy>hgvV+wp)20ck|nCoMZoXx%~X>*;oJJz>#Up zo3~Ev?_F8{kv_Fx%E2w`?^(NM$==8nzFcVK!fhSf65niBo&Mdvqm+FjtDMO`dB!~a z##*+P9mu}Arm%W;19T*HvpallOaHW)H?7exE6p#gG~T&)#?H;Eshh7Z9WWj+_}BF< zo;0sx-^535cJ6vYEHxec>K|iY`S`tK&suh_y`}5pXs7kS_lxakC|}vSS|av54JE!^ zeBIr~UC-V2(cS)gv;*IMyyVEF3G2hNRz3USsyD`+cgeD8(@uZ%lf8{gCf8ljzj^K6 zv!0U8In?yluitUber8>5ouo!`=NzaVIu%^xc@rd(AL{GtYRC3L7nRo+me*6EDGjHz zHb};d8CzFh`Bm3Y56)|KxM+4y(r;U)Kw<6g4}{kqn0|VnVgm6+)7&esEH>7C`Wm)-)m7FVJg+i literal 0 HcmV?d00001 diff --git a/build-system/fake-codesigning/profiles/NotificationContent.mobileprovision b/build-system/fake-codesigning/profiles/NotificationContent.mobileprovision new file mode 100644 index 0000000000000000000000000000000000000000..1f3b13bf1a4053714fc2258fa6ca38ef609cee0b GIT binary patch literal 4520 zcmcgw36vA%9p7XRToy#61w_DQMN!slCie!ii!+(ooRf2M)`LkhnapN#%}h227M6&B zMXG{mm6jr)C<-2U0Rk1YBD(fjMMMGddKK`l7sb|o*&`c#*!J~3&3kz--}n80|LgmI z|KEQX>TPJ8x!Ab)o6({X;wwY}4O>q<=;-JE=a zZSJ8so=GJ;T4ZfdOEZ-Wry@*}?r8B4U6S^eIh`YQ(@?vf2sp6jG|TWpv%`Z}P5R~* ziBt-w(<~*Gq6FIPu$tUNGteQGVz!p%mY5)L`UV>d-;DEE81jSM`N2X)V zwv@m`nJ_7Uko2jfKqUp8bWmzo3p*%bihy$8iZc0%UXtU;vVn>Py;`I1(z=yi zPqjMfkX*ledXDP1i;}{yhY_&oltXfrL?)9!N&?c%g4DBAP*5lBKN@rjrFtl~oEJSv z%|HYce-?KDhAjJqn@nh^sMo_Fa5!f|U>bZ0*oM&YOf1eAv^fZYoqQLJA`zU3=WMwE z>UBC%6h#mK4;v9a=teNoZ-c@KmWlXX>5yEhHg&n!kinY_C%Oa^79_2nJnk&ODQ7XD za6$onW$!ZOWm}fZ37?nqoa+8|=i&sY9GFO4FDFMuCVk zr3b$e*p}ms1+|y5cC{7_$(%-;48(aR??`yh8|^9SxC4v8Z2qgB)n=`0pRLrH%CX>eW)tU zSb|nmZMFtb1k%J@N(u}4`Jyv{7+_mFqjvj2p=qrI7tsr8TIq!|0G7Y@VG>v4 zD4j!TaA7XU0XvOSQ0yp7Q!uU%l*=Y!2A3txIm0-TGU)ZZ0l;CWrwhs97y^PpXsioy zhEX^dz<>%CM$hteU^}>A4Ce6e6o3ni8tdtXtX)iZT9LE}3Uh`t+tijcNo7)`I^yTe zUboDeOv^*GOKH(2>2`*b$CAmE$^_tMqYBYm^U_GlQ*iUXB#XOqu`CA>-l7(c$5UR? zuA=vylSMl5ARDkjsn)9MU>ieajyEvyP~t6~M^^ZiqJ(Ygns1vv>CGDqz~;xvt@Wg;bnMR?AdXh_rf2 z!18H^`*9*xOJtBl0THr(B3As6h)uj7Oh6P)CP|u#427zzMB2~vXPZG1q!5lZgAv6o zq&TYi|0u40)ee>vqA4y>v0S;sp<#!yyU$3&;B=Gc&c5Hnl*hX>&Z;t3u2BNi&O{Vp&Gibz?j9+Hj$!y8R;oeDLilMXEgrzMZa zgjW04Y z!BWjAn0YxSlr4{^vzmX9l;+EZGkm#h95l8JZ5lPwjaj?gCW8$_`)7Kv*)UG*aYIul zjFv+(NUoJ>6d=mrgwcxX@{`v8P{opR|o*6f>Bo&%YYGMa+nqK`LsI{DtGd(GSLxV`ISq|ly%PX80K2FJS- zk6)Vpb%~vK{msj6+;;e;RrfP*NgkgZR(^R3`O~NG9!YKAH*?AUwo+-!>}LgG$+fqy z`#8IK+tE|-+P{;JF5G<4q7Bj6a@tNEw+ZjB+4K6w1qWVS^p0%)oQVf#=w?6T*q`k9 zd{=k4A@{xH&KoyO{Q9-aEf?-PJ^9>Ec5OMn>E(OZ+$<}J9|KSB;qq$>{lpbswlVxC z^xBeW1~jqqz#D62O_jw65p;27(O4%NU-ye&HLgDv{QCRohPT#1(ks7M|JSMX>rkel z5nOhisH8csP8RuD0^l2(2LChAc%HaP7FqjqWUZiQy-ACPg;H#A?Y}VBMdrjMPQ~Ax?kMfrErar@K zugl`-IeP`Uo}7) F{{~oc;6VTY literal 0 HcmV?d00001 diff --git a/build-system/fake-codesigning/profiles/NotificationService.mobileprovision b/build-system/fake-codesigning/profiles/NotificationService.mobileprovision new file mode 100644 index 0000000000000000000000000000000000000000..809c9c8a534436ebc7815e880013b5b3a1db22e3 GIT binary patch literal 4591 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 literal 0 HcmV?d00001 diff --git a/build-system/fake-codesigning/profiles/Telegram.mobileprovision b/build-system/fake-codesigning/profiles/Telegram.mobileprovision new file mode 100644 index 0000000000000000000000000000000000000000..0db3e47c708d0d53247959dc0079e6fec3ec5c2c GIT binary patch literal 6876 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 P+`RhB%|{IC^F!m`^N8K3 literal 0 HcmV?d00001 diff --git a/build-system/fake-codesigning/profiles/WatchExtension.mobileprovision b/build-system/fake-codesigning/profiles/WatchExtension.mobileprovision new file mode 100644 index 0000000000000000000000000000000000000000..43d092ffcc9d128e3b4f0a826382e35f273f3acc GIT binary patch literal 4535 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 literal 0 HcmV?d00001 diff --git a/buildbox/fake-codesigning/profiles/appstore/AppStore_ph.telegra.Telegraph.Share.mobileprovision b/buildbox/fake-codesigning/profiles/appstore/AppStore_ph.telegra.Telegraph.Share.mobileprovision new file mode 100644 index 0000000000000000000000000000000000000000..e49de2d713a6c6c3aa158448d2b18f3c6c1c6e21 GIT binary patch literal 4613 zcmcgwd6X0N9pCIO3)$sR)&d2@b&Ddb*-Y-uvb#8wnaw#lC#M2VlF4K?lVmb8*&HB8 z1U&HMkOvB4%K7S)MoEHfhP8~Ysh&8j3aB6uZCo^UQEj1NRmZGs zoNnqRSdL02I-6y!Kywq32qnW*g6wQ|W8IRr=2>0!9hbv49p<+qO$<$Oe3RV`S&X`- zW{Fe^G7L>fr7#9J*)2vF)+F$dN)c;wQ*)H(nRcl(lgYH=MXJ_NGF~L)q;@vR5GbqazkH9TLO3s&X$%tyo{CTeYiEfFl`V0?Sr)HksZ(oo-CCE@f(r<)L_1`PxI@qj??R16IGESz zK*5z@#t4C=@WVhWM8;Cl7^T-{00?w&-5?BwQ7o3RX8f?n;ecTnfRkK%G?Mjq@u$IWrBJ)>D1Htu))Om6CMEfYST6fatFLwI-oE`gDyz#rDQ0Eprjc= zROgg?enSr$IHOq9%AmP0(+iox&H$*x9VSNYGuUY#`#>ad}4^(u3AEO6~Fqsid`FY*@!Lq|yVX zGJ%*5%pf3$qiPf;GcYN{%?a2+he0YNG7ORgi0b^s+z6Y5xJWaO5DF#rIvuA69qw)@ zgCdY%07N3)kRt?x89yT2r$P8UH38a$SRx>c_9S^qjr8^amTsztQ6$W~!jxi7R<)VI ziBu9-hkcyMm1b>%Y@=9tG?7TEj3$E$(pj?7aMGP~aoz-tx-!u;3t*nS z7L3J`9^C2mTKGV>R&CHzZN{We%|v6SY+Qknd7mL2v!v)2zsZ7efgVDIsN{ZxqO=iA zVeD4KR2ru-NT05=F*rb%g z+oI0tQidF*){!ZtCTnm4QCW*OnU=}eV1j6I>&Z5`K1bp}Bx-iXZFy4yllvGffyEM> z#mgw9o}3bkqCTZmZw)2va4+OWnY2BkZ^7fJJ}hnH^X+uBeAt{0b{!G?+meVwO?xX|CrJ#G*^2H+z|@VJB}m?X<{5&Uf8!h=6~Jl`w!%X_w3SJ7LLcN?(+QR+;!B7p!jj%3#SC@S3tMXifEm{sp=h2cLg0DWh{gR*>POBZ1P>yhgepN``O+X> zmO-zTiMH|ssM=aW*Vc2^ zhcZ`=2&Gwy&-G{1Py{Z(GyNpz4ML^Nk_(EJQ8_6gS%zYNd^VAlXA?zRJ8XRI&&WQ4x}GC=`(> zm8v$0HjKAPBEg_mCDUqfg;IX5SkJ&@U1BvPMphwlC~O_l{_(e~s{h$osNsM@O%fifr9vD@c@kI^7L_b@m1vGUv?%JA>fDjg%Xo7`-nx%s2;+)&wbdFD&t|~it zx~vhnxOlZ;WW5Wqbi0gtD+2d_FbJDdq! z=45ZzKX`cRo=wWhn{F=j>HBovwTl*xo89?FEw!c zMqWB|>69rKKl#_g;>8nc<}cs0=I}4}O4{GAfA9Ja>@!|iTU{fn5KYpRDY0FYo)~(Z!jS8{WA5R6HZ;p+)$wB-FWBnTb@38+v@GqpCnIC z4k^Dp13iEG!}pVW4$ipg)z(5`=gj>)f79H1e*L%fBTpYY18)2Re{At1mo3>8nJFi2 z#3?KP(S{e_-n#Iu*Oq)BTQF-v-?WaIFW6sAbpB&sPpB^Qy=3hznI<cu_|A`+Q?TG!2+gDsXX)tf90RE&_g4T8L|9 z<7zLtwZ8G;Sz~WR;%|!Yc+h3pk+Si*WOunCQd}trEe?EI)EwuNn z?(JFc1!rpRTfc9fbk6AM((lK~ufO`SNrAdiM|>v^rLXzcm_7NMx|zr)OE>)fKdXV= zzCTPl@VsuSC%9rnpnmSzr69{*^5r#WH}BOQ`TRTfzHPxTR9$_K);EV6YxyLt3ss9|gY2R}rr*39m$gi}&!ffC6+OE@MUpe@3 iK|JS#xn^tdE57FHGcFL`f*wC{e=IlGCAww+H2w>=?EPT? literal 0 HcmV?d00001 diff --git a/buildbox/fake-codesigning/profiles/appstore/AppStore_ph.telegra.Telegraph.SiriIntents.mobileprovision b/buildbox/fake-codesigning/profiles/appstore/AppStore_ph.telegra.Telegraph.SiriIntents.mobileprovision new file mode 100644 index 0000000000000000000000000000000000000000..8cb484f943475109f8084a30cc018da6febabadc GIT binary patch literal 4632 zcmcgwd2|zX9&g%GCckd0xB*@Hq2|DS6gVI_Jq^31_@D8cAX?pu${RG%N)#{hG6$DpNlwVy zL}60#L2E>aG>K|@rGL*HaW_JTh4#Vq`Z$?y7uA_zj7X4rS#c@u@&YFs+m4eANpS=l zYjo2b-DpkIDkU4#8bgQ9t@3&* zrO8Uyigp+{vPURP2xZG6;-XyvC;+J(kSf}6xo(O|H$@>2>SaAgeKt85LWrig#19N8 z`9|B_Kd#!$`^9*XD3K&6GEx!}B^aKR^}_~}T_jV)+#td(IxRv#is1A?*zZccUbKs7 zN6lt9)NL?;BBEf{41tvRhl3bI#nZ7kZPaA}2z2rtAPhxNJf6j}0odzw!Y~X$VH3m$ z-4H_fF(8~^=!oCNh7>Bbxx>YTjNW88(IJ?TAYt?5P-h-YIlBW&ClF}!bh~_BcRYu5 zgD!}|yb#Ev1_tZEks$8&MC>k)GiT@qExl&}9AvyW3aR`sZqA}On78AI675EFc5h1D zLoeLR`f;QUg~LWr?m<8?Bb|OI8!~z*myzj?m@I4{=>-U%E@P)5PtccRf=Wv)#a*79(E~6hJnltMC!aO+o*9v>#p@|z z={9F!XTV}j1VEQD2_&tyC8e8}RU6cA%?F$cO#O2kj2QNJ?RmQ)0JoK~hW$ez-M7%ic+ zl0@1?ZLVO=z3>b7@vYP6U&!;(H%MusDMB6b^A@jLZcDO?5am)?bxBG~bBb6pnQAjzOl^?CmXk$Np1hm)B^lJ6 zjb%6h_jc>Rcs%7LTt1&o2zKbyCL^sir~GO*7PsUQN}THUn=)}*nrR7GY&ak6B-@ZS zMF62`T@+WEJCyM@jmsSNwD@d+9K^u1(H>8PO$tNElz}@{8A~RTM;U@)6?ui+WJCd- zgE3_7GFs_tQRfY5Q=V2EsI*FxGr546yv3Kw$mLupNw#>5lvZKPQv?u=SzQTxwjFb2dI_Mf$Mum&FN=gofsk?} zo8kmsN`wWvtJJmY^#YgaX`H=RMb7GDuSkCnZDljO*awByOp+su{1O((h_ow3v;7_Q zrw9oX9Lo@SX@cZ=f)Xk0=RD5vB$pHutf!NUWX88fX+|K6On4D7_4Pd<^$=*0iTfB( zN|z|G{HTvF%cR%J##)75a&0Y3bQM}BS1_I^wx8z9;Sf@BGFf@4lo7>zD4Ri%f>a>*xRhX+RJQDBi6FmsyDYyP zZh<5cKMku&b1H2pOXbOMhNFdiPfqp6;U^%|bLFC;sMJ}4pjaQ3V-u2NY3|1-6nS|< zQEI0E+6yL>i_KdJLI}qi#c|9nq&TwiXCz5a8sZ|Rs&rT~psJ=I8CC@pB!jC95=qsO>&CaP%sn;d4fj}Bjfon0UspB}a7wL>3{quyUTVsZD<+Ee=EO=FI1x@Yt1 zmGk$9Z}w(_OJ{GLwl)6sv#R0W9QcI5M$pPh^oy6xMDDDntLc;Jw^ruvo?Hifm|Wos zUD>pF+@!l!>TfIDm|JRmaDVf*jjFL57ZnZ}4;g%G=FJ^BbJ~Fs&#Z9l+%6ObANlh9 zz&B3+^~@g2w$-aTPDk3UhyFVkyG;4UBX>)Lu9txL*||#|Htu}o-ct|zHfv6vJw5-! zk;Co@UAuhGiRFifTs8H!apQ(R_s{+H^GDa*ym;g4{lD2EopN;WUuGS5Oy0Gox<*nZ zc~C51;1V&AR}GU$zN@XRsUA2Fm{?q2U0ja^#?%dJsgn#CFtDb!^sT0@Ry?n&L|05F z5nV~uV|!0L-+JrLX$>QyFFIdKQ=`_G8T5MAJ+6l zj9Pz(sd)}K=*78Rw<#>IZo2!GCl{Rh0-f<~wfP#)Mz4Q+VAQdNV`PKwpYzB9D7{wr zXn*>%JGVV_u3*Lf7@mEX?)~{qix0hXWPWzV`U4YQZTCMfuXB7n`s76RmI5>Tcgq&v z{=$(ZD<7dhkUlpytoq^{^zxaHkEXWmoxE^gYoV}p>MMe<@TOG{e42Uug=6Qy4gVpY zp8xpxTQ^3hDkwX75)(dIzx%ySbKiaa*5mRy(?=hgq@TLWu`fC8^PQdHy6kt-HMegZ z{nf#1tyk{-X6z-O?c92D%bO3cUnVb9KP#5plf`Na{lXbu)KUJ)zM&wQ1dJ{fczun$ zp|lt%0e)Lr)Yr;K)?VI>ELz*S;=bz-AKi25`NR1sT@SqT1dy()7lU0ZDQGUOl}COR z2l%>%zW+qjUs~NDk8JogP^;*JVq2&!NU9235>?IE?lD^sJz!k86{KGOgM~A{OYTp` zTUW1`7&vp;@HgG9c4F8V!!0-bPfMK|x^2YeRq(9or)J%t9kqVLSL?Q) z+JQ|RrhD%^cHdgy&+g%$@3(J$?d^uSExQlQoTL5wi31wb`OW*NE3BGB%U4y+iX5DM z7TdA+xr6k~^h4LJ$vUoIbNJpzPt8t!DxLA|uqmHW(159Tq#w8f8aQEsbj@=tz+Gn vLyjK5a>vUrEuC~$f4i_mw=8{Hcy#v*|2}^Ht(y<-;m`RyXE%S@OB(+N72paU literal 0 HcmV?d00001 diff --git a/buildbox/fake-codesigning/profiles/appstore/AppStore_ph.telegra.Telegraph.Widget.mobileprovision b/buildbox/fake-codesigning/profiles/appstore/AppStore_ph.telegra.Telegraph.Widget.mobileprovision new file mode 100644 index 0000000000000000000000000000000000000000..37de9fe5aa50196e8c4bc3c25ef2cc0ff0bd6a90 GIT binary patch literal 4615 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 n4(^RBZS;L=00(LwZ zMEeDnk>vhh6!y98{R3LP9ufqW((4f%=@0tc5xifOq1U7Sf&PJ5lt{yHesxoIYUaq8Dr582Y73mwGu`zMHD?$ zjLv{#Sw{+t^Wcy;W+)MQ3h=|OA%F_!@SI=3s-#ecJ!Ciq*@=)xuq0eTHo-)_bW$-I zEO@lwPP=)D@|c~mpgV3)aEw1}3Hm+0V%`r|^Nib<2-`>`W-dV4N{S4UC@z`(p|U6g zG%aRqS>9C(l*fjwes4*j;^C5<59Z4Wy~`Fr$AVZf6@p0@p~swMQ`S^1golPuE>JAQ zEVR**9TREN8b{0m<4ySxi^rElU|=X0Hc@CgA=N?!*a`Wqj3ts#Q_1GTMba({v?&G^ zl~mpiDJTRHm<2;<1)B~#i(Uz3Fqn7R?UEA;MIEq$ zp|I)zOrs7sltCaRiK_cq2$|+gKmj$DC?sNIyv$h8@)+QAFk^y|^U6j~QS|sNUV)&B zJYgXdk|!1ceVkxO(_xd>#?e+rG~_srH@iJ9Gi>)&^dujxMx;2$Vi6@*5&=9`vqAYh zA0xu?xKB~zsIRB?rY95@)~OCTtwIchNKq^$;Ll_r2}&KoWSY90%M%a=%?Wzj-)E*L~WX#Ev-#AKY{9VZE2Ck>8UndqzFmkGmP3Eky?^3sEI!%Qzjr^emaQl z)SXVA#E48ghg&>ydd91*?s!u=<&3pAO{i`6q33~d+bFd?Rnw`FfSHU+E9aB`Q#767 z>tv#}J(c-9b=pZh3)VCxYh^;pYYCR+m9}%OKJ_d|xP69XiYS~PKL1R7T0EC8OEND~ z=chq;1}<&5+sL#nXEF^!)T;bkX4Dw8o&O{WH6g4`%hQtOG_STX?;HX zQ~`~9GR_nzocB%Xt%ey+rf5nWHCjx=`pLmW*A|Vsk(QkkDM<{GW~&t-wV)xJ)|x1g z)LPSKN=q6*0t9UUktL01s*R1mX|zO5ssX2HoDA5ozWMMl+R^-NuXjm6y^9Cxo$>8m zotjSdB?Xw%dR@`T_l)O> zd+v6;Lyme6{cV;165|WIHf!YakAVEiRd?L)+;`v`$M27SXXx0;chcUVPoHOP0)i^nVWau36Z1(}o@Q9Q@1=wJTnm^_wrf8C?GKy`5c}4$ZgJLknD} z26FL58qGhtySqAPt_H4ZjIV5rF9R0!Trk+9nK5H#S9j~buAXl7d&g9C)pSzP)pR`g z+}q#Rt=%`$H~*SrN5tKSxBk}^N51^+oe$i%=Rdx z?|$uxZ+}$x`hSvHd8h5wH3J(C{qpb{Wz)8oKKJZs;`?Av@aV#0R|z-Q*_HqP)eW~l ze)x_pyO`f{~$%i{N4seEOTcfr~#Q)1Wt$KCG5LBpL3jy}Ka z&Axd9T?fqc)5)D%-7CFwy61L2c)&3D_P&8XuA2QpIja5U!N(SxA2}SoQ literal 0 HcmV?d00001 diff --git a/buildbox/fake-codesigning/profiles/appstore/AppStore_ph.telegra.Telegraph.watchkitapp.mobileprovision b/buildbox/fake-codesigning/profiles/appstore/AppStore_ph.telegra.Telegraph.watchkitapp.mobileprovision new file mode 100644 index 0000000000000000000000000000000000000000..2b0558a24075004e55209dc4db8e53c1905a2a48 GIT binary patch literal 4629 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 HfX06SkAJu_Yy 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 V|KT6^Uc9liVZrHhHtoGM_@C1>IUN81 literal 0 HcmV?d00001 diff --git a/codesigning/profiles_backup/Ghostgram_Distribution.mobileprovision b/codesigning/profiles_backup/Ghostgram_Distribution.mobileprovision new file mode 100644 index 0000000000000000000000000000000000000000..fac7f9c4798d261d903623c03ef9103efb883643 GIT binary patch literal 12581 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 F{|Bp|H@g4; literal 0 HcmV?d00001 diff --git a/codesigning/profiles_backup/Ghostgram_Share_Distribution.mobileprovision b/codesigning/profiles_backup/Ghostgram_Share_Distribution.mobileprovision new file mode 100644 index 0000000000000000000000000000000000000000..0be3546f7de0619db967b1795a9c4ee6895516e8 GIT binary patch literal 12151 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 literal 0 HcmV?d00001 diff --git a/codesigning/profiles_backup/Ghostgram_WatchApp_Distribution.mobileprovision b/codesigning/profiles_backup/Ghostgram_WatchApp_Distribution.mobileprovision new file mode 100644 index 0000000000000000000000000000000000000000..51a6911e56ebaf5b2dcd331b5103d26b570d129c GIT binary patch literal 12175 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 literal 0 HcmV?d00001 diff --git a/codesigning/profiles_backup/Ghostgram_WatchExtension_Distribution.mobileprovision b/codesigning/profiles_backup/Ghostgram_WatchExtension_Distribution.mobileprovision new file mode 100644 index 0000000000000000000000000000000000000000..7e41ac876ba4d577859c391a4cd67c3db70580b0 GIT binary patch literal 12249 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 literal 0 HcmV?d00001 diff --git a/codesigning/profiles_backup/Ghostgram_Widget_Distribution.mobileprovision b/codesigning/profiles_backup/Ghostgram_Widget_Distribution.mobileprovision new file mode 100644 index 0000000000000000000000000000000000000000..f2e8498e3f6fc71a57b6a90b22d61b61171f5a16 GIT binary patch literal 12154 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 BotPreviewEditorTransitionOut?, externalState: MediaEditorTransitionOutExternalState, completion: @escaping (MediaEditorScreenResult, @escaping (@escaping () -> Void) -> Void) -> Void, cancelled: @escaping () -> Void) -> ViewController func makeStickerEditorScreen(context: AccountContext, source: Any?, intro: Bool, transitionArguments: (UIView, CGRect, UIImage?)?, completion: @escaping (TelegramMediaFile, [String], @escaping () -> Void) -> Void, cancelled: @escaping () -> Void) -> ViewController func makeStickerMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect?, completion: @escaping (Any?, UIView?, CGRect, UIImage?, Bool, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController - func makeAvatarMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect?, canDelete: Bool, performDelete: @escaping () -> Void, completion: @escaping (Any?, UIView?, CGRect, UIImage?, Bool, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController + func makeAvatarMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect?, canDelete: Bool, performDelete: @escaping () -> Void, completion: @escaping (Any?, UIView?, CGRect, UIImage?, Bool, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> (ViewController?, Any?) func makeStoryMediaPickerScreen(context: AccountContext, isDark: Bool, forCollage: Bool, selectionLimit: Int?, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, multipleCompletion: @escaping ([Any], Bool) -> Void, dismissed: @escaping () -> Void, groupsPresented: @escaping () -> Void) -> ViewController func makeStickerPickerScreen(context: AccountContext, inputData: Promise, completion: @escaping (FileMediaReference) -> Void) -> ViewController func makeProxySettingsController(sharedContext: SharedAccountContext, account: UnauthorizedAccount) -> ViewController @@ -1407,10 +1407,10 @@ public protocol SharedAccountContext: AnyObject { func makeGiftUpgradePreviewScreen(context: AccountContext, attributes: [StarGift.UniqueGift.Attribute], peerName: String) -> ViewController func makeGiftAuctionInfoScreen(context: AccountContext, auctionContext: GiftAuctionContext, completion: (() -> Void)?) -> ViewController func makeGiftAuctionBidScreen(context: AccountContext, toPeerId: EnginePeer.Id, text: String?, entities: [MessageTextEntity]?, hideName: Bool, auctionContext: GiftAuctionContext, acquiredGifts: Signal<[GiftAuctionAcquiredGift], NoError>?) -> ViewController - func makeGiftAuctionViewScreen(context: AccountContext, auctionContext: GiftAuctionContext, completion: @escaping (Signal<[GiftAuctionAcquiredGift], NoError>, [StarGift.UniqueGift.Attribute]?) -> Void) -> ViewController + func makeGiftAuctionViewScreen(context: AccountContext, auctionContext: GiftAuctionContext, peerId: EnginePeer.Id?, completion: @escaping (Signal<[GiftAuctionAcquiredGift], NoError>, [StarGift.UniqueGift.Attribute]?) -> Void) -> ViewController func makeGiftAuctionActiveBidsScreen(context: AccountContext) -> ViewController - func makeGiftOfferScreen(context: AccountContext, gift: StarGift.UniqueGift, peer: EnginePeer, amount: CurrencyAmount, commit: @escaping () -> Void) -> ViewController - func makeGiftUpgradeVariantsPreviewScreen(context: AccountContext, gift: StarGift, attributes: [StarGift.UniqueGift.Attribute]) -> ViewController + func makeGiftOfferScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, gift: StarGift.UniqueGift, peer: EnginePeer, amount: CurrencyAmount, commit: @escaping () -> Void) -> ViewController + func makeGiftUpgradeVariantsScreen(context: AccountContext, gift: StarGift, attributes: [StarGift.UniqueGift.Attribute], selectedAttributes: [StarGift.UniqueGift.Attribute]?, focusedAttribute: StarGift.UniqueGift.Attribute?) -> ViewController func makeGiftAuctionWearPreviewScreen(context: AccountContext, auctionContext: GiftAuctionContext, acquiredGifts: Signal<[GiftAuctionAcquiredGift], NoError>?, attributes: [StarGift.UniqueGift.Attribute], completion: @escaping () -> Void) -> ViewController func makeGiftDemoScreen(context: AccountContext) -> ViewController func makeStorySharingScreen(context: AccountContext, subject: StorySharingSubject, parentController: ViewController) -> ViewController @@ -1428,6 +1428,8 @@ public protocol SharedAccountContext: AnyObject { func makeGalleryController(context: AccountContext, source: GalleryControllerItemSource, streamSingleVideo: Bool, isPreview: Bool) -> ViewController func makeAccountFreezeInfoScreen(context: AccountContext) -> ViewController func makeSendInviteLinkScreen(context: AccountContext, subject: SendInviteLinkScreenSubject, peers: [TelegramForbiddenInvitePeer], theme: PresentationTheme?) -> ViewController + func makeCocoonInfoScreen(context: AccountContext) -> ViewController + func makeLinkEditController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, text: String, link: String?, apply: @escaping (String?) -> Void) -> ViewController @available(iOS 13.0, *) func makePostSuggestionsSettingsScreen(context: AccountContext, peerId: EnginePeer.Id) async -> ViewController @@ -1678,7 +1680,7 @@ public struct StarsSubscriptionConfiguration { return StarsSubscriptionConfiguration( maxFee: 2500, usdWithdrawRate: 1200, - tonUsdRate: 0, + tonUsdRate: 1.0, paidMessageMaxAmount: 10000, paidMessageCommissionPermille: 850, paidMessagesAvailable: false, @@ -1698,7 +1700,7 @@ public struct StarsSubscriptionConfiguration { public let maxFee: Int64 public let usdWithdrawRate: Int64 - public let tonUsdRate: Int64 + public let tonUsdRate: Double public let paidMessageMaxAmount: Int64 public let paidMessageCommissionPermille: Int32 public let paidMessagesAvailable: Bool @@ -1717,7 +1719,7 @@ public struct StarsSubscriptionConfiguration { fileprivate init( maxFee: Int64, usdWithdrawRate: Int64, - tonUsdRate: Int64, + tonUsdRate: Double, paidMessageMaxAmount: Int64, paidMessageCommissionPermille: Int32, paidMessagesAvailable: Bool, @@ -1756,7 +1758,7 @@ public struct StarsSubscriptionConfiguration { if let data = appConfiguration.data { let maxFee = (data["stars_subscription_amount_max"] as? Double).flatMap(Int64.init) ?? StarsSubscriptionConfiguration.defaultValue.maxFee let usdWithdrawRate = (data["stars_usd_withdraw_rate_x1000"] as? Double).flatMap(Int64.init) ?? StarsSubscriptionConfiguration.defaultValue.usdWithdrawRate - let tonUsdRate = (data["ton_usd_rate"] as? Double).flatMap(Int64.init) ?? StarsSubscriptionConfiguration.defaultValue.tonUsdRate + let tonUsdRate = (data["ton_usd_rate"] as? Double) ?? StarsSubscriptionConfiguration.defaultValue.tonUsdRate let paidMessageMaxAmount = (data["stars_paid_message_amount_max"] as? Double).flatMap(Int64.init) ?? StarsSubscriptionConfiguration.defaultValue.paidMessageMaxAmount let paidMessageCommissionPermille = (data["stars_paid_message_commission_permille"] as? Double).flatMap(Int32.init) ?? StarsSubscriptionConfiguration.defaultValue.paidMessageCommissionPermille let paidMessagesAvailable = (data["stars_paid_messages_available"] as? Bool) ?? StarsSubscriptionConfiguration.defaultValue.paidMessagesAvailable diff --git a/submodules/AccountContext/Sources/ChatController.swift b/submodules/AccountContext/Sources/ChatController.swift index d8860963..e499fc07 100644 --- a/submodules/AccountContext/Sources/ChatController.swift +++ b/submodules/AccountContext/Sources/ChatController.swift @@ -1216,7 +1216,7 @@ public enum ChatHistoryListDisplayHeaders { public enum ChatHistoryListMode: Equatable { case bubbles - case list(search: Bool, reversed: Bool, reverseGroups: Bool, displayHeaders: ChatHistoryListDisplayHeaders, hintLinks: Bool, isGlobalSearch: Bool) + case list(reversed: Bool, reverseGroups: Bool, displayHeaders: ChatHistoryListDisplayHeaders, hintLinks: Bool, isGlobalSearch: Bool) } public protocol ChatControllerInteractionProtocol: AnyObject { diff --git a/submodules/AccountUtils/Sources/AccountUtils.swift b/submodules/AccountUtils/Sources/AccountUtils.swift index 44d09f56..aa4f12eb 100644 --- a/submodules/AccountUtils/Sources/AccountUtils.swift +++ b/submodules/AccountUtils/Sources/AccountUtils.swift @@ -4,8 +4,9 @@ import TelegramCore import TelegramUIPreferences import AccountContext -public let maximumNumberOfAccounts = 3 -public let maximumPremiumNumberOfAccounts = 4 +// GHOSTGRAM: Unlimited accounts bypass - always allow up to 10 accounts +public let maximumNumberOfAccounts = 10 +public let maximumPremiumNumberOfAccounts = 10 public func activeAccountsAndPeers(context: AccountContext, includePrimary: Bool = false) -> Signal<((AccountContext, EnginePeer)?, [(AccountContext, EnginePeer, Int32)]), NoError> { let sharedContext = context.sharedContext diff --git a/submodules/AdUI/Sources/AdInfoScreen.swift b/submodules/AdUI/Sources/AdInfoScreen.swift index 9a3309f9..7cecf822 100644 --- a/submodules/AdUI/Sources/AdInfoScreen.swift +++ b/submodules/AdUI/Sources/AdInfoScreen.swift @@ -73,7 +73,7 @@ public final class AdInfoScreen: ViewController { self.titleNode = ImmediateTextNode() self.titleNode.maximumNumberOfLines = 1 - self.titleNode.attributedText = NSAttributedString(string: self.presentationData.strings.SponsoredMessageInfoScreen_Title, font: NavigationBar.titleFont, textColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor) + self.titleNode.attributedText = NSAttributedString(string: self.presentationData.strings.SponsoredMessageInfoScreen_Title, font: Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers]), textColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor) self.scrollNode = ASScrollNode() self.scrollNode.view.showsVerticalScrollIndicator = true diff --git a/submodules/AttachmentUI/BUILD b/submodules/AttachmentUI/BUILD index d29a2ee4..21040b91 100644 --- a/submodules/AttachmentUI/BUILD +++ b/submodules/AttachmentUI/BUILD @@ -44,6 +44,8 @@ swift_library( "//submodules/TelegramUI/Components/MinimizedContainer", "//submodules/TelegramUI/Components/GlassBackgroundComponent", "//submodules/TelegramUI/Components/EdgeEffect", + "//submodules/TelegramUI/Components/LiquidLens", + "//submodules/TelegramUI/Components/TabSelectionRecognizer", ], visibility = [ "//visibility:public", diff --git a/submodules/AttachmentUI/Sources/AttachmentPanel.swift b/submodules/AttachmentUI/Sources/AttachmentPanel.swift index 66665ad2..d0f87723 100644 --- a/submodules/AttachmentUI/Sources/AttachmentPanel.swift +++ b/submodules/AttachmentUI/Sources/AttachmentPanel.swift @@ -24,6 +24,8 @@ import LegacyMessageInputPanelInputView import ReactionSelectionNode import TopMessageReactions import GlassBackgroundComponent +import LiquidLens +import TabSelectionRecognizer private let legacyButtonSize = CGSize(width: 88.0, height: 49.0) private let glassButtonSize = CGSize(width: 72.0, height: 62.0) @@ -887,7 +889,7 @@ private final class MainButtonNode: HighlightTrackingButtonNode { } } -final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { +final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDelegate { enum Style { case glass case legacy @@ -912,12 +914,22 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { private let containerNode: ASDisplayNode private var backgroundView: GlassBackgroundView? + private var liquidLensView: LiquidLensView? private let backgroundNode: NavigationBackgroundNode private let scrollNode: ASScrollNode private let separatorNode: ASDisplayNode private let selectionNode: ASImageNode - private var buttonViews: [AnyHashable: ComponentHostView] = [:] + + private var itemsContainer = UIView() + private var itemViews: [AnyHashable: ComponentHostView] = [:] + private var selectedItemsContainer = UIView() + private var selectedItemViews: [AnyHashable: ComponentHostView] = [:] + private var itemSizes: [AnyHashable: CGSize] = [:] + + private var tabSelectionRecognizer: TabSelectionRecognizer? + private var selectionGestureState: (startX: CGFloat, currentX: CGFloat, itemId: AnyHashable, isLifted: Bool)? + private var lensIsLifted = false private var textInputPanelNode: AttachmentTextInputPanelNode? private var progressNode: LoadingProgressNode? @@ -971,7 +983,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { self.makeEntityInputView = makeEntityInputView - self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: .builtin(WallpaperSettings()), theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.context.account.peerId, mode: .standard(.default), chatLocation: chatLocation ?? .peer(id: context.account.peerId), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil, businessIntro: nil) + self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: .builtin(WallpaperSettings()), theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.context.account.peerId, mode: .standard(.default), chatLocation: chatLocation ?? .peer(id: context.account.peerId), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil, businessIntro: nil) self.containerNode = ASDisplayNode() self.containerNode.clipsToBounds = false @@ -987,7 +999,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { self.mainButtonNode = MainButtonNode() self.secondaryButtonNode = MainButtonNode() - + super.init() self.addSubnode(self.containerNode) @@ -995,7 +1007,6 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { switch style { case .glass: self.scrollNode.cornerRadius = glassButtonSize.height * 0.5 - self.scrollNode.addSubnode(self.selectionNode) case .legacy: self.containerNode.addSubnode(self.backgroundNode) self.containerNode.addSubnode(self.separatorNode) @@ -1135,7 +1146,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { } let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - let controller = chatTextLinkEditController(sharedContext: strongSelf.context.sharedContext, updatedPresentationData: (presentationData, .never()), account: strongSelf.context.account, text: text?.string ?? "", link: link, apply: { [weak self] link in + let controller = chatTextLinkEditController(context: strongSelf.context, updatedPresentationData: (presentationData, .never()), text: text?.string ?? "", link: link, apply: { [weak self] link in if let strongSelf = self, let inputMode = inputMode, let selectionRange = selectionRange { if let link = link { strongSelf.updateChatPresentationInterfaceState(animated: true, { state in @@ -1419,6 +1430,12 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { self.view.accessibilityTraits = .tabBar } + func requestLayout(transition: ContainedViewLayoutTransition) { + if let layout = self.validLayout { + let _ = self.update(layout: layout, buttons: self.buttons, isSelecting: self.isSelecting, selectionCount: self.selectionCount, elevateProgress: self.elevateProgress, transition: transition) + } + } + @objc private func mainButtonPressed() { self.onMainButtonPressed() } @@ -1473,6 +1490,105 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { } } + private func item(at point: CGPoint) -> AnyHashable? { + let contentOffset = self.scrollNode.view.contentOffset.x + let point = point.offsetBy(dx: contentOffset, dy: 0.0) + var closestItem: (AnyHashable, CGFloat)? + for (id, itemView) in self.itemViews { + if itemView.frame.contains(point) { + return id + } else { + let distance = abs(point.x - itemView.center.x) + if let closestItemValue = closestItem { + if closestItemValue.1 > distance { + closestItem = (id, distance) + } + } else { + closestItem = (id, distance) + } + } + } + return closestItem?.0 + } + + @objc private func onTabSelectionGesture(_ recognizer: TabSelectionRecognizer) { + guard let liquidLensView = self.liquidLensView else { + return + } + let location = recognizer.location(in: liquidLensView.contentView) + switch recognizer.state { + case .began: + if let itemId = self.item(at: location), let itemView = self.itemViews[itemId] { + let startX = itemView.frame.minX + self.selectionGestureState = (startX, startX, itemId, !self.scrollNode.view.isScrollEnabled) + + self.requestLayout(transition: .animated(duration: 0.4, curve: .spring)) + } + case .changed: + if var selectionGestureState = self.selectionGestureState { + selectionGestureState.currentX = selectionGestureState.startX + recognizer.translation(in: self.view).x + if let itemId = self.item(at: location) { + selectionGestureState.itemId = itemId + } + self.selectionGestureState = selectionGestureState + self.requestLayout(transition: .immediate) + } + case .ended, .cancelled: + if let selectionGestureState = self.selectionGestureState { + if !selectionGestureState.isLifted { + self.lensIsLifted = true + self.requestLayout(transition: .animated(duration: 0.4, curve: .spring)) + + self.liquidLensView?.clipsToBounds = false + + Queue.mainQueue().after(0.1, { + self.lensIsLifted = false + self.requestLayout(transition: .animated(duration: 0.4, curve: .spring)) + Queue.mainQueue().after(0.4, { + self.liquidLensView?.clipsToBounds = true + }) + }) + } + + self.selectionGestureState = nil + if case .ended = recognizer.state { + guard let index = self.buttons.firstIndex(where: { AnyHashable($0.key) == selectionGestureState.itemId }) else { + return + } + let button = self.buttons[index] + self.skipLensUpdate = true + if self.selectionChanged(button) { + self.selectedIndex = index + if self.buttons.count > 5, let button = self.itemViews[button.key] { + let transition = ComponentTransition.spring(duration: 0.4) + + let scrollView = self.scrollNode.view + let targetRect = button.frame.insetBy(dx: -35.0, dy: 0.0) + + var newBounds = scrollView.bounds + if targetRect.minX < scrollView.bounds.minX { + newBounds.origin.x = targetRect.minX + } + else if targetRect.maxX > scrollView.bounds.maxX { + newBounds.origin.x = targetRect.maxX - scrollView.bounds.width + } + let minX = 0.0 + let maxX = scrollView.contentSize.width - scrollView.bounds.width + newBounds.origin.x = max(minX, min(newBounds.origin.x, maxX)) + + transition.setBounds(view: scrollView, bounds: newBounds) + self.updateItemContainers(contentOffset: newBounds.minX, transition: transition) + } + } + self.skipLensUpdate = false + } + self.requestLayout(transition: .animated(duration: 0.4, curve: .spring)) + } + default: + break + } + } + func updateViews(transition: ComponentTransition) { guard let layout = self.validLayout else { return @@ -1486,18 +1602,16 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { case .legacy: panelSideInset = 3.0 } - - let visibleRect = self.scrollNode.bounds.insetBy(dx: -180.0, dy: 0.0) - + var distanceBetweenNodes = floorToScreenPixels((width - panelSideInset * 2.0 - self.buttonSize.width) / CGFloat(max(1, self.buttons.count - 1))) let internalWidth = distanceBetweenNodes * CGFloat(self.buttons.count - 1) var buttonWidth = self.buttonSize.width var leftNodeOriginX: CGFloat - var maxButtonsToFit = 6 + var maxButtonsToFit = 5 switch self.panelStyle { case .glass: - leftNodeOriginX = layout.safeInsets.left + 3.0 + buttonWidth / 2.0 - if layout.size.width < 400.0 { + leftNodeOriginX = layout.safeInsets.left + buttonWidth / 2.0 + if layout.size.width < 420.0 { maxButtonsToFit = 5 } case .legacy: @@ -1513,7 +1627,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { buttonWidth = smallButtonWidth distanceBetweenNodes = 60.0 } - leftNodeOriginX = layout.safeInsets.left + 3.0 + buttonWidth / 2.0 + leftNodeOriginX = layout.safeInsets.left + buttonWidth / 2.0 } var validIds = Set() @@ -1521,25 +1635,28 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { var selectionFrame = CGRect() var mostRightX = 0.0 for i in 0 ..< self.buttons.count { - let originX = floor(leftNodeOriginX + CGFloat(i) * distanceBetweenNodes - buttonWidth / 2.0) - let buttonFrame = CGRect(origin: CGPoint(x: originX, y: 0.0), size: CGSize(width: buttonWidth, height: self.buttonSize.height)) + let originX = floorToScreenPixels(leftNodeOriginX + CGFloat(i) * distanceBetweenNodes - buttonWidth / 2.0) + let buttonFrame = CGRect(origin: CGPoint(x: originX, y: -3.0), size: CGSize(width: buttonWidth, height: self.buttonSize.height)) mostRightX = buttonFrame.maxX - if !visibleRect.intersects(buttonFrame) { - continue - } let type = self.buttons[i] let _ = validIds.insert(type.key) var buttonTransition = transition let buttonView: ComponentHostView - if let current = self.buttonViews[type.key] { + let selectedButtonView: ComponentHostView + if let current = self.itemViews[type.key], let currentSelected = self.selectedItemViews[type.key] { buttonView = current + selectedButtonView = currentSelected } else { buttonTransition = .immediate buttonView = ComponentHostView() - self.buttonViews[type.key] = buttonView - self.scrollNode.view.addSubview(buttonView) + self.itemViews[type.key] = buttonView + self.itemsContainer.addSubview(buttonView) + + selectedButtonView = ComponentHostView() + self.selectedItemViews[type.key] = selectedButtonView + self.selectedItemsContainer.addSubview(selectedButtonView) } if case let .app(bot) = type { @@ -1578,7 +1695,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { style: self.panelStyle == .glass ? .glass : .legacy, type: type, isFirstOrLast: i == 0 || i == self.buttons.count - 1, - isSelected: i == self.selectedIndex, + isSelected: false, strings: self.presentationData.strings, theme: self.presentationData.theme, action: { [weak self] in @@ -1587,7 +1704,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { strongSelf.selectedIndex = i strongSelf.updateViews(transition: .init(animation: .curve(duration: 0.2, curve: .spring))) - if strongSelf.buttons.count > 5, let button = strongSelf.buttonViews[type.key] { + if strongSelf.buttons.count > 5, let button = strongSelf.itemViews[type.key] { strongSelf.scrollNode.view.scrollRectToVisible(button.frame.insetBy(dx: -35.0, dy: 0.0), animated: true) } } @@ -1601,11 +1718,32 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { environment: {}, containerSize: CGSize(width: buttonWidth, height: self.buttonSize.height) ) + self.itemSizes[type.key] = actualButtonSize + + let _ = selectedButtonView.update( + transition: buttonTransition, + component: AnyComponent(AttachButtonComponent( + context: self.context, + style: self.panelStyle == .glass ? .glass : .legacy, + type: type, + isFirstOrLast: i == 0 || i == self.buttons.count - 1, + isSelected: true, + strings: self.presentationData.strings, + theme: self.presentationData.theme, + action: { + }, + longPressAction: { + }) + ), + environment: {}, + containerSize: CGSize(width: buttonWidth, height: self.buttonSize.height) + ) if i == self.selectedIndex { selectionFrame = CGRect(origin: CGPoint(x: buttonFrame.midX - actualButtonSize.width * 0.5, y: buttonFrame.minY), size: actualButtonSize) } buttonTransition.setFrame(view: buttonView, frame: buttonFrame) + buttonTransition.setFrame(view: selectedButtonView, frame: buttonFrame) var accessibilityTitle = "" switch type { case .gallery: @@ -1634,14 +1772,14 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { buttonView.accessibilityTraits = [.button] } var removeIds: [AnyHashable] = [] - for (id, itemView) in self.buttonViews { + for (id, itemView) in self.itemViews { if !validIds.contains(id) { removeIds.append(id) itemView.removeFromSuperview() } } for id in removeIds { - self.buttonViews.removeValue(forKey: id) + self.itemViews.removeValue(forKey: id) } selectionFrame = selectionFrame.insetBy(dx: 1.0, dy: 4.0) @@ -1651,9 +1789,10 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { mostRightX += layout.safeInsets.right + 3.0 let contentSize = CGSize(width: mostRightX, height: self.buttonSize.height) - if contentSize != self.scrollNode.view.contentSize { + if contentSize != self.scrollNode.view.contentSize && self.scrollNode.view.bounds.width > 0.0 { self.scrollNode.view.contentSize = contentSize - self.scrollNode.view.isScrollEnabled = abs(contentSize.width - self.scrollNode.view.bounds.width) > 1.0 + self.scrollNode.view.isScrollEnabled = contentSize.width - self.scrollNode.view.bounds.width > 1.0 + self.liquidLensView?.clipsToBounds = self.scrollNode.view.isScrollEnabled } } @@ -1916,31 +2055,65 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { textPanelWidth = layout.size.width - panelSideInset * 2.0 } + self.updateViews(transition: .immediate) + let glassPanelHeight: CGFloat = 62.0 let bounds = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: self.buttonSize.height + insets.bottom)) if case .glass = self.panelStyle { let backgroundView: GlassBackgroundView - if let current = self.backgroundView { - backgroundView = current + let liquidLensView: LiquidLensView + if let currentBackground = self.backgroundView, let currentLens = self.liquidLensView { + backgroundView = currentBackground + liquidLensView = currentLens } else { backgroundView = GlassBackgroundView() - self.containerNode.view.addSubview(backgroundView) - self.containerNode.view.addSubview(self.scrollNode.view) self.backgroundView = backgroundView + + liquidLensView = LiquidLensView(kind: .noContainer) + liquidLensView.clipsToBounds = true + liquidLensView.layer.cornerRadius = 28.0 + + self.containerNode.view.addSubview(backgroundView) + self.containerNode.view.addSubview(liquidLensView) + self.containerNode.view.addSubview(self.scrollNode.view) + self.liquidLensView = liquidLensView + + liquidLensView.contentView.addSubview(self.itemsContainer) + liquidLensView.selectedContentView.addSubview(self.selectedItemsContainer) + + self.itemsContainer.clipsToBounds = true + self.itemsContainer.layer.cornerRadius = 28.0 + self.selectedItemsContainer.clipsToBounds = true + self.selectedItemsContainer.layer.cornerRadius = 28.0 + + let tabSelectionRecognizer = TabSelectionRecognizer(target: self, action: #selector(self.onTabSelectionGesture(_:))) + tabSelectionRecognizer.delegate = self.wrappedGestureRecognizerDelegate + self.tabSelectionRecognizer = tabSelectionRecognizer + self.scrollNode.view.addGestureRecognizer(tabSelectionRecognizer) } let panelSize = CGSize(width: isSelecting ? textPanelWidth : layout.size.width - layout.safeInsets.left - layout.safeInsets.right - panelSideInset * 2.0, height: isSelecting ? textPanelHeight - 11.0 : glassPanelHeight) - let backgroundViewColor: UIColor - if self.presentationData.theme.overallDarkAppearance { - backgroundViewColor = self.presentationData.theme.list.modalBlocksBackgroundColor.withAlphaComponent(0.55) - } else { - backgroundViewColor = self.presentationData.theme.list.plainBackgroundColor.withAlphaComponent(0.75) - } - + let cornerRadius: CGFloat = isSelecting ? min(20.0, panelSize.height * 0.5) : panelSize.height * 0.5 let backgroundOriginX: CGFloat = isSelecting ? panelSideInset : floorToScreenPixels((layout.size.width - panelSize.width) / 2.0) - backgroundView.update(size: panelSize, cornerRadius: isSelecting ? 20.0 : glassPanelHeight * 0.5, isDark: self.presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .custom, color: backgroundViewColor), transition: ComponentTransition(transition)) + + backgroundView.update(size: panelSize, cornerRadius: cornerRadius, isDark: self.presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.presentationData.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: ComponentTransition(transition)) + + let lensSideInset: CGFloat = defaultPanelSideInset + layout.safeInsets.left + let lensPanelSize = CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - lensSideInset * 2.0, height: glassPanelHeight) + self.lensParams = (lensPanelSize, cornerRadius) + self.updateLiquidLens(transition: ComponentTransition(transition)) + + transition.updatePosition(layer: liquidLensView.layer, position: CGPoint(x: backgroundOriginX + panelSize.width * 0.5, y: panelSize.height * 0.5)) + transition.updateBounds(layer: liquidLensView.layer, bounds: CGRect(origin: .zero, size: CGSize(width: lensPanelSize.width - 3.0 * 2.0, height: lensPanelSize.height - 3.0 * 2.0))) + transition.updatePosition(layer: backgroundView.layer, position: CGPoint(x: backgroundOriginX + panelSize.width * 0.5, y: panelSize.height * 0.5)) transition.updateBounds(layer: backgroundView.layer, bounds: CGRect(origin: .zero, size: panelSize)) + + let itemsContainerFrame = CGRect(origin: .zero, size: CGSize(width: lensPanelSize.width - 3.0 * 2.0, height: lensPanelSize.height - 3.0 * 2.0)) + transition.updateBounds(layer: self.itemsContainer.layer, bounds: CGRect(origin: .zero, size: itemsContainerFrame.size)) + transition.updateBounds(layer: self.selectedItemsContainer.layer, bounds: CGRect(origin: .zero, size: itemsContainerFrame.size)) + transition.updatePosition(layer: self.itemsContainer.layer, position: itemsContainerFrame.center) + transition.updatePosition(layer: self.selectedItemsContainer.layer, position: itemsContainerFrame.center) } var containerTransition: ContainedViewLayoutTransition @@ -2002,8 +2175,10 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { let alphaTransition = ContainedViewLayoutTransition.animated(duration: isSelecting ? 0.1 : 0.25, curve: .easeInOut) alphaTransition.updateAlpha(node: self.scrollNode, alpha: isSelecting || isAnyButtonVisible ? 0.0 : 1.0) containerTransition.updateTransformScale(node: self.scrollNode, scale: isSelecting || isAnyButtonVisible ? 0.85 : 1.0) - if let backgroundView = self.backgroundView { - containerTransition.updateTransformScale(layer: backgroundView.layer, scale: isAnyButtonVisible ? 0.85 : 1.0) + + if let liquidLensView = self.liquidLensView { + alphaTransition.updateAlpha(layer: liquidLensView.layer, alpha: isSelecting || isAnyButtonVisible ? 0.0 : 1.0) + containerTransition.updateTransformScale(layer: liquidLensView.layer, scale: isSelecting || isAnyButtonVisible ? 0.85 : 1.0) } if isSelectingUpdated { @@ -2022,7 +2197,10 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { alphaTransition.updateAlpha(node: textInputPanelNode, alpha: 1.0) let componentTransition = ComponentTransition.easeInOut(duration: 0.25) - componentTransition.animateBlur(layer: self.scrollNode.layer, fromRadius: 0.0, toRadius: 10.0) + if let liquidLensView = self.liquidLensView { + componentTransition.animateBlur(layer: liquidLensView.layer, fromRadius: 0.0, toRadius: 10.0) + transition.animatePositionAdditive(layer: liquidLensView.layer, offset: .zero, to: CGPoint(x: -27.0, y: 0.0)) + } let blurTransition = ComponentTransition.easeInOut(duration: 0.18) transition.animateTransformScale(node: textInputPanelNode.opaqueActionButtons, from: 0.01) @@ -2030,9 +2208,12 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { textInputPanelNode.opaqueActionButtons.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) transition.animatePosition(node: textInputPanelNode.opaqueActionButtons, from: CGPoint(x: textInputPanelNode.opaqueActionButtons.position.x, y: textInputPanelNode.opaqueActionButtons.position.y + heightDelta)) + blurTransition.animateBlur(layer: textInputPanelNode.inputModeView.layer, fromRadius: 4.0, toRadius: 0.0) + componentTransition.animateAlpha(view: textInputPanelNode.inputModeView, from: 0.0, to: 1.0) + transition.animatePositionAdditive(layer: textInputPanelNode.textPlaceholderNode.layer, offset: CGPoint(x: 6.0, y: heightDelta)) transition.animatePositionAdditive(layer: textInputPanelNode.inputModeView.layer, offset: CGPoint(x: 64.0, y: heightDelta)) - + textInputPanelNode.animateIn(transition: transition) } else { textInputPanelNode.alpha = 1.0 @@ -2054,7 +2235,10 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { alphaTransition.updateAlpha(node: textInputPanelNode, alpha: 0.0) let componentTransition = ComponentTransition.easeInOut(duration: 0.25) - componentTransition.animateBlur(layer: self.scrollNode.layer, fromRadius: 10.0, toRadius: 0.0) + if let liquidLensView = self.liquidLensView { + componentTransition.animateBlur(layer: liquidLensView.layer, fromRadius: 10.0, toRadius: 0.0) + transition.animatePositionAdditive(layer: liquidLensView.layer, offset: CGPoint(x: -27.0, y: 0.0)) + } let blurTransition = ComponentTransition.easeInOut(duration: 0.18) transition.animateTransformScale(layer: textInputPanelNode.opaqueActionButtons.layer, from: CGPoint(x: 1.0, y: 1.0), to: CGPoint(x: 0.01, y: 0.01)) @@ -2062,6 +2246,9 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { textInputPanelNode.opaqueActionButtons.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) transition.animatePosition(node: textInputPanelNode.opaqueActionButtons, to: CGPoint(x: textInputPanelNode.opaqueActionButtons.position.x, y: textInputPanelNode.opaqueActionButtons.position.y + heightDelta)) + blurTransition.animateBlur(layer: textInputPanelNode.inputModeView.layer, fromRadius: 0.0, toRadius: 4.0) + componentTransition.animateAlpha(view: textInputPanelNode.inputModeView, from: 1.0, to: 0.0) + transition.animatePositionAdditive(layer: textInputPanelNode.textPlaceholderNode.layer, offset: .zero, to: CGPoint(x: 6.0, y: heightDelta)) transition.animatePositionAdditive(layer: textInputPanelNode.inputModeView.layer, offset: .zero, to: CGPoint(x: 64.0, y: heightDelta)) } else { @@ -2086,8 +2273,6 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { transition.updateFrameAsPositionAndBounds(node: self.scrollNode, frame: CGRect(origin: CGPoint(x: self.isSelecting ? panelSideInset - defaultPanelSideInset : panelSideInset, y: self.isSelecting ? -11.0 : 0.0), size: CGSize(width: layout.size.width - panelSideInset * 2.0, height: self.buttonSize.height))) } - self.updateViews(transition: .immediate) - if let progress = self.loadingProgress { let loadingProgressNode: LoadingProgressNode if let current = self.progressNode { @@ -2183,8 +2368,70 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { return containerFrame.height } + func updateItemContainers(contentOffset: CGFloat, transition: ComponentTransition) { + let transform = CATransform3DMakeTranslation(-contentOffset, 0.0, 0.0) + transition.setSublayerTransform(view: self.itemsContainer, transform: transform) + transition.setSublayerTransform(view: self.selectedItemsContainer, transform: transform) + } + + func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + if self.selectionGestureState != nil { + self.selectionGestureState = nil + self.requestLayout(transition: .animated(duration: 0.4, curve: .spring)) + } + } + func scrollViewDidScroll(_ scrollView: UIScrollView) { + self.updateItemContainers(contentOffset: scrollView.contentOffset.x, transition: .immediate) self.updateViews(transition: .immediate) + self.updateLiquidLens(transition: .immediate) + } + + private var skipLensUpdate = false + private var lensParams: (panelSize: CGSize, cornerRadius: CGFloat)? + func updateLiquidLens(transition: ComponentTransition) { + guard !self.skipLensUpdate, let liquidLensView = self.liquidLensView, let (panelSize, _) = self.lensParams else { + return + } + + var selectionFrame = CGRect() + if self.selectedIndex >= 0 && self.selectedIndex < self.buttons.count, let itemView = self.itemViews[self.buttons[self.selectedIndex].key], let itemSize = self.itemSizes[self.buttons[self.selectedIndex].key] { + let contentOffset = self.scrollNode.view.contentOffset.x + selectionFrame = CGRect(origin: CGPoint(x: itemView.center.x - itemSize.width / 2.0 - contentOffset, y: itemView.frame.minY), size: itemSize) + } + + var lensSelection: (x: CGFloat, width: CGFloat) + if let selectionGestureState = self.selectionGestureState, selectionGestureState.isLifted { + lensSelection = (selectionGestureState.currentX, selectionFrame.width) + } else { + lensSelection = (selectionFrame.minX, selectionFrame.width) + } + + if !self.scrollNode.view.isScrollEnabled { + lensSelection.x = max(0.0, min(lensSelection.x, panelSize.width - lensSelection.width)) + } + + var isLifted = self.selectionGestureState?.isLifted == true || self.lensIsLifted + if let widthClass = self.validLayout?.metrics.widthClass, case .regular = widthClass { + isLifted = false + } + + let inset: CGFloat = 3.0 + liquidLensView.update(size: CGSize(width: panelSize.width - inset * 2.0, height: panelSize.height - inset * 2.0), cornerRadius: 28.0, selectionOrigin: CGPoint(x: lensSelection.x, y: 0.0), selectionSize: CGSize(width: lensSelection.width, height: panelSize.height - inset * 2.0), inset: 0.0, isDark: self.presentationData.theme.overallDarkAppearance, isLifted: isLifted, isCollapsed: self.isSelecting || self.buttons.count < 2, transition: transition) + } + + override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + if gestureRecognizer == self.tabSelectionRecognizer { + return true + } + return super.gestureRecognizerShouldBegin(gestureRecognizer) + } + + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { + return true + } + + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { + return true } } - diff --git a/submodules/AuthorizationUI/BUILD b/submodules/AuthorizationUI/BUILD index 5a48a9b3..cc280f15 100644 --- a/submodules/AuthorizationUI/BUILD +++ b/submodules/AuthorizationUI/BUILD @@ -47,6 +47,7 @@ swift_library( "//submodules/TelegramUI/Components/Premium/PremiumCoinComponent", "//submodules/TelegramUI/Components/PlainButtonComponent", "//submodules/Utils/DeviceModel", + "//submodules/PresentationDataUtils", ], visibility = [ "//visibility:public", diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequenceCodeEntryController.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequenceCodeEntryController.swift index 6cd8ce98..72f1db42 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequenceCodeEntryController.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequenceCodeEntryController.swift @@ -5,7 +5,9 @@ import AsyncDisplayKit import SwiftSignalKit import TelegramCore import TelegramPresentationData +import PresentationDataUtils import ProgressNavigationButtonNode +import AccountContext public final class AuthorizationSequenceCodeEntryController: ViewController { private var controllerNode: AuthorizationSequenceCodeEntryControllerNode { @@ -14,6 +16,7 @@ public final class AuthorizationSequenceCodeEntryController: ViewController { private var validLayout: ContainerViewLayout? + private let sharedContext: SharedAccountContext private let strings: PresentationStrings private let theme: PresentationTheme @@ -45,7 +48,8 @@ public final class AuthorizationSequenceCodeEntryController: ViewController { return self.data?.6 ?? false } - public init(presentationData: PresentationData, back: @escaping () -> Void) { + public init(sharedContext: SharedAccountContext, presentationData: PresentationData, back: @escaping () -> Void) { + self.sharedContext = sharedContext self.strings = presentationData.strings self.theme = presentationData.theme @@ -61,11 +65,14 @@ public final class AuthorizationSequenceCodeEntryController: ViewController { return false } self.navigationBar?.backPressed = { [weak self] in + guard let self else { + return + } let text: String let proceed: String let stop: String - if let (_, _, type, _, _, _, _) = self?.data, case .email = type { + if let (_, _, type, _, _, _, _) = self.data, case .email = type { text = presentationData.strings.Login_CancelEmailVerification proceed = presentationData.strings.Login_CancelEmailVerificationContinue stop = presentationData.strings.Login_CancelEmailVerificationStop @@ -75,7 +82,7 @@ public final class AuthorizationSequenceCodeEntryController: ViewController { stop = presentationData.strings.Login_CancelPhoneVerificationStop } - self?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: proceed, action: { + self.present(textAlertController(sharedContext: self.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: proceed, action: { }), TextAlertAction(type: .defaultAction, title: stop, action: { back() })]), in: .window(.root)) diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift index b6d861d6..bc640f69 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift @@ -9,6 +9,7 @@ import MtProtoKit import MessageUI import CoreTelephony import TelegramPresentationData +import PresentationDataUtils import TextFormat import AccountContext import CountrySelectionUI @@ -33,7 +34,7 @@ private enum InnerState: Equatable { public final class AuthorizationSequenceController: NavigationController, ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding { static func navigationBarTheme(_ theme: PresentationTheme) -> NavigationBarTheme { - return NavigationBarTheme(buttonColor: theme.intro.accentTextColor, disabledButtonColor: theme.intro.disabledTextColor, primaryTextColor: theme.intro.primaryTextColor, backgroundColor: .clear, opaqueBackgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: theme.rootController.navigationBar.badgeBackgroundColor, badgeStrokeColor: theme.rootController.navigationBar.badgeStrokeColor, badgeTextColor: theme.rootController.navigationBar.badgeTextColor) + return NavigationBarTheme(overallDarkAppearance: theme.overallDarkAppearance, buttonColor: theme.chat.inputPanel.panelControlColor, disabledButtonColor: theme.intro.disabledTextColor, primaryTextColor: theme.intro.primaryTextColor, backgroundColor: .clear, opaqueBackgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: theme.rootController.navigationBar.badgeBackgroundColor, badgeStrokeColor: theme.rootController.navigationBar.badgeStrokeColor, badgeTextColor: theme.rootController.navigationBar.badgeTextColor, edgeEffectColor: .clear, style: .glass) } private let sharedContext: SharedAccountContext @@ -247,7 +248,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth let carrier = CTCarrier() let mnc = carrier.mobileNetworkCode ?? "none" - AuthorizationSequenceController.presentEmailComposeController(address: "recover@telegram.org", subject: strongSelf.presentationData.strings.Login_InvalidPhoneEmailSubject(formattedNumber).string, body: strongSelf.presentationData.strings.Login_InvalidPhoneEmailBody(formattedNumber, appVersion, systemVersion, locale, mnc).string, from: controller, presentationData: strongSelf.presentationData) + AuthorizationSequenceController.presentEmailComposeController(sharedContext: strongSelf.sharedContext, address: "recover@telegram.org", subject: strongSelf.presentationData.strings.Login_InvalidPhoneEmailSubject(formattedNumber).string, body: strongSelf.presentationData.strings.Login_InvalidPhoneEmailBody(formattedNumber, appVersion, systemVersion, locale, mnc).string, from: controller, presentationData: strongSelf.presentationData) })) case .phoneLimitExceeded: text = strongSelf.presentationData.strings.Login_PhoneFloodError @@ -273,7 +274,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth let carrier = CTCarrier() let mnc = carrier.mobileNetworkCode ?? "none" - AuthorizationSequenceController.presentEmailComposeController(address: "recover@telegram.org", subject: strongSelf.presentationData.strings.Login_PhoneBannedEmailSubject(formattedNumber).string, body: strongSelf.presentationData.strings.Login_PhoneBannedEmailBody(formattedNumber, appVersion, systemVersion, locale, mnc).string, from: controller, presentationData: strongSelf.presentationData) + AuthorizationSequenceController.presentEmailComposeController(sharedContext: strongSelf.sharedContext, address: "recover@telegram.org", subject: strongSelf.presentationData.strings.Login_PhoneBannedEmailSubject(formattedNumber).string, body: strongSelf.presentationData.strings.Login_PhoneBannedEmailBody(formattedNumber, appVersion, systemVersion, locale, mnc).string, from: controller, presentationData: strongSelf.presentationData) })) case let .generic(info): text = strongSelf.presentationData.strings.Login_UnknownError @@ -295,7 +296,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth errorString = "unknown" } - AuthorizationSequenceController.presentEmailComposeController(address: "recover@telegram.org", subject: strongSelf.presentationData.strings.Login_PhoneGenericEmailSubject(formattedNumber).string, body: strongSelf.presentationData.strings.Login_PhoneGenericEmailBody(formattedNumber, errorString, appVersion, systemVersion, locale, mnc).string, from: controller, presentationData: strongSelf.presentationData) + AuthorizationSequenceController.presentEmailComposeController(sharedContext: strongSelf.sharedContext, address: "recover@telegram.org", subject: strongSelf.presentationData.strings.Login_PhoneGenericEmailSubject(formattedNumber).string, body: strongSelf.presentationData.strings.Login_PhoneGenericEmailBody(formattedNumber, errorString, appVersion, systemVersion, locale, mnc).string, from: controller, presentationData: strongSelf.presentationData) })) case .timeout: text = strongSelf.presentationData.strings.Login_NetworkError @@ -307,7 +308,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth controller.present(strongSelf.sharedContext.makeProxySettingsController(sharedContext: strongSelf.sharedContext, account: strongSelf.account), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) })) } - (controller.navigationController as? NavigationController)?.presentOverlay(controller: standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: actions), inGlobal: true, blockInteraction: true) + (controller.navigationController as? NavigationController)?.presentOverlay(controller: textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: actions), inGlobal: true, blockInteraction: true) controller.dismissConfirmation() } @@ -352,7 +353,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth text = strongSelf.presentationData.strings.Login_UnknownError } - controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + controller.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } } })) @@ -376,7 +377,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth if let currentController = currentController { controller = currentController } else { - controller = AuthorizationSequenceCodeEntryController(presentationData: self.presentationData, back: { [weak self] in + controller = AuthorizationSequenceCodeEntryController(sharedContext: self.sharedContext, presentationData: self.presentationData, back: { [weak self] in guard let strongSelf = self else { return } @@ -451,7 +452,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth let _ = self.engine.auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .empty)).startStandalone() } - controller.presentInGlobalOverlay(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})])) + controller.presentInGlobalOverlay(textAlertController(sharedContext: self.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})])) } }) ) @@ -510,7 +511,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth controller.resetCode() } - controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + controller.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } } } @@ -623,7 +624,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth controller.resetCode() } - controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + controller.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } } } @@ -645,7 +646,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth let mnc = carrier.mobileNetworkCode ?? "none" let _ = strongSelf.engine.auth.reportMissingCode(phoneNumber: number, phoneCodeHash: phoneCodeHash, mnc: mnc).start() - AuthorizationSequenceController.presentDidNotGetCodeUI(controller: controller, presentationData: strongSelf.presentationData, phoneNumber: number, mnc: mnc) + AuthorizationSequenceController.presentDidNotGetCodeUI(sharedContext: strongSelf.sharedContext, controller: controller, presentationData: strongSelf.presentationData, phoneNumber: number, mnc: mnc) } } else { controller?.inProgress = true @@ -681,7 +682,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth text = strongSelf.presentationData.strings.Login_NetworkError } - controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: actions), in: .window(.root)) + controller.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: actions), in: .window(.root)) } })) } @@ -780,7 +781,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth text = strongSelf.presentationData.strings.Login_EmailNotAllowedError } - controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + controller.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } }, completed: { controller?.inProgress = false @@ -827,7 +828,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth switch authorization.credential { case let appleIdCredential as ASAuthorizationAppleIDCredential: guard let tokenData = appleIdCredential.identityToken, let token = String(data: tokenData, encoding: .utf8) else { - lastController?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + lastController?.present(textAlertController(sharedContext: self.sharedContext, title: nil, text: self.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) return } @@ -850,7 +851,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth case .emailNotAllowed: text = strongSelf.presentationData.strings.Login_EmailNotAllowedError } - lastController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + lastController.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } })) } else { @@ -891,7 +892,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth text = strongSelf.presentationData.strings.Login_InvalidEmailAddressError } - lastController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + lastController.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } } }) @@ -907,7 +908,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth guard let lastController = self.viewControllers.last as? ViewController else { return } - lastController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: error.localizedDescription, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + lastController.present(textAlertController(sharedContext: self.sharedContext, title: nil, text: error.localizedDescription, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } @available(iOS 13.0, *) @@ -927,7 +928,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth if let currentController = currentController { controller = currentController } else { - controller = AuthorizationSequencePasswordEntryController(presentationData: self.presentationData, back: { [weak self] in + controller = AuthorizationSequencePasswordEntryController(sharedContext: self.sharedContext, presentationData: self.presentationData, back: { [weak self] in guard let strongSelf = self else { return } @@ -954,7 +955,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth text = strongSelf.presentationData.strings.Login_UnknownError } - controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + controller.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) controller.passwordIsInvalid() } } @@ -988,14 +989,14 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth strongController.inProgress = false - strongController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.TwoStepAuth_RecoveryUnavailable, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + strongController.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: strongSelf.presentationData.strings.TwoStepAuth_RecoveryUnavailable, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) strongController.didForgotWithNoRecovery = true })) } } controller.reset = { [weak self, weak controller] in if let strongSelf = self, let strongController = controller { - strongController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: suggestReset ? strongSelf.presentationData.strings.TwoStepAuth_RecoveryFailed : strongSelf.presentationData.strings.TwoStepAuth_RecoveryUnavailable, actions: [ + strongController.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: suggestReset ? strongSelf.presentationData.strings.TwoStepAuth_RecoveryFailed : strongSelf.presentationData.strings.TwoStepAuth_RecoveryUnavailable, actions: [ TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.Login_ResetAccountProtected_Reset, action: { if let strongSelf = self, let strongController = controller { @@ -1015,7 +1016,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth case .limitExceeded: text = strongSelf.presentationData.strings.Login_ResetAccountProtected_LimitExceeded } - strongController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + strongController.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } })) } @@ -1082,7 +1083,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth }) controller.reset = { [weak self, weak controller] in if let strongSelf = self, let strongController = controller { - strongController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.TwoStepAuth_ResetAccountConfirmation, actions: [ + strongController.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: strongSelf.presentationData.strings.TwoStepAuth_ResetAccountConfirmation, actions: [ TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.Login_ResetAccountProtected_Reset, action: { if let strongSelf = self, let strongController = controller { @@ -1102,7 +1103,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth case .limitExceeded: text = strongSelf.presentationData.strings.Login_ResetAccountProtected_LimitExceeded } - strongController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + strongController.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } })) } @@ -1132,7 +1133,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth if let currentController = currentController { controller = currentController } else { - controller = AuthorizationSequenceSignUpController(presentationData: self.presentationData, back: { [weak self] in + controller = AuthorizationSequenceSignUpController(sharedContext: self.sharedContext, presentationData: self.presentationData, back: { [weak self] in guard let strongSelf = self else { return } @@ -1231,7 +1232,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth text = strongSelf.presentationData.strings.Login_UnknownError } - controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + controller.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } } })) @@ -1379,7 +1380,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth } } - static func presentEmailComposeController(address: String, subject: String, body: String, from controller: ViewController, presentationData: PresentationData) { + static func presentEmailComposeController(sharedContext: SharedAccountContext, address: String, subject: String, body: String, from controller: ViewController, presentationData: PresentationData) { if MFMailComposeViewController.canSendMail() { final class ComposeDelegate: NSObject, MFMailComposeViewControllerDelegate { @objc func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { @@ -1398,7 +1399,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth controller.view.window?.rootViewController?.present(composeController, animated: true, completion: nil) } else { - controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.Login_EmailNotConfiguredError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + controller.present(textAlertController(sharedContext: sharedContext, title: nil, text: presentationData.strings.Login_EmailNotConfiguredError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } } @@ -1451,6 +1452,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth } public static func presentDidNotGetCodeUI( + sharedContext: SharedAccountContext, controller: ViewController, presentationData: PresentationData, phoneNumber: String, @@ -1470,6 +1472,6 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth emailBody.append("Locale: \(locale)\n") emailBody.append("MNC: \(mnc)") - AuthorizationSequenceController.presentEmailComposeController(address: "sms@telegram.org", subject: presentationData.strings.Login_EmailCodeSubject(formattedNumber).string, body: emailBody, from: controller, presentationData: presentationData) + AuthorizationSequenceController.presentEmailComposeController(sharedContext: sharedContext, address: "sms@telegram.org", subject: presentationData.strings.Login_EmailCodeSubject(formattedNumber).string, body: emailBody, from: controller, presentationData: presentationData) } } diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequencePasswordEntryController.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequencePasswordEntryController.swift index 000a0a49..913678b1 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequencePasswordEntryController.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequencePasswordEntryController.swift @@ -3,7 +3,9 @@ import UIKit import Display import AsyncDisplayKit import TelegramPresentationData +import PresentationDataUtils import ProgressNavigationButtonNode +import AccountContext final class AuthorizationSequencePasswordEntryController: ViewController { private var controllerNode: AuthorizationSequencePasswordEntryControllerNode { @@ -12,6 +14,7 @@ final class AuthorizationSequencePasswordEntryController: ViewController { private var validLayout: ContainerViewLayout? + private let sharedContext: SharedAccountContext private let presentationData: PresentationData var loginWithPassword: ((String) -> Void)? @@ -40,7 +43,8 @@ final class AuthorizationSequencePasswordEntryController: ViewController { } } - init(presentationData: PresentationData, back: @escaping () -> Void) { + init(sharedContext: SharedAccountContext, presentationData: PresentationData, back: @escaping () -> Void) { + self.sharedContext = sharedContext self.presentationData = presentationData super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: AuthorizationSequenceController.navigationBarTheme(presentationData.theme), strings: NavigationBarStrings(presentationStrings: presentationData.strings))) @@ -153,10 +157,8 @@ final class AuthorizationSequencePasswordEntryController: ViewController { } func forgotPressed() { - /*if self.suggestReset { - self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.TwoStepAuth_RecoveryFailed, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - } else*/ if self.didForgotWithNoRecovery { - self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.TwoStepAuth_RecoveryUnavailable, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + if self.didForgotWithNoRecovery { + self.present(textAlertController(sharedContext: self.sharedContext, title: nil, text: self.presentationData.strings.TwoStepAuth_RecoveryUnavailable, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } else { self.forgot?() } diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequencePasswordEntryControllerNode.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequencePasswordEntryControllerNode.swift index 2a7c258b..e94a8f98 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequencePasswordEntryControllerNode.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequencePasswordEntryControllerNode.swift @@ -107,7 +107,7 @@ final class AuthorizationSequencePasswordEntryControllerNode: ASDisplayNode, UIT self.codeField.textField.tintColor = self.theme.list.itemAccentColor self.codeField.textField.accessibilityHint = self.strings.Login_VoiceOver_Password - self.proceedNode = SolidRoundedButtonNode(title: self.strings.Login_Continue, theme: SolidRoundedButtonTheme(theme: self.theme), height: 50.0, cornerRadius: 11.0) + self.proceedNode = SolidRoundedButtonNode(title: self.strings.Login_Continue, theme: SolidRoundedButtonTheme(theme: self.theme), glass: true, height: 50.0, cornerRadius: 50.0 * 0.5) self.proceedNode.progressType = .embedded self.proceedNode.isEnabled = false diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequencePaymentScreen.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequencePaymentScreen.swift index f261be57..61715e95 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequencePaymentScreen.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequencePaymentScreen.swift @@ -215,7 +215,7 @@ final class AuthorizationSequencePaymentScreenComponent: Component { ).string let presentationData = component.presentationData - AuthorizationSequenceController.presentEmailComposeController(address: component.supportEmailAddress, subject: environment.strings.Login_PhonePaidEmailSubject, body: body, from: controller, presentationData: presentationData) + AuthorizationSequenceController.presentEmailComposeController(sharedContext: component.sharedContext, address: component.supportEmailAddress, subject: environment.strings.Login_PhonePaidEmailSubject, body: body, from: controller, presentationData: presentationData) } func update(component: AuthorizationSequencePaymentScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryController.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryController.swift index 54855476..c28585b3 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryController.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryController.swift @@ -6,6 +6,7 @@ import SwiftSignalKit import TelegramCore import Postbox import TelegramPresentationData +import PresentationDataUtils import ProgressNavigationButtonNode import AccountContext import CountrySelectionUI @@ -91,7 +92,7 @@ public final class AuthorizationSequencePhoneEntryController: ViewController, MF } if !otherAccountPhoneNumbers.1.isEmpty { - self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed)) + self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "___close", style: .plain, target: self, action: #selector(self.cancelPressed)) } if let countriesConfiguration { @@ -173,7 +174,7 @@ public final class AuthorizationSequencePhoneEntryController: ViewController, MF self.controllerNode.selectCountryCode = { [weak self] in if let strongSelf = self { - let controller = AuthorizationSequenceCountrySelectionController(strings: strongSelf.presentationData.strings, theme: strongSelf.presentationData.theme) + let controller = AuthorizationSequenceCountrySelectionController(strings: strongSelf.presentationData.strings, theme: strongSelf.presentationData.theme, glass: true) controller.completeWithCountryCode = { code, name in if let strongSelf = self, let currentData = strongSelf.currentData { strongSelf.updateData(countryCode: Int32(code), countryName: name, number: currentData.2) @@ -404,7 +405,7 @@ public final class AuthorizationSequencePhoneEntryController: ViewController, MF })) } actions.append(TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})) - self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Login_PhoneNumberAlreadyAuthorized, actions: actions), in: .window(.root)) + self.present(textAlertController(sharedContext: self.sharedContext, title: nil, text: self.presentationData.strings.Login_PhoneNumberAlreadyAuthorized, actions: actions), in: .window(.root)) } else { if let validLayout = self.validLayout, validLayout.size.width > 320.0 { let (code, formattedNumber) = self.controllerNode.formattedCodeAndNumber @@ -425,7 +426,7 @@ public final class AuthorizationSequencePhoneEntryController: ViewController, MF strongSelf.loginWithNumber?(strongSelf.controllerNode.currentNumber, strongSelf.controllerNode.syncContacts) } })) - self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: logInNumber, text: self.presentationData.strings.Login_PhoneNumberConfirmation, actions: actions), in: .window(.root)) + self.present(textAlertController(sharedContext: self.sharedContext, title: logInNumber, text: self.presentationData.strings.Login_PhoneNumberConfirmation, actions: actions), in: .window(.root)) } } } else { diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift index 69ca7fc7..4b440530 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift @@ -423,7 +423,7 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode { self.phoneAndCountryNode = PhoneAndCountryNode(strings: strings, theme: theme) - self.proceedNode = SolidRoundedButtonNode(title: self.strings.Login_Continue, theme: SolidRoundedButtonTheme(theme: self.theme), height: 50.0, cornerRadius: 11.0) + self.proceedNode = SolidRoundedButtonNode(title: self.strings.Login_Continue, theme: SolidRoundedButtonTheme(theme: self.theme), glass: true, height: 50.0, cornerRadius: 50 * 0.5) self.proceedNode.progressType = .embedded self.proceedNode.isEnabled = false @@ -857,7 +857,7 @@ final class PhoneConfirmationController: ViewController { self.cancelButton.accessibilityTraits = [.button] self.cancelButton.accessibilityLabel = strings.Login_Edit - self.proceedNode = SolidRoundedButtonNode(title: strings.Login_Continue, theme: SolidRoundedButtonTheme(theme: theme), height: 50.0, cornerRadius: 11.0) + self.proceedNode = SolidRoundedButtonNode(title: strings.Login_Continue, theme: SolidRoundedButtonTheme(theme: theme), glass: true, height: 50.0, cornerRadius: 50.0 * 0.5) self.proceedNode.progressType = .embedded let font = Font.with(size: 20.0, design: .regular, traits: [.monospacedNumbers]) diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequenceSignUpController.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequenceSignUpController.swift index 8c8fdd8d..2ec0d705 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequenceSignUpController.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequenceSignUpController.swift @@ -5,6 +5,7 @@ import AsyncDisplayKit import SwiftSignalKit import TelegramCore import TelegramPresentationData +import PresentationDataUtils import LegacyComponents import ProgressNavigationButtonNode import ImageCompression @@ -13,6 +14,7 @@ import Postbox import TextFormat import MoreButtonNode import ContextUI +import AccountContext final class AuthorizationSequenceSignUpController: ViewController { private var controllerNode: AuthorizationSequenceSignUpControllerNode { @@ -23,6 +25,7 @@ final class AuthorizationSequenceSignUpController: ViewController { private let moreButtonNode: MoreButtonNode + private let sharedContext: SharedAccountContext private let presentationData: PresentationData private let back: () -> Void @@ -46,7 +49,8 @@ final class AuthorizationSequenceSignUpController: ViewController { } } - init(presentationData: PresentationData, back: @escaping () -> Void, displayCancel: Bool) { + init(sharedContext: SharedAccountContext, presentationData: PresentationData, back: @escaping () -> Void, displayCancel: Bool) { + self.sharedContext = sharedContext self.presentationData = presentationData self.back = back @@ -68,7 +72,7 @@ final class AuthorizationSequenceSignUpController: ViewController { guard let strongSelf = self else { return } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.Login_CancelSignUpConfirmation, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Login_CancelPhoneVerificationContinue, action: { + strongSelf.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: presentationData.strings.Login_CancelSignUpConfirmation, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Login_CancelPhoneVerificationContinue, action: { }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Login_CancelPhoneVerificationStop, action: { back() })]), in: .window(.root)) @@ -92,7 +96,7 @@ final class AuthorizationSequenceSignUpController: ViewController { } @objc private func cancelPressed() { - self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Login_CancelSignUpConfirmation, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Login_CancelPhoneVerificationContinue, action: { + self.present(textAlertController(sharedContext: self.sharedContext, title: nil, text: self.presentationData.strings.Login_CancelSignUpConfirmation, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Login_CancelPhoneVerificationContinue, action: { }), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Login_CancelPhoneVerificationStop, action: { [weak self] in self?.back() })]), in: .window(.root)) diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequenceSplashController.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequenceSplashController.swift index 044c5c70..5f517176 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequenceSplashController.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequenceSplashController.swift @@ -73,10 +73,12 @@ public final class AuthorizationSequenceSplashController: ViewController { self.controller = RMIntroViewController(backgroundColor: theme.list.plainBackgroundColor, primaryColor: theme.list.itemPrimaryTextColor, buttonColor: theme.intro.startButtonColor, accentColor: theme.list.itemAccentColor, regularDotColor: theme.intro.dotColor, highlightedDotColor: theme.list.itemAccentColor, suggestedLocalizationSignal: localizationSignal) - self.startButton = SolidRoundedButtonNode(title: "Start Messaging", theme: SolidRoundedButtonTheme(theme: theme), height: 50.0, cornerRadius: 13.0, isShimmering: true) + self.startButton = SolidRoundedButtonNode(title: "Start Messaging", theme: SolidRoundedButtonTheme(theme: theme), glass: true, height: 50.0, cornerRadius: 50.0 * 0.5, isShimmering: true) super.init(navigationBarPresentationData: nil) + self._hasGlassStyle = true + self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) self.statusBar.statusBarStyle = theme.intro.statusBarStyle.style diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutHeaderItem.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutHeaderItem.swift index c2c4a9c9..84ff2287 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutHeaderItem.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutHeaderItem.swift @@ -114,7 +114,7 @@ class BotCheckoutHeaderItemNode: ListViewItemNode { self.highlightedBackgroundNode = ASDisplayNode() self.highlightedBackgroundNode.isLayerBacked = true - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.backgroundNode) self.addSubnode(self.imageNode) diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutPriceItem.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutPriceItem.swift index a88ecb09..43c32de5 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutPriceItem.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutPriceItem.swift @@ -113,7 +113,7 @@ class BotCheckoutPriceItemNode: ListViewItemNode { self.maskNode = ASImageNode() self.maskNode.isUserInteractionEnabled = false - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.backgroundNode) self.addSubnode(self.titleNode) diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutTipItem.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutTipItem.swift index 7989f744..7b177db9 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutTipItem.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutTipItem.swift @@ -509,7 +509,7 @@ class BotCheckoutTipItemNode: ListViewItemNode, UITextFieldDelegate { self.maskNode = ASImageNode() self.maskNode.isUserInteractionEnabled = false - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.backgroundNode) diff --git a/submodules/BrowserUI/BUILD b/submodules/BrowserUI/BUILD index e031d767..472c7550 100644 --- a/submodules/BrowserUI/BUILD +++ b/submodules/BrowserUI/BUILD @@ -50,6 +50,13 @@ swift_library( "//submodules/TelegramUI/Components/ListActionItemComponent", "//submodules/Utils/DeviceModel", "//submodules/LegacyMediaPickerUI", + "//submodules/TelegramUI/Components/AlertComponent", + "//submodules/TelegramUI/Components/GlassBackgroundComponent", + "//submodules/TelegramUI/Components/EdgeEffect", + "//submodules/TelegramUI/Components/ButtonComponent", + "//submodules/TelegramUI/Components/GlassBarButtonComponent", + "//submodules/TelegramUI/Components/SearchInputPanelComponent", + "//submodules/TelegramUI/Components/GlassControls", ], visibility = [ "//visibility:public", diff --git a/submodules/BrowserUI/Sources/BrowserAddressBarComponent.swift b/submodules/BrowserUI/Sources/BrowserAddressBarComponent.swift index da83dddb..00915444 100644 --- a/submodules/BrowserUI/Sources/BrowserAddressBarComponent.swift +++ b/submodules/BrowserUI/Sources/BrowserAddressBarComponent.swift @@ -9,6 +9,9 @@ import AccountContext import BundleIconComponent import MultilineTextComponent import UrlEscaping +import GlassBackgroundComponent +import GlassBarButtonComponent +import EdgeEffect final class AddressBarContentComponent: Component { public typealias EnvironmentType = BrowserNavigationBarEnvironment @@ -19,6 +22,8 @@ final class AddressBarContentComponent: Component { let url: String let isSecure: Bool let isExpanded: Bool + let readingProgress: CGFloat + let loadingProgress: Double? let performAction: ActionSlot init( @@ -28,6 +33,8 @@ final class AddressBarContentComponent: Component { url: String, isSecure: Bool, isExpanded: Bool, + readingProgress: CGFloat, + loadingProgress: Double?, performAction: ActionSlot ) { self.theme = theme @@ -36,6 +43,8 @@ final class AddressBarContentComponent: Component { self.url = url self.isSecure = isSecure self.isExpanded = isExpanded + self.readingProgress = readingProgress + self.loadingProgress = loadingProgress self.performAction = performAction } @@ -58,6 +67,12 @@ final class AddressBarContentComponent: Component { if lhs.isExpanded != rhs.isExpanded { return false } + if lhs.readingProgress != rhs.readingProgress { + return false + } + if lhs.loadingProgress != rhs.loadingProgress { + return false + } return true } @@ -85,6 +100,8 @@ final class AddressBarContentComponent: Component { var isSecure: Bool var collapseFraction: CGFloat var isTablet: Bool + var readingProgress: CGFloat + var loadingProgress: Double? static func ==(lhs: Params, rhs: Params) -> Bool { if lhs.theme !== rhs.theme { @@ -111,26 +128,33 @@ final class AddressBarContentComponent: Component { if lhs.isTablet != rhs.isTablet { return false } + if lhs.readingProgress != rhs.readingProgress { + return false + } + if lhs.loadingProgress != rhs.loadingProgress { + return false + } return true } } private let activated: (Bool) -> Void = { _ in } private let deactivated: (Bool) -> Void = { _ in } - - private let backgroundLayer: SimpleLayer - - private let iconView: UIImageView + + private let backgroundView: GlassBackgroundView private let clearIconView: UIImageView private let clearIconButton: HighlightTrackingButton - private let cancelButtonTitle: ComponentView - private let cancelButton: HighlightTrackingButton + private let cancelButton = ComponentView() private var placeholderContent = ComponentView() private var titleContent = ComponentView() + private let clippingView = UIView() + private var loadingProgress = ComponentView() + private var readingProgressView = UIView() + private var textFrame: CGRect? private var textField: TextField? @@ -144,50 +168,30 @@ final class AddressBarContentComponent: Component { } init() { - self.backgroundLayer = SimpleLayer() - - self.iconView = UIImageView() + self.backgroundView = GlassBackgroundView() self.clearIconView = UIImageView() self.clearIconButton = HighlightableButton() self.clearIconView.isHidden = false self.clearIconButton.isHidden = false - - self.cancelButtonTitle = ComponentView() - self.cancelButton = HighlightTrackingButton() - + super.init(frame: CGRect()) - self.layer.addSublayer(self.backgroundLayer) + self.clippingView.clipsToBounds = true - self.addSubview(self.iconView) - self.addSubview(self.clearIconView) - self.addSubview(self.clearIconButton) + self.addSubview(self.backgroundView) + self.backgroundView.contentView.addSubview(self.clippingView) + self.clippingView.addSubview(self.readingProgressView) + + self.backgroundView.contentView.addSubview(self.clearIconView) + self.backgroundView.contentView.addSubview(self.clearIconButton) - self.addSubview(self.cancelButton) self.clipsToBounds = true let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))) self.tapRecognizer = tapRecognizer self.addGestureRecognizer(tapRecognizer) - - self.cancelButton.highligthedChanged = { [weak self] highlighted in - if let strongSelf = self { - if highlighted { - if let cancelButtonTitleView = strongSelf.cancelButtonTitle.view { - cancelButtonTitleView.layer.removeAnimation(forKey: "opacity") - cancelButtonTitleView.alpha = 0.4 - } - } else { - if let cancelButtonTitleView = strongSelf.cancelButtonTitle.view { - cancelButtonTitleView.alpha = 1.0 - cancelButtonTitleView.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) - } - } - } - } - self.cancelButton.addTarget(self, action: #selector(self.cancelPressed), for: .touchUpInside) - + self.clearIconButton.highligthedChanged = { [weak self] highlighted in if let strongSelf = self { if highlighted { @@ -263,11 +267,16 @@ final class AddressBarContentComponent: Component { self.placeholderContent.view?.isHidden = !text.isEmpty if let params = self.params { - self.update(theme: params.theme, strings: params.strings, size: params.size, isActive: params.isActive, title: params.title, isSecure: params.isSecure, collapseFraction: params.collapseFraction, isTablet: params.isTablet, transition: .immediate) + self.update(theme: params.theme, strings: params.strings, size: params.size, isActive: params.isActive, title: params.title, isSecure: params.isSecure, collapseFraction: params.collapseFraction, isTablet: params.isTablet, readingProgress: params.readingProgress, loadingProgress: params.loadingProgress, transition: .immediate) } } - func update(component: AddressBarContentComponent, availableSize: CGSize, environment: Environment, transition: ComponentTransition) -> CGSize { + func update( + component: AddressBarContentComponent, + availableSize: CGSize, + environment: Environment, + transition: ComponentTransition + ) -> CGSize { let collapseFraction = environment[BrowserNavigationBarEnvironment.self].fraction let wasExpanded = self.component?.isExpanded ?? false @@ -282,12 +291,36 @@ final class AddressBarContentComponent: Component { let isActive = self.textField?.isFirstResponder ?? false let title = getDisplayUrl(component.url, hostOnly: true) - self.update(theme: component.theme, strings: component.strings, size: availableSize, isActive: isActive, title: title.lowercased(), isSecure: component.isSecure, collapseFraction: collapseFraction, isTablet: component.metrics.isTablet, transition: transition) + self.update( + theme: component.theme, + strings: component.strings, + size: availableSize, + isActive: isActive, + title: title.lowercased(), + isSecure: component.isSecure, + collapseFraction: collapseFraction, + isTablet: component.metrics.isTablet, + readingProgress: component.readingProgress, + loadingProgress: component.loadingProgress, + transition: transition + ) return availableSize } - public func update(theme: PresentationTheme, strings: PresentationStrings, size: CGSize, isActive: Bool, title: String, isSecure: Bool, collapseFraction: CGFloat, isTablet: Bool, transition: ComponentTransition) { + public func update( + theme: PresentationTheme, + strings: PresentationStrings, + size: CGSize, + isActive: Bool, + title: String, + isSecure: Bool, + collapseFraction: CGFloat, + isTablet: Bool, + readingProgress: CGFloat, + loadingProgress: Double?, + transition: ComponentTransition + ) { let params = Params( theme: theme, strings: strings, @@ -296,7 +329,9 @@ final class AddressBarContentComponent: Component { title: title, isSecure: isSecure, collapseFraction: collapseFraction, - isTablet: isTablet + isTablet: isTablet, + readingProgress: readingProgress, + loadingProgress: loadingProgress ) if self.params == params { @@ -306,8 +341,6 @@ final class AddressBarContentComponent: Component { let isActiveWithText = self.component?.isExpanded ?? false if self.params?.theme !== theme { - self.iconView.image = generateTintedImage(image: UIImage(bundleImageName: "Media Grid/Lock"), color: .white)?.withRenderingMode(.alwaysTemplate) - self.iconView.tintColor = theme.rootController.navigationSearchBar.inputIconColor self.clearIconView.image = generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: .white)?.withRenderingMode(.alwaysTemplate) self.clearIconView.tintColor = theme.rootController.navigationSearchBar.inputClearButtonColor } @@ -315,57 +348,9 @@ final class AddressBarContentComponent: Component { self.params = params let sideInset: CGFloat = 10.0 - let inputHeight: CGFloat = 36.0 + let inputHeight: CGFloat = 44.0 let topInset: CGFloat = (size.height - inputHeight) / 2.0 - self.backgroundLayer.backgroundColor = theme.rootController.navigationSearchBar.inputFillColor.cgColor - self.backgroundLayer.cornerRadius = 10.5 - transition.setAlpha(layer: self.backgroundLayer, alpha: max(0.0, min(1.0, 1.0 - collapseFraction * 1.5))) - - let cancelTextSize = self.cancelButtonTitle.update( - transition: .immediate, - component: AnyComponent(Text( - text: strings.Common_Cancel, - font: Font.regular(17.0), - color: theme.rootController.navigationBar.accentTextColor - )), - environment: {}, - containerSize: CGSize(width: size.width - 32.0, height: 100.0) - ) - - let cancelButtonSpacing: CGFloat = 8.0 - - var backgroundFrame = CGRect(origin: CGPoint(x: sideInset, y: topInset), size: CGSize(width: size.width - sideInset * 2.0, height: inputHeight)) - if isActiveWithText && !isTablet { - backgroundFrame.size.width -= cancelTextSize.width + cancelButtonSpacing - } - transition.setFrame(layer: self.backgroundLayer, frame: backgroundFrame) - - transition.setFrame(view: self.cancelButton, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX, y: 0.0), size: CGSize(width: cancelButtonSpacing + cancelTextSize.width, height: size.height))) - self.cancelButton.isUserInteractionEnabled = isActiveWithText && !isTablet - - let textX: CGFloat = backgroundFrame.minX + sideInset - let textFrame = CGRect(origin: CGPoint(x: textX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.maxX - textX, height: backgroundFrame.height)) - - let placeholderSize = self.placeholderContent.update( - transition: transition, - component: AnyComponent( - Text(text: strings.WebBrowser_AddressPlaceholder, font: Font.regular(17.0), color: theme.rootController.navigationSearchBar.inputPlaceholderTextColor) - ), - environment: {}, - containerSize: size - ) - if let placeholderContentView = self.placeholderContent.view { - if placeholderContentView.superview == nil { - placeholderContentView.alpha = 0.0 - placeholderContentView.isHidden = true - self.addSubview(placeholderContentView) - } - let placeholderContentFrame = CGRect(origin: CGPoint(x: textFrame.minX, y: backgroundFrame.midY - placeholderSize.height / 2.0), size: placeholderSize) - transition.setFrame(view: placeholderContentView, frame: placeholderContentFrame) - transition.setAlpha(view: placeholderContentView, alpha: isActiveWithText ? 1.0 : 0.0) - } - let titleSize = self.titleContent.update( transition: transition, component: AnyComponent( @@ -379,51 +364,96 @@ final class AddressBarContentComponent: Component { environment: {}, containerSize: CGSize(width: size.width - 36.0, height: size.height) ) - var titleContentFrame = CGRect(origin: CGPoint(x: isActiveWithText ? textFrame.minX : backgroundFrame.midX - titleSize.width / 2.0, y: backgroundFrame.midY - titleSize.height / 2.0), size: titleSize) - if isSecure && !isActiveWithText { - titleContentFrame.origin.x += 7.0 + + let expandedBackgroundWidth = size.width - 14.0 * 2.0 + let collapsedBackgroundWidth = titleSize.width + 32.0 + var backgroundSize = CGSize(width: expandedBackgroundWidth * (1.0 - collapseFraction) + collapsedBackgroundWidth * collapseFraction, height: 44.0) + + let cancelButtonSpacing: CGFloat = 8.0 + let cancelSize = self.cancelButton.update( + transition: transition, + component: AnyComponent( + GlassBarButtonComponent( + size: CGSize(width: 44.0, height: 44.0), + backgroundColor: nil, + isDark: theme.overallDarkAppearance, + state: .glass, + component: AnyComponentWithIdentity(id: "close", component: AnyComponent( + BundleIconComponent(name: "Navigation/Close", tintColor: theme.chat.inputPanel.panelControlColor) + )), + action: { [weak self] _ in + self?.cancelPressed() + } + ) + ), + environment: {}, + containerSize: CGSize(width: 44.0, height: 44.0) + ) + + if isActiveWithText && !isTablet { + backgroundSize.width -= cancelSize.width + cancelButtonSpacing + 4.0 } - var titleSizeChanged = false + self.backgroundView.update(size: backgroundSize, cornerRadius: backgroundSize.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition) + + + var backgroundFrame = CGRect(origin: CGPoint(x: floor((size.width - backgroundSize.width) / 2.0), y: topInset), size: backgroundSize) + if isActiveWithText && !isTablet { + backgroundFrame.origin.x = 16.0 + } + transition.setFrame(view: self.backgroundView, frame: backgroundFrame) + + transition.setFrame(view: self.clippingView, frame: CGRect(origin: .zero, size: backgroundFrame.size)) + transition.setCornerRadius(layer: self.clippingView.layer, cornerRadius: backgroundFrame.size.height * 0.5) + + let textX: CGFloat = sideInset + let textFrame = CGRect(origin: CGPoint(x: textX, y: 0.0), size: CGSize(width: backgroundFrame.maxX - textX, height: backgroundFrame.height)) + + let placeholderSize = self.placeholderContent.update( + transition: transition, + component: AnyComponent( + Text(text: strings.WebBrowser_AddressPlaceholder, font: Font.regular(17.0), color: theme.rootController.navigationSearchBar.inputPlaceholderTextColor) + ), + environment: {}, + containerSize: size + ) + if let placeholderContentView = self.placeholderContent.view { + if placeholderContentView.superview == nil { + placeholderContentView.alpha = 0.0 + placeholderContentView.isHidden = true + self.backgroundView.contentView.addSubview(placeholderContentView) + } + let placeholderContentFrame = CGRect(origin: CGPoint(x: textFrame.minX, y: backgroundFrame.size.height / 2.0 - placeholderSize.height / 2.0), size: placeholderSize) + transition.setFrame(view: placeholderContentView, frame: placeholderContentFrame) + transition.setAlpha(view: placeholderContentView, alpha: isActiveWithText ? 1.0 : 0.0) + } + + let titleContentFrame = CGRect(origin: CGPoint(x: isActiveWithText ? textFrame.minX : backgroundFrame.width / 2.0 - titleSize.width / 2.0, y: backgroundFrame.height / 2.0 - titleSize.height / 2.0), size: titleSize) if let titleContentView = self.titleContent.view { if titleContentView.superview == nil { - self.addSubview(titleContentView) - } - if titleContentView.frame.width != titleContentFrame.size.width { - titleSizeChanged = true + self.backgroundView.contentView.addSubview(titleContentView) } transition.setPosition(view: titleContentView, position: titleContentFrame.center) titleContentView.bounds = CGRect(origin: .zero, size: titleContentFrame.size) transition.setAlpha(view: titleContentView, alpha: isActiveWithText ? 0.0 : 1.0) } - - if let image = self.iconView.image { - let iconFrame = CGRect(origin: CGPoint(x: titleContentFrame.minX - image.size.width - 3.0, y: backgroundFrame.minY + floor((backgroundFrame.height - image.size.height) / 2.0)), size: image.size) - var iconTransition = transition - if titleSizeChanged { - iconTransition = .immediate - } - iconTransition.setFrame(view: self.iconView, frame: iconFrame) - transition.setAlpha(view: self.iconView, alpha: isActiveWithText || !isSecure ? 0.0 : 1.0) - } - + if let image = self.clearIconView.image { - let iconFrame = CGRect(origin: CGPoint(x: backgroundFrame.maxX - image.size.width - 4.0, y: backgroundFrame.minY + floor((backgroundFrame.height - image.size.height) / 2.0)), size: image.size) + let iconFrame = CGRect(origin: CGPoint(x: backgroundFrame.width - image.size.width - 4.0, y: floor((backgroundFrame.height - image.size.height) / 2.0)), size: image.size) transition.setFrame(view: self.clearIconView, frame: iconFrame) transition.setFrame(view: self.clearIconButton, frame: iconFrame.insetBy(dx: -8.0, dy: -10.0)) transition.setAlpha(view: self.clearIconView, alpha: isActiveWithText ? 1.0 : 0.0) self.clearIconButton.isUserInteractionEnabled = isActiveWithText } - if let cancelButtonTitleComponentView = self.cancelButtonTitle.view { - if cancelButtonTitleComponentView.superview == nil { - self.addSubview(cancelButtonTitleComponentView) - cancelButtonTitleComponentView.isUserInteractionEnabled = false + if let cancelButtonView = self.cancelButton.view { + if cancelButtonView.superview == nil { + self.addSubview(cancelButtonView) } - transition.setFrame(view: cancelButtonTitleComponentView, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX + cancelButtonSpacing, y: floor((size.height - cancelTextSize.height) / 2.0)), size: cancelTextSize)) - transition.setAlpha(view: cancelButtonTitleComponentView, alpha: isActiveWithText && !isTablet ? 1.0 : 0.0) + transition.setFrame(view: cancelButtonView, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX + cancelButtonSpacing, y: floor((size.height - cancelSize.height) / 2.0)), size: cancelSize)) + transition.setAlpha(view: cancelButtonView, alpha: isActiveWithText && !isTablet ? 1.0 : 0.0) } - let textFieldFrame = CGRect(origin: CGPoint(x: textFrame.minX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.maxX - textFrame.minX, height: backgroundFrame.height)) + let textFieldFrame = CGRect(origin: CGPoint(x: sideInset, y: 0.0), size: CGSize(width: backgroundFrame.width - sideInset, height: backgroundFrame.height)) let textField: TextField if let current = self.textField { @@ -434,7 +464,7 @@ final class AddressBarContentComponent: Component { textField.autocorrectionType = .no textField.keyboardType = .URL textField.returnKeyType = .go - self.insertSubview(textField, belowSubview: self.clearIconView) + self.backgroundView.contentView.insertSubview(textField, belowSubview: self.clearIconView) self.textField = textField textField.delegate = self @@ -450,9 +480,34 @@ final class AddressBarContentComponent: Component { } textField.textColor = theme.rootController.navigationSearchBar.inputTextColor - transition.setFrame(view: textField, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + sideInset, y: backgroundFrame.minY - UIScreenPixel), size: CGSize(width: backgroundFrame.width - sideInset - 32.0, height: backgroundFrame.height))) + transition.setFrame(view: textField, frame: CGRect(origin: CGPoint(x: sideInset, y: -UIScreenPixel), size: CGSize(width: backgroundFrame.width - sideInset - 32.0, height: backgroundFrame.height))) transition.setAlpha(view: textField, alpha: isActiveWithText ? 1.0 : 0.0) textField.isUserInteractionEnabled = isActiveWithText + + + let loadingProgressInset: CGFloat = 12.0 + let loadingProgressSize = CGSize(width: backgroundSize.width - loadingProgressInset * 2.0, height: 2.0) + let _ = self.loadingProgress.update( + transition: transition, + component: AnyComponent(LoadingProgressComponent( + color: theme.rootController.navigationBar.accentTextColor, + height: loadingProgressSize.height, + value: params.loadingProgress ?? 0.0 + )), + environment: {}, + containerSize: loadingProgressSize + ) + if let loadingProgressView = self.loadingProgress.view { + if loadingProgressView.superview == nil { + self.clippingView.addSubview(loadingProgressView) + } + transition.setFrame(view: loadingProgressView, frame: CGRect(origin: CGPoint(x: loadingProgressInset, y: backgroundSize.height - loadingProgressSize.height), size: loadingProgressSize)) + transition.setAlpha(view: loadingProgressView, alpha: isActiveWithText ? 0.0 : 1.0) + } + + self.readingProgressView.backgroundColor = theme.rootController.navigationBar.primaryTextColor.withMultipliedAlpha(0.07) + self.readingProgressView.frame = CGRect(origin: .zero, size: CGSize(width: backgroundSize.width * params.readingProgress, height: backgroundSize.height)) + transition.setAlpha(view: self.readingProgressView, alpha: isActiveWithText ? 0.0 : 1.0) } } diff --git a/submodules/BrowserUI/Sources/BrowserAddressListComponent.swift b/submodules/BrowserUI/Sources/BrowserAddressListComponent.swift index 1551fcf4..cd308ad4 100644 --- a/submodules/BrowserUI/Sources/BrowserAddressListComponent.swift +++ b/submodules/BrowserUI/Sources/BrowserAddressListComponent.swift @@ -19,6 +19,7 @@ final class BrowserAddressListComponent: Component { let insets: UIEdgeInsets let metrics: LayoutMetrics let addressBarFrame: CGRect + let navigationBarHeight: CGFloat let performAction: ActionSlot let presentInGlobalOverlay: (ViewController) -> Void @@ -29,6 +30,7 @@ final class BrowserAddressListComponent: Component { insets: UIEdgeInsets, metrics: LayoutMetrics, addressBarFrame: CGRect, + navigationBarHeight: CGFloat, performAction: ActionSlot, presentInGlobalOverlay: @escaping (ViewController) -> Void ) { @@ -38,6 +40,7 @@ final class BrowserAddressListComponent: Component { self.insets = insets self.metrics = metrics self.addressBarFrame = addressBarFrame + self.navigationBarHeight = navigationBarHeight self.performAction = performAction self.presentInGlobalOverlay = presentInGlobalOverlay } @@ -61,6 +64,9 @@ final class BrowserAddressListComponent: Component { if lhs.addressBarFrame != rhs.addressBarFrame { return false } + if lhs.navigationBarHeight != rhs.navigationBarHeight { + return false + } return true } @@ -214,15 +220,16 @@ final class BrowserAddressListComponent: Component { var validIds: [AnyHashable] = [] var validSectionHeaders: [AnyHashable] = [] var sectionOffset: CGFloat = 0.0 + let headerOffset: CGFloat = self.scrollView.frame.minY let sideInset: CGFloat = 0.0 - let containerInset: CGFloat = 0.0 + let containerInset: CGFloat = self.scrollView.frame.minY for sectionIndex in 0 ..< itemLayout.sections.count { let section = itemLayout.sections[sectionIndex] do { - var sectionHeaderFrame = CGRect(origin: CGPoint(x: sideInset, y: sectionOffset - self.scrollView.bounds.minY), size: CGSize(width: itemLayout.containerSize.width, height: section.insets.top)) + var sectionHeaderFrame = CGRect(origin: CGPoint(x: sideInset, y: headerOffset + sectionOffset - self.scrollView.bounds.minY), size: CGSize(width: itemLayout.containerSize.width, height: section.insets.top)) let sectionHeaderMinY = topOffset + containerInset let sectionHeaderMaxY = containerInset + sectionOffset - self.scrollView.bounds.minY + section.totalHeight - 28.0 @@ -540,7 +547,7 @@ final class BrowserAddressListComponent: Component { let containerFrame: CGRect if component.metrics.isTablet { let containerSize = CGSize(width: component.addressBarFrame.width + 32.0, height: 540.0) - containerFrame = CGRect(origin: CGPoint(x: floor(component.addressBarFrame.center.x - containerSize.width / 2.0), y: 72.0), size: containerSize) + containerFrame = CGRect(origin: CGPoint(x: floor(component.addressBarFrame.center.x - containerSize.width / 2.0), y: 100.0), size: containerSize) self.backgroundView.layer.cornerRadius = 10.0 } else { @@ -602,23 +609,28 @@ final class BrowserAddressListComponent: Component { let itemLayout = ItemLayout(containerSize: containerFrame.size, insets: .zero, sections: sections) self.itemLayout = itemLayout - let containerWidth = containerFrame.size.width let scrollContentHeight = max(itemLayout.contentHeight, containerFrame.size.height) self.ignoreScrolling = true - transition.setFrame(view: self.scrollView, frame: CGRect(origin: .zero, size: containerFrame.size)) - let contentSize = CGSize(width: containerWidth, height: scrollContentHeight) + let scrollFrame: CGRect + if component.metrics.isTablet { + scrollFrame = CGRect(origin: .zero, size: containerFrame.size) + } else { + scrollFrame = CGRect(origin: CGPoint(x: 0.0, y: component.navigationBarHeight), size: CGSize(width: containerFrame.size.width, height: containerFrame.size.height - component.navigationBarHeight)) + } + transition.setFrame(view: self.scrollView, frame: scrollFrame) + let contentSize = CGSize(width: scrollFrame.width, height: scrollContentHeight) if contentSize != self.scrollView.contentSize { self.scrollView.contentSize = contentSize } if resetScrolling { - self.scrollView.bounds = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: containerWidth, height: containerFrame.size.height)) + self.scrollView.bounds = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: scrollFrame.size) } self.ignoreScrolling = false self.updateScrolling(transition: transition) transition.setFrame(view: self.backgroundView, frame: containerFrame) - transition.setFrame(view: self.itemContainerView, frame: CGRect(origin: .zero, size: CGSize(width: containerWidth, height: scrollContentHeight))) + transition.setFrame(view: self.itemContainerView, frame: CGRect(origin: .zero, size: CGSize(width: scrollFrame.width, height: scrollContentHeight))) if component.metrics.isTablet { transition.setFrame(view: self.shadowView, frame: containerFrame.insetBy(dx: -60.0, dy: -60.0)) diff --git a/submodules/BrowserUI/Sources/BrowserAddressListItemComponent.swift b/submodules/BrowserUI/Sources/BrowserAddressListItemComponent.swift index f41a5395..49c9ece4 100644 --- a/submodules/BrowserUI/Sources/BrowserAddressListItemComponent.swift +++ b/submodules/BrowserUI/Sources/BrowserAddressListItemComponent.swift @@ -12,8 +12,15 @@ import AccountContext import ContextUI import UrlEscaping -private let iconFont = Font.with(size: 30.0, design: .round, weight: .bold) -private let iconTextBackgroundImage = generateStretchableFilledCircleImage(radius: 6.0, color: UIColor(rgb: 0xFF9500)) +private let iconFont = Font.with(size: 28.0, design: .round, weight: .bold) +private let iconTextBackgroundImage = generateImage(CGSize(width: 40.0, height: 40.0), contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(UIColor(rgb: 0xFF9500).cgColor) + + let path = UIBezierPath(roundedRect: CGRect(origin: .zero, size: size), cornerRadius: 12.0) + context.addPath(path.cgPath) + context.fillPath() +}) final class BrowserAddressListItemComponent: Component { let context: AccountContext @@ -261,7 +268,7 @@ final class BrowserAddressListItemComponent: Component { let iconImageLayout = self.icon.asyncLayout() var iconImageApply: (() -> Void)? if let iconImageReferenceAndRepresentation = iconImageReferenceAndRepresentation { - let imageCorners = ImageCorners(radius: 6.0) + let imageCorners = ImageCorners(radius: 12.0, curve: .continuous) let arguments = TransformImageArguments(corners: imageCorners, imageSize: iconImageReferenceAndRepresentation.1.dimensions.cgSize.aspectFilled(iconSize), boundingSize: iconSize, intrinsicInsets: UIEdgeInsets(), emptyColor: component.theme.list.mediaPlaceholderColor) iconImageApply = iconImageLayout(arguments) } diff --git a/submodules/BrowserUI/Sources/BrowserBookmarksScreen.swift b/submodules/BrowserUI/Sources/BrowserBookmarksScreen.swift index 274ce3b5..d5fa9632 100644 --- a/submodules/BrowserUI/Sources/BrowserBookmarksScreen.swift +++ b/submodules/BrowserUI/Sources/BrowserBookmarksScreen.swift @@ -18,6 +18,10 @@ import SearchBarNode import ChatHistorySearchContainerNode import ContextUI import UndoUI +import ComponentFlow +import EdgeEffect +import ButtonComponent +import BundleIconComponent public final class BrowserBookmarksScreen: ViewController { final class Node: ViewControllerTracingNode, ASScrollViewDelegate { @@ -196,7 +200,6 @@ public final class BrowserBookmarksScreen: ViewController { controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list( - search: false, reversed: false, reverseGroups: false, displayHeaders: .none, @@ -287,9 +290,9 @@ public final class BrowserBookmarksScreen: ViewController { } let tagMask: MessageTags = .webPage - self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, mode: .list, placeholder: self.presentationData.strings.Common_Search, hasBackground: true, contentNode: ChatHistorySearchContainerNode(context: self.context, peerId: self.context.account.peerId, threadId: nil, tagMask: tagMask, interfaceInteraction: self.controllerInteraction), cancel: { [weak self] in + self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, mode: .navigation, placeholder: self.presentationData.strings.Common_Search, hasBackground: true, contentNode: ChatHistorySearchContainerNode(context: self.context, peerId: self.context.account.peerId, threadId: nil, tagMask: tagMask, interfaceInteraction: self.controllerInteraction), cancel: { [weak self] in self?.controller?.deactivateSearch() - }) + }, fieldStyle: placeholderNode.fieldStyle) self.searchDisplayController?.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate) self.searchDisplayController?.activate(insertSubnode: { [weak self, weak placeholderNode] subnode, isSearchBar in @@ -372,7 +375,9 @@ public final class BrowserBookmarksScreen: ViewController { self.openUrl = openUrl self.addBookmark = addBookmark - super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData)) + super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData, style: .glass)) + + self._hasGlassStyle = true self.navigationPresentation = .modal self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) @@ -429,19 +434,13 @@ public final class BrowserBookmarksScreen: ViewController { searchContentNode.updateListVisibleContentOffset(offset) } } -// -// self.node.historyNode.didEndScrolling = { [weak self] _ in -// if let strongSelf = self, let searchContentNode = strongSelf.searchContentNode { -// let _ = fixNavigationSearchableListNodeScrolling(strongSelf.node.historyNode, searchNode: searchContentNode) -// } -// } self.displayNodeDidLoad() } private func updateThemeAndStrings() { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData, style: .glass), transition: .immediate) self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search) } @@ -484,10 +483,8 @@ private class BottomPanelNode: ASDisplayNode { private let strings: PresentationStrings private let action: () -> Void - private let separatorNode: ASDisplayNode - private let button: HighlightTrackingButtonNode - private let iconNode: ASImageNode - private let textNode: ImmediateTextNode + private let edgeEffectView = EdgeEffectView() + private let button = ComponentView() private var validLayout: (CGFloat, CGFloat, CGFloat)? @@ -496,49 +493,13 @@ private class BottomPanelNode: ASDisplayNode { self.strings = strings self.action = action - self.separatorNode = ASDisplayNode() - self.separatorNode.backgroundColor = theme.rootController.navigationBar.separatorColor - - self.iconNode = ASImageNode() - self.iconNode.displaysAsynchronously = false - self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/AddIcon"), color: theme.rootController.navigationBar.accentTextColor) - self.iconNode.isUserInteractionEnabled = false - - self.textNode = ImmediateTextNode() - self.textNode.displaysAsynchronously = false - self.textNode.attributedText = NSAttributedString(string: strings.WebBrowser_Bookmarks_BookmarkCurrent, font: Font.regular(17.0), textColor: theme.rootController.navigationBar.accentTextColor) - self.textNode.isUserInteractionEnabled = false - - self.button = HighlightTrackingButtonNode() - super.init() + } + + override func didLoad() { + super.didLoad() - self.backgroundColor = theme.rootController.navigationBar.opaqueBackgroundColor - - self.addSubnode(self.button) - self.addSubnode(self.separatorNode) - self.addSubnode(self.iconNode) - self.addSubnode(self.textNode) - self.addSubnode(self.button) - - self.button.highligthedChanged = { [weak self] highlighted in - if let self { - if highlighted { - self.iconNode.layer.removeAnimation(forKey: "opacity") - self.iconNode.alpha = 0.4 - - self.textNode.layer.removeAnimation(forKey: "opacity") - self.textNode.alpha = 0.4 - } else { - self.iconNode.alpha = 1.0 - self.iconNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) - - self.textNode.alpha = 1.0 - self.textNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) - } - } - } - self.button.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside) + self.view.addSubview(self.edgeEffectView) } @objc private func buttonPressed() { @@ -551,26 +512,76 @@ private class BottomPanelNode: ASDisplayNode { var bottomInset = bottomInset bottomInset += topInset - (bottomInset.isZero ? 0.0 : 4.0) - let buttonHeight: CGFloat = 40.0 - let textSize = self.textNode.updateLayout(CGSize(width: width, height: 44.0)) +// let buttonHeight: CGFloat = 40.0 +// let textSize = self.textNode.updateLayout(CGSize(width: width, height: 44.0)) +// +// let spacing: CGFloat = 8.0 +// var contentWidth = textSize.width +// var contentOriginX = floorToScreenPixels((width - contentWidth) / 2.0) +// if let icon = self.iconNode.image { +// contentWidth += icon.size.width + spacing +// contentOriginX = floorToScreenPixels((width - contentWidth) / 2.0) +// transition.updateFrame(node: self.iconNode, frame: CGRect(origin: CGPoint(x: contentOriginX, y: 12.0 + UIScreenPixel), size: icon.size)) +// contentOriginX += icon.size.width + spacing +// } +// let textFrame = CGRect(origin: CGPoint(x: contentOriginX, y: 17.0), size: textSize) +// transition.updateFrame(node: self.textNode, frame: textFrame) +// +// transition.updateFrame(node: self.button, frame: textFrame.insetBy(dx: -10.0, dy: -10.0)) +// +// transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: UIScreenPixel))) +// + + let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: bottomInset, innerDiameter: 52.0, sideInset: 30.0) + let height: CGFloat = 52.0 + buttonInsets.bottom - let spacing: CGFloat = 8.0 - var contentWidth = textSize.width - var contentOriginX = floorToScreenPixels((width - contentWidth) / 2.0) - if let icon = self.iconNode.image { - contentWidth += icon.size.width + spacing - contentOriginX = floorToScreenPixels((width - contentWidth) / 2.0) - transition.updateFrame(node: self.iconNode, frame: CGRect(origin: CGPoint(x: contentOriginX, y: 12.0 + UIScreenPixel), size: icon.size)) - contentOriginX += icon.size.width + spacing + let edgeEffectHeight: CGFloat = height + let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: height - edgeEffectHeight), size: CGSize(width: width, height: edgeEffectHeight)) + transition.updateFrame(view: self.edgeEffectView, frame: edgeEffectFrame) + self.edgeEffectView.update( + content: self.theme.list.plainBackgroundColor, + blur: true, + rect: edgeEffectFrame, + edge: .bottom, + edgeSize: edgeEffectFrame.height, + transition: ComponentTransition(transition) + ) + + var buttonItems: [AnyComponentWithIdentity] = [] + buttonItems.append(AnyComponentWithIdentity(id: "icon", component: AnyComponent(BundleIconComponent(name: "Chat/Context Menu/Fave", tintColor: self.theme.list.itemCheckColors.foregroundColor)))) + buttonItems.append(AnyComponentWithIdentity(id: "label", component: AnyComponent(Text(text: self.strings.WebBrowser_Bookmarks_BookmarkCurrent, font: Font.semibold(17.0), color: self.theme.list.itemCheckColors.foregroundColor)))) + + let buttonSize = self.button.update( + transition: .immediate, + component: AnyComponent( + ButtonComponent( + background: ButtonComponent.Background( + style: .glass, + color: self.theme.list.itemCheckColors.fillColor, + foreground: self.theme.list.itemCheckColors.foregroundColor, + pressedColor: self.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9) + ), + content: AnyComponentWithIdentity( + id: AnyHashable(0), + component: AnyComponent(HStack(buttonItems, spacing: 7.0)) + ), + action: { [weak self] in + self?.action() + } + ) + ), + environment: {}, + containerSize: CGSize(width: width - sideInset * 2.0 - buttonInsets.left - buttonInsets.right, height: 52.0) + ) + let buttonFrame = CGRect(origin: CGPoint(x: sideInset + buttonInsets.left, y: height - buttonInsets.bottom - buttonSize.height), size: buttonSize) + if let buttonView = self.button.view { + if buttonView.superview == nil { + self.view.addSubview(buttonView) + } + transition.updateFrame(view: buttonView, frame: buttonFrame) } - let textFrame = CGRect(origin: CGPoint(x: contentOriginX, y: 17.0), size: textSize) - transition.updateFrame(node: self.textNode, frame: textFrame) - transition.updateFrame(node: self.button, frame: textFrame.insetBy(dx: -10.0, dy: -10.0)) - - transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: UIScreenPixel))) - - return topInset + buttonHeight + bottomInset + return height } } diff --git a/submodules/BrowserUI/Sources/BrowserInstantPageContent.swift b/submodules/BrowserUI/Sources/BrowserInstantPageContent.swift index b09b444e..cb03ff6e 100644 --- a/submodules/BrowserUI/Sources/BrowserInstantPageContent.swift +++ b/submodules/BrowserUI/Sources/BrowserInstantPageContent.swift @@ -90,12 +90,21 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg private let updateLayoutDisposable = MetaDisposable() private let loadProgress = ValuePromise(1.0, ignoreRepeated: true) - private let readingProgress = ValuePromise(1.0, ignoreRepeated: true) + private let readingProgress = ValuePromise(0.0, ignoreRepeated: true) private var containerLayout: (size: CGSize, insets: UIEdgeInsets, fullInsets: UIEdgeInsets)? private var setupScrollOffsetOnLayout = false - init(context: AccountContext, presentationData: PresentationData, webPage: TelegramMediaWebpage, anchor: String?, url: String, sourceLocation: InstantPageSourceLocation, preloadedResouces: [Any]?, originalContent: BrowserContent? = nil) { + init( + context: AccountContext, + presentationData: PresentationData, + webPage: TelegramMediaWebpage, + anchor: String?, + url: String, + sourceLocation: InstantPageSourceLocation, + preloadedResouces: [Any]?, + originalContent: BrowserContent? = nil + ) { self.context = context var instantPage: InstantPage? if case let .Loaded(content) = webPage.content { @@ -132,6 +141,8 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg super.init(frame: .zero) + self.backgroundColor = self.theme.pageBackgroundColor + self.statePromise.set(.single(self._state) |> then( combineLatest( @@ -193,6 +204,8 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg self.presentationData = presentationData self.theme = instantPageThemeForType(presentationData.theme.overallDarkAppearance ? .dark : .light, settings: self.settings) + self.backgroundColor = self.theme.pageBackgroundColor + self.updatePageLayout() self.updateVisibleItems(visibleBounds: self.scrollNode.view.bounds) } @@ -415,8 +428,7 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg var updateVisibleItems = false let resetContentOffset = self.scrollNode.bounds.size.width.isZero || self.setupScrollOffsetOnLayout || !(self.initialAnchor ?? "").isEmpty - var scrollInsets = insets - scrollInsets.top = 0.0 + let scrollInsets = fullInsets if self.scrollNode.view.contentInset != scrollInsets { self.scrollNode.view.contentInset = scrollInsets self.scrollNode.view.scrollIndicatorInsets = scrollInsets @@ -424,7 +436,7 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg self.wrapperNode.frame = CGRect(origin: .zero, size: size) - let scrollFrame = CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: size.width, height: size.height - insets.top)) + let scrollFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)) let scrollFrameUpdated = self.scrollNode.bounds.size != scrollFrame.size if scrollFrameUpdated { let widthUpdated = self.scrollNode.bounds.size.width != scrollFrame.width diff --git a/submodules/BrowserUI/Sources/BrowserNavigationBarComponent.swift b/submodules/BrowserUI/Sources/BrowserNavigationBarComponent.swift index 89e0387e..26cc5ae1 100644 --- a/submodules/BrowserUI/Sources/BrowserNavigationBarComponent.swift +++ b/submodules/BrowserUI/Sources/BrowserNavigationBarComponent.swift @@ -2,8 +2,10 @@ import Foundation import UIKit import Display import ComponentFlow -import BlurredBackgroundComponent +import TelegramPresentationData import ContextUI +import GlassBackgroundComponent +import EdgeEffect final class BrowserNavigationBarEnvironment: Equatable { public let fraction: CGFloat @@ -20,7 +22,7 @@ final class BrowserNavigationBarEnvironment: Equatable { } } -final class BrowserNavigationBarComponent: CombinedComponent { +final class BrowserNavigationBarComponent: Component { public class ExternalState { public fileprivate(set) var centerItemFrame: CGRect @@ -29,11 +31,7 @@ final class BrowserNavigationBarComponent: CombinedComponent { } } - let backgroundColor: UIColor - let separatorColor: UIColor - let textColor: UIColor - let progressColor: UIColor - let accentColor: UIColor + let theme: PresentationTheme let topInset: CGFloat let height: CGFloat let sideInset: CGFloat @@ -42,17 +40,11 @@ final class BrowserNavigationBarComponent: CombinedComponent { let leftItems: [AnyComponentWithIdentity] let rightItems: [AnyComponentWithIdentity] let centerItem: AnyComponentWithIdentity? - let readingProgress: CGFloat - let loadingProgress: Double? let collapseFraction: CGFloat let activate: () -> Void init( - backgroundColor: UIColor, - separatorColor: UIColor, - textColor: UIColor, - progressColor: UIColor, - accentColor: UIColor, + theme: PresentationTheme, topInset: CGFloat, height: CGFloat, sideInset: CGFloat, @@ -61,16 +53,10 @@ final class BrowserNavigationBarComponent: CombinedComponent { leftItems: [AnyComponentWithIdentity], rightItems: [AnyComponentWithIdentity], centerItem: AnyComponentWithIdentity?, - readingProgress: CGFloat, - loadingProgress: Double?, collapseFraction: CGFloat, activate: @escaping () -> Void ) { - self.backgroundColor = backgroundColor - self.separatorColor = separatorColor - self.textColor = textColor - self.progressColor = progressColor - self.accentColor = accentColor + self.theme = theme self.topInset = topInset self.height = height self.sideInset = sideInset @@ -79,26 +65,12 @@ final class BrowserNavigationBarComponent: CombinedComponent { self.leftItems = leftItems self.rightItems = rightItems self.centerItem = centerItem - self.readingProgress = readingProgress - self.loadingProgress = loadingProgress self.collapseFraction = collapseFraction self.activate = activate } static func ==(lhs: BrowserNavigationBarComponent, rhs: BrowserNavigationBarComponent) -> Bool { - if lhs.backgroundColor != rhs.backgroundColor { - return false - } - if lhs.separatorColor != rhs.separatorColor { - return false - } - if lhs.textColor != rhs.textColor { - return false - } - if lhs.progressColor != rhs.progressColor { - return false - } - if lhs.accentColor != rhs.accentColor { + if lhs.theme !== rhs.theme { return false } if lhs.topInset != rhs.topInset { @@ -122,203 +94,353 @@ final class BrowserNavigationBarComponent: CombinedComponent { if lhs.centerItem != rhs.centerItem { return false } - if lhs.readingProgress != rhs.readingProgress { - return false - } - if lhs.loadingProgress != rhs.loadingProgress { - return false - } if lhs.collapseFraction != rhs.collapseFraction { return false } return true } - static var body: Body { - let background = Child(Rectangle.self) - let readingProgress = Child(Rectangle.self) - let separator = Child(Rectangle.self) - let loadingProgress = Child(LoadingProgressComponent.self) - let leftItems = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self) - let rightItems = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self) - let centerItems = ChildMap(environment: BrowserNavigationBarEnvironment.self, keyedBy: AnyHashable.self) - let activate = Child(Button.self) + final class View: UIView { + private var edgeEffectView = EdgeEffectView() + private let containerView = GlassBackgroundContainerView() + + private var leftItemsBackground: GlassBackgroundView? + private var leftItems: [AnyHashable: ComponentView] = [:] - return { context in - var availableWidth = context.availableSize.width - let sideInset: CGFloat = (context.component.metrics.isTablet ? 20.0 : 16.0) + context.component.sideInset + private var rightItemsBackground: GlassBackgroundView? + private var rightItems: [AnyHashable: ComponentView] = [:] + + private var centerItems: [AnyHashable: ComponentView] = [:] + + private let activateButton = HighlightTrackingButton() + + private var component: BrowserNavigationBarComponent? + private weak var state: EmptyComponentState? + + override init(frame: CGRect) { + super.init(frame: frame) - let collapsedHeight: CGFloat = 24.0 - let expandedHeight = context.component.height - let contentHeight: CGFloat = expandedHeight * (1.0 - context.component.collapseFraction) + collapsedHeight * context.component.collapseFraction - let size = CGSize(width: context.availableSize.width, height: context.component.topInset + contentHeight) - let verticalOffset: CGFloat = context.component.metrics.isTablet ? -2.0 : 0.0 - let itemSpacing: CGFloat = context.component.metrics.isTablet ? 26.0 : 8.0 + self.addSubview(self.edgeEffectView) - let background = background.update( - component: Rectangle(color: context.component.backgroundColor.withAlphaComponent(1.0)), - availableSize: CGSize(width: size.width, height: size.height), - transition: context.transition - ) - - let readingProgress = readingProgress.update( - component: Rectangle(color: context.component.progressColor), - availableSize: CGSize(width: size.width * context.component.readingProgress, height: size.height), - transition: context.transition - ) - - let separator = separator.update( - component: Rectangle(color: context.component.separatorColor, height: UIScreenPixel), - availableSize: CGSize(width: size.width, height: size.height), - transition: context.transition - ) - - let loadingProgressHeight: CGFloat = 2.0 - let loadingProgress = loadingProgress.update( - component: LoadingProgressComponent( - color: context.component.accentColor, - height: loadingProgressHeight, - value: context.component.loadingProgress ?? 0.0 - ), - availableSize: CGSize(width: size.width, height: size.height), - transition: context.transition - ) - - var leftItemList: [_UpdatedChildComponent] = [] - for item in context.component.leftItems { - let item = leftItems[item.id].update( - component: item.component, - availableSize: CGSize(width: availableWidth, height: expandedHeight), - transition: context.transition - ) - leftItemList.append(item) - availableWidth -= item.size.width - } - - var rightItemList: [_UpdatedChildComponent] = [] - for item in context.component.rightItems { - let item = rightItems[item.id].update( - component: item.component, - availableSize: CGSize(width: availableWidth, height: expandedHeight), - transition: context.transition - ) - rightItemList.append(item) - availableWidth -= item.size.width + self.addSubview(self.containerView) + self.activateButton.addTarget(self, action: #selector(self.activatePressed), for: .touchUpInside) + } + + required init?(coder: NSCoder) { + preconditionFailure() + } + + @objc private func activatePressed() { + guard let component = self.component else { + return } + component.activate() + } + + func update(component: BrowserNavigationBarComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + self.state = state + + var availableWidth = availableSize.width + let sideInset: CGFloat = (component.metrics.isTablet ? 20.0 : 16.0) + component.sideInset + + let collapsedHeight: CGFloat = 54.0 + let expandedHeight = component.height + let contentHeight: CGFloat = expandedHeight * (1.0 - component.collapseFraction) + collapsedHeight * component.collapseFraction + let size = CGSize(width: availableSize.width, height: component.topInset + contentHeight) + let verticalOffset: CGFloat = component.metrics.isTablet ? -2.0 : 0.0 + let itemSpacing: CGFloat = 0.0 //component.metrics.isTablet ? 26.0 : 8.0 + let panelHeight: CGFloat = 44.0 + + var leftItemsBackground: GlassBackgroundView? + var leftItemsBackgroundTransition = transition + if !component.leftItems.isEmpty { + if let current = self.leftItemsBackground { + leftItemsBackground = current + } else { + leftItemsBackgroundTransition = .immediate + leftItemsBackground = GlassBackgroundView() + self.containerView.contentView.addSubview(leftItemsBackground!) + self.leftItemsBackground = leftItemsBackground - context.add(background - .position(CGPoint(x: size.width / 2.0, y: size.height / 2.0)) - ) - - var readingProgressAlpha = context.component.collapseFraction - if leftItemList.isEmpty && rightItemList.isEmpty { - readingProgressAlpha = 0.0 + transition.animateScale(view: leftItemsBackground!, from: 0.1, to: 1.0) + transition.animateAlpha(view: leftItemsBackground!, from: 0.0, to: 1.0) + } } - context.add(readingProgress - .position(CGPoint(x: readingProgress.size.width / 2.0, y: size.height / 2.0)) - .opacity(readingProgressAlpha) - ) - context.add(separator - .position(CGPoint(x: size.width / 2.0, y: size.height)) - ) - - context.add(loadingProgress - .position(CGPoint(x: size.width / 2.0, y: size.height - loadingProgressHeight / 2.0)) - ) + var rightItemsBackground: GlassBackgroundView? + var rightItemsBackgroundTransition = transition + if !component.rightItems.isEmpty { + if let current = self.rightItemsBackground { + rightItemsBackground = current + } else { + rightItemsBackgroundTransition = .immediate + rightItemsBackground = GlassBackgroundView() + self.containerView.contentView.addSubview(rightItemsBackground!) + self.rightItemsBackground = rightItemsBackground + + transition.animateScale(view: rightItemsBackground!, from: 0.1, to: 1.0) + transition.animateAlpha(view: rightItemsBackground!, from: 0.0, to: 1.0) + } + } + + var validLeftItemIds: Set = Set() + var leftItemTransitions: [AnyHashable: (CGSize, ComponentTransition)] = [:] + var leftItemsWidth: CGFloat = 0.0 + for item in component.leftItems { + validLeftItemIds.insert(item.id) + var itemTransition = transition + let itemView: ComponentView + if let current = self.leftItems[item.id] { + itemView = current + } else { + itemTransition = .immediate + itemView = ComponentView() + self.leftItems[item.id] = itemView + } + + let itemSize = itemView.update( + transition: itemTransition, + component: item.component, + environment: {}, + containerSize: CGSize(width: availableWidth, height: expandedHeight) + ) + leftItemTransitions[item.id] = (itemSize, itemTransition) + availableWidth -= itemSize.width + leftItemsWidth += itemSize.width + } + + var validRightItemIds: Set = Set() + var rightItemTransitions: [AnyHashable: (CGSize, ComponentTransition)] = [:] + var rightItemsWidth: CGFloat = 0.0 + for item in component.rightItems { + validRightItemIds.insert(item.id) + var itemTransition = transition + let itemView: ComponentView + if let current = self.rightItems[item.id] { + itemView = current + } else { + itemTransition = .immediate + itemView = ComponentView() + self.rightItems[item.id] = itemView + } + + let itemSize = itemView.update( + transition: itemTransition, + component: item.component, + environment: {}, + containerSize: CGSize(width: availableWidth, height: expandedHeight) + ) + rightItemTransitions[item.id] = (itemSize, itemTransition) + availableWidth -= itemSize.width + rightItemsWidth += itemSize.width + } var centerLeftInset = sideInset - var leftItemX = sideInset - for item in leftItemList { - context.add(item - .position(CGPoint(x: leftItemX + item.size.width / 2.0 - (item.size.width / 2.0 * 0.35 * context.component.collapseFraction), y: context.component.topInset + contentHeight / 2.0 + verticalOffset)) - .scale(1.0 - 0.35 * context.component.collapseFraction) - .opacity(1.0 - context.component.collapseFraction) - .appear(.default(scale: true, alpha: true)) - .disappear(.default(scale: true, alpha: true)) - ) - leftItemX += item.size.width + itemSpacing - centerLeftInset += item.size.width + itemSpacing + var leftItemX = 0.0 + for item in component.leftItems { + guard let (itemSize, itemTransition) = leftItemTransitions[item.id], let itemView = self.leftItems[item.id]?.view else { + continue + } + let itemPosition = CGPoint(x: leftItemX + itemSize.width / 2.0, y: panelHeight * 0.5) + let itemFrame = CGRect(origin: CGPoint(x: itemPosition.x - itemSize.width * 0.5, y: itemPosition.y - itemSize.height * 0.5), size: itemSize) + if itemView.superview == nil { + leftItemsBackground?.contentView.addSubview(itemView) + transition.animateAlpha(view: itemView, from: 0.0, to: 1.0) + transition.animateScale(view: itemView, from: 0.01, to: 1.0) + } + itemTransition.setBounds(view: itemView, bounds: CGRect(origin: .zero, size: itemFrame.size)) + itemTransition.setPosition(view: itemView, position: itemFrame.center) + + leftItemX += itemSize.width + itemSpacing + centerLeftInset += itemSize.width + itemSpacing } - - var centerRightInset = sideInset - 5.0 - var rightItemX = context.availableSize.width - (sideInset - 5.0) - for item in rightItemList.reversed() { - context.add(item - .position(CGPoint(x: rightItemX - item.size.width / 2.0 + (item.size.width / 2.0 * 0.35 * context.component.collapseFraction), y: context.component.topInset + contentHeight / 2.0 + verticalOffset)) - .scale(1.0 - 0.35 * context.component.collapseFraction) - .opacity(1.0 - context.component.collapseFraction) - .appear(.default(scale: true, alpha: true)) - .disappear(.default(scale: true, alpha: true)) - ) - rightItemX -= item.size.width + itemSpacing - centerRightInset += item.size.width + itemSpacing + + var centerRightInset = sideInset + var rightItemX = rightItemsWidth + for item in component.rightItems.reversed() { + guard let (itemSize, itemTransition) = rightItemTransitions[item.id], let itemView = self.rightItems[item.id]?.view else { + continue + } + let itemPosition = CGPoint(x: rightItemX - itemSize.width / 2.0, y: panelHeight * 0.5) + let itemFrame = CGRect(origin: CGPoint(x: itemPosition.x - itemSize.width * 0.5, y: itemPosition.y - itemSize.height * 0.5), size: itemSize) + if itemView.superview == nil { + rightItemsBackground?.contentView.addSubview(itemView) + transition.animateAlpha(view: itemView, from: 0.0, to: 1.0) + transition.animateScale(view: itemView, from: 0.01, to: 1.0) + } + itemTransition.setBounds(view: itemView, bounds: CGRect(origin: .zero, size: itemFrame.size)) + itemTransition.setPosition(view: itemView, position: itemFrame.center) + itemTransition.setScale(view: itemView, scale: 1.0 - 0.35 * component.collapseFraction) + itemTransition.setAlpha(view: itemView, alpha: 1.0 - component.collapseFraction) + + rightItemX -= itemSize.width + itemSpacing + centerRightInset += itemSize.width + itemSpacing + } + + if let leftItemsBackground { + let leftItemsFrame = CGRect(origin: CGPoint(x: sideInset - (leftItemsWidth / 2.0 * 0.35 * component.collapseFraction), y: component.topInset + contentHeight / 2.0 + verticalOffset - panelHeight / 2.0), size: CGSize(width: leftItemsWidth, height: panelHeight)) + leftItemsBackgroundTransition.setFrame(view: leftItemsBackground, frame: leftItemsFrame) + leftItemsBackground.update(size: leftItemsFrame.size, shape: .roundedRect(cornerRadius: leftItemsFrame.height * 0.5), isDark: component.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: component.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: leftItemsBackgroundTransition) + + leftItemsBackgroundTransition.setScale(view: leftItemsBackground, scale: 1.0 - 0.999 * component.collapseFraction) + leftItemsBackgroundTransition.setAlpha(view: leftItemsBackground.contentView, alpha: 1.0 - component.collapseFraction) + } else if let leftItemsBackground = self.leftItemsBackground { + self.leftItemsBackground = nil + leftItemsBackground.removeFromSuperview() + } + + if let rightItemsBackground { + let rightItemsFrame = CGRect(origin: CGPoint(x: availableSize.width - sideInset - rightItemsWidth * (1.0 - component.collapseFraction) + (rightItemsWidth / 2.0 * 0.35 * component.collapseFraction), y: component.topInset + contentHeight / 2.0 + verticalOffset - panelHeight / 2.0), size: CGSize(width: rightItemsWidth, height: panelHeight)) + rightItemsBackgroundTransition.setFrame(view: rightItemsBackground, frame: rightItemsFrame) + rightItemsBackground.update(size: rightItemsFrame.size, shape: .roundedRect(cornerRadius: rightItemsFrame.height * 0.5), isDark: component.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: component.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: rightItemsBackgroundTransition) + + rightItemsBackgroundTransition.setScale(view: rightItemsBackground, scale: 1.0 - 0.999 * component.collapseFraction) + rightItemsBackgroundTransition.setAlpha(view: rightItemsBackground.contentView, alpha: 1.0 - component.collapseFraction) + } else if let rightItemsBackground = self.rightItemsBackground { + self.rightItemsBackground = nil + rightItemsBackground.removeFromSuperview() + } + + var removeLeftItemIds: [AnyHashable] = [] + for (id, item) in self.leftItems { + if !validLeftItemIds.contains(id) { + removeLeftItemIds.append(id) + if let itemView = item.view { + transition.setScale(view: itemView, scale: 0.01) + transition.setAlpha(view: itemView, alpha: 0.0, completion: { _ in + itemView.removeFromSuperview() + }) + } + } + } + for id in removeLeftItemIds { + self.leftItems.removeValue(forKey: id) + } + + var removeRightItemIds: [AnyHashable] = [] + for (id, item) in self.rightItems { + if !validRightItemIds.contains(id) { + removeRightItemIds.append(id) + if let itemView = item.view { + transition.setScale(view: itemView, scale: 0.01) + transition.setAlpha(view: itemView, alpha: 0.0, completion: { _ in + itemView.removeFromSuperview() + }) + } + } + } + for id in removeRightItemIds { + self.rightItems.removeValue(forKey: id) } let maxCenterInset = max(centerLeftInset, centerRightInset) - if !leftItemList.isEmpty || !rightItemList.isEmpty { - availableWidth -= itemSpacing * CGFloat(max(0, leftItemList.count - 1)) + itemSpacing * CGFloat(max(0, rightItemList.count - 1)) + 30.0 + if !component.leftItems.isEmpty || !component.rightItems.isEmpty { + availableWidth -= itemSpacing * CGFloat(max(0, component.leftItems.count - 1)) + itemSpacing * CGFloat(max(0, component.rightItems.count - 1)) + 30.0 } - availableWidth -= context.component.sideInset * 2.0 + availableWidth -= component.sideInset * 2.0 - let canCenter = availableWidth > 660.0 - availableWidth = min(660.0, availableWidth) + let canCenter = availableWidth > 390.0 + availableWidth = min(390.0, availableWidth) - let environment = BrowserNavigationBarEnvironment(fraction: context.component.collapseFraction) + let environment = BrowserNavigationBarEnvironment(fraction: component.collapseFraction) - let centerItem = context.component.centerItem.flatMap { item in - centerItems[item.id].update( + var centerX = maxCenterInset + (availableSize.width - maxCenterInset * 2.0) / 2.0 + if canCenter { + centerX = availableSize.width / 2.0 + } else { + centerX = centerLeftInset + (availableSize.width - centerLeftInset - centerRightInset) / 2.0 + } + + var validCenterItemIds: Set = Set() + if let item = component.centerItem { + validCenterItemIds.insert(item.id) + + var itemTransition = transition + let itemView: ComponentView + if let current = self.centerItems[item.id] { + itemView = current + } else { + itemTransition = .immediate + itemView = ComponentView() + self.centerItems[item.id] = itemView + } + + let itemSize = itemView.update( + transition: itemTransition, component: item.component, environment: { environment }, - availableSize: CGSize(width: availableWidth, height: expandedHeight), - transition: context.transition - ) - } - - var centerX = maxCenterInset + (context.availableSize.width - maxCenterInset * 2.0) / 2.0 - if "".isEmpty { - if canCenter { - centerX = context.availableSize.width / 2.0 - } else { - centerX = centerLeftInset + (context.availableSize.width - centerLeftInset - centerRightInset) / 2.0 - } - } - if let centerItem = centerItem { - let centerItemPosition = CGPoint(x: centerX, y: context.component.topInset + contentHeight / 2.0 + verticalOffset) - context.add(centerItem - .position(centerItemPosition) - .scale(1.0 - 0.35 * context.component.collapseFraction) - .appear(.default(scale: false, alpha: true)) - .disappear(.default(scale: false, alpha: true)) + containerSize: CGSize(width: availableWidth, height: expandedHeight) ) - context.component.externalState?.centerItemFrame = centerItem.size.centered(around: centerItemPosition) + let itemPosition = CGPoint(x: centerX, y: component.topInset + contentHeight / 2.0 + verticalOffset) + let itemFrame = CGRect(origin: CGPoint(x: itemPosition.x - itemSize.width * 0.5, y: itemPosition.y - itemSize.height * 0.5), size: itemSize) + if let itemView = itemView.view { + if itemView.superview == nil { + self.containerView.contentView.addSubview(itemView) + transition.animateAlpha(view: itemView, from: 0.0, to: 1.0) + } + itemTransition.setBounds(view: itemView, bounds: CGRect(origin: .zero, size: itemFrame.size)) + itemTransition.setPosition(view: itemView, position: itemFrame.center) + itemTransition.setScale(view: itemView, scale: 1.0 - 0.25 * component.collapseFraction) + } + component.externalState?.centerItemFrame = itemFrame } - if context.component.collapseFraction == 1.0 { - let activateAction = context.component.activate - let activate = activate.update( - component: Button( - content: AnyComponent(Rectangle(color: UIColor(rgb: 0x000000, alpha: 0.001))), - action: { - activateAction() - } - ), - availableSize: size, - transition: .immediate - ) - context.add(activate - .position(CGPoint(x: size.width / 2.0, y: size.height / 2.0)) - ) + var removeCenterItemIds: [AnyHashable] = [] + for (id, item) in self.centerItems { + if !validCenterItemIds.contains(id) { + removeCenterItemIds.append(id) + if let itemView = item.view { + transition.setAlpha(view: itemView, alpha: 0.0, completion: { _ in + itemView.removeFromSuperview() + }) + } + } } + for id in removeCenterItemIds { + self.centerItems.removeValue(forKey: id) + } + + if component.collapseFraction == 1.0 { + if self.activateButton.superview == nil { + self.addSubview(self.activateButton) + } + self.activateButton.frame = CGRect(origin: .zero, size: size) + } else { + self.activateButton.removeFromSuperview() + } + + self.containerView.update(size: size, isDark: component.theme.overallDarkAppearance, transition: transition) + transition.setFrame(view: self.containerView, frame: CGRect(origin: .zero, size: size)) + + let edgeEffectHeight: CGFloat = 80.0 + let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: edgeEffectHeight)) + transition.setFrame(view: self.edgeEffectView, frame: edgeEffectFrame) + self.edgeEffectView.update( + content: .clear, + blur: true, + rect: edgeEffectFrame, + edge: .top, + edgeSize: edgeEffectFrame.height, + transition: transition + ) return size } } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } } -private final class LoadingProgressComponent: Component { +final class LoadingProgressComponent: Component { let color: UIColor let height: CGFloat let value: CGFloat @@ -414,111 +536,3 @@ private final class LoadingProgressComponent: Component { return view.update(component: self, availableSize: availableSize, transition: transition) } } - -final class ReferenceButtonComponent: Component { - let content: AnyComponent - let tag: AnyObject? - let action: () -> Void - - init( - content: AnyComponent, - tag: AnyObject? = nil, - action: @escaping () -> Void - ) { - self.content = content - self.tag = tag - self.action = action - } - - static func ==(lhs: ReferenceButtonComponent, rhs: ReferenceButtonComponent) -> Bool { - if lhs.content != rhs.content { - return false - } - if lhs.tag !== rhs.tag { - return false - } - return true - } - - final class View: HighlightTrackingButton, ComponentTaggedView { - private let sourceView: ContextControllerSourceView - let referenceNode: ContextReferenceContentNode - let componentView: ComponentView - - private var component: ReferenceButtonComponent? - - public func matches(tag: Any) -> Bool { - if let component = self.component, let componentTag = component.tag { - let tag = tag as AnyObject - if componentTag === tag { - return true - } - } - return false - } - - init() { - self.componentView = ComponentView() - self.sourceView = ContextControllerSourceView() - self.sourceView.animateScale = false - self.referenceNode = ContextReferenceContentNode() - - super.init(frame: CGRect()) - - self.sourceView.isUserInteractionEnabled = false - self.addSubview(self.sourceView) - self.sourceView.addSubnode(self.referenceNode) - - self.highligthedChanged = { [weak self] highlighted in - if let strongSelf = self, let contentView = strongSelf.componentView.view { - if highlighted { - contentView.layer.removeAnimation(forKey: "opacity") - contentView.alpha = 0.4 - } else { - contentView.alpha = 1.0 - contentView.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) - } - } - } - self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside) - } - - required init?(coder aDecoder: NSCoder) { - preconditionFailure() - } - - @objc private func pressed() { - self.component?.action() - } - - func update(component: ReferenceButtonComponent, availableSize: CGSize, transition: ComponentTransition) -> CGSize { - self.component = component - - let componentSize = self.componentView.update( - transition: transition, - component: component.content, - environment: {}, - containerSize: availableSize - ) - if let componentView = self.componentView.view { - if componentView.superview == nil { - self.referenceNode.view.addSubview(componentView) - } - transition.setFrame(view: componentView, frame: CGRect(origin: .zero, size: componentSize)) - } - - transition.setFrame(view: self.sourceView, frame: CGRect(origin: .zero, size: componentSize)) - self.referenceNode.frame = CGRect(origin: .zero, size: componentSize) - - return componentSize - } - } - - func makeView() -> View { - return View() - } - - func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { - return view.update(component: self, availableSize: availableSize, transition: transition) - } -} diff --git a/submodules/BrowserUI/Sources/BrowserPdfContent.swift b/submodules/BrowserUI/Sources/BrowserPdfContent.swift index 8cad6151..be32b96e 100644 --- a/submodules/BrowserUI/Sources/BrowserPdfContent.swift +++ b/submodules/BrowserUI/Sources/BrowserPdfContent.swift @@ -16,6 +16,7 @@ import ShareController import UndoUI import UrlEscaping import PDFKit +import GlassBackgroundComponent final class BrowserPdfContent: UIView, BrowserContent, UIScrollViewDelegate, PDFDocumentDelegate { private let context: AccountContext @@ -25,7 +26,7 @@ final class BrowserPdfContent: UIView, BrowserContent, UIScrollViewDelegate, PDF private let pdfView: PDFView private let scrollView: UIScrollView! - private let pageIndicatorBackgorund: UIVisualEffectView + private let pageIndicatorBackground = GlassBackgroundView() private let pageIndicator = ComponentView() private var pageNumber: (Int, Int)? private var pageTimer: SwiftSignalKit.Timer? @@ -61,11 +62,7 @@ final class BrowserPdfContent: UIView, BrowserContent, UIScrollViewDelegate, PDF self.pdfView = PDFView() self.pdfView.clipsToBounds = false - - self.pageIndicatorBackgorund = UIVisualEffectView(effect: UIBlurEffect(style: .light)) - self.pageIndicatorBackgorund.clipsToBounds = true - self.pageIndicatorBackgorund.layer.cornerRadius = 10.0 - + var scrollView: UIScrollView? for view in self.pdfView.subviews { if let view = view as? UIScrollView { @@ -170,7 +167,7 @@ final class BrowserPdfContent: UIView, BrowserContent, UIScrollViewDelegate, PDF return } let transition = ComponentTransition.easeInOut(duration: 0.25) - transition.setAlpha(view: self.pageIndicatorBackgorund, alpha: 0.0) + transition.setAlpha(view: self.pageIndicatorBackground, alpha: 0.0) }, queue: Queue.mainQueue()) self.pageTimer?.start() } @@ -354,7 +351,7 @@ final class BrowserPdfContent: UIView, BrowserContent, UIScrollViewDelegate, PDF self.validLayout = (size, insets, fullInsets) self.previousScrollingOffset = ScrollingOffsetState(value: self.scrollView.contentOffset.y, isDraggingOrDecelerating: self.scrollView.isDragging || self.scrollView.isDecelerating) - + let currentBounds = self.scrollView.bounds let offsetToBottomEdge = max(0.0, self.scrollView.contentSize.height - currentBounds.maxY) var bottomInset = insets.bottom @@ -368,23 +365,24 @@ final class BrowserPdfContent: UIView, BrowserContent, UIScrollViewDelegate, PDF let pageIndicatorSize = self.pageIndicator.update( transition: .immediate, component: AnyComponent( - Text(text: "\(self.pageNumber?.0 ?? 1) of \(self.pageNumber?.1 ?? 1)", font: Font.with(size: 15.0, weight: .semibold, traits: .monospacedNumbers), color: self.presentationData.theme.list.itemSecondaryTextColor) + Text(text: "\(self.pageNumber?.0 ?? 1) of \(self.pageNumber?.1 ?? 1)", font: Font.with(size: 15.0, weight: .regular, traits: .monospacedNumbers), color: self.presentationData.theme.list.itemPrimaryTextColor) ), environment: {}, containerSize: size ) if let view = self.pageIndicator.view { if view.superview == nil { - self.addSubview(self.pageIndicatorBackgorund) - self.pageIndicatorBackgorund.contentView.addSubview(view) + self.addSubview(self.pageIndicatorBackground) + self.pageIndicatorBackground.contentView.addSubview(view) } - + let horizontalPadding: CGFloat = 10.0 let verticalPadding: CGFloat = 8.0 - let pageBackgroundFrame = CGRect(origin: CGPoint(x: insets.left + 20.0, y: insets.top + 16.0), size: CGSize(width: horizontalPadding * 2.0 + pageIndicatorSize.width, height: verticalPadding * 2.0 + pageIndicatorSize.height)) + let pageBackgroundFrame = CGRect(origin: CGPoint(x: insets.left + 16.0, y: insets.top + 16.0), size: CGSize(width: horizontalPadding * 2.0 + pageIndicatorSize.width, height: verticalPadding * 2.0 + pageIndicatorSize.height)) - self.pageIndicatorBackgorund.bounds = CGRect(origin: .zero, size: pageBackgroundFrame.size) - transition.setPosition(view: self.pageIndicatorBackgorund, position: pageBackgroundFrame.center) + self.pageIndicatorBackground.update(size: pageBackgroundFrame.size, cornerRadius: pageBackgroundFrame.size.height * 0.5, isDark: self.presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.presentationData.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), transition: transition) + self.pageIndicatorBackground.bounds = CGRect(origin: .zero, size: pageBackgroundFrame.size) + transition.setPosition(view: self.pageIndicatorBackground, position: pageBackgroundFrame.center) view.frame = CGRect(origin: CGPoint(x: horizontalPadding, y: verticalPadding), size: pageIndicatorSize) } @@ -459,7 +457,7 @@ final class BrowserPdfContent: UIView, BrowserContent, UIScrollViewDelegate, PDF } let transition = ComponentTransition.easeInOut(duration: 0.1) - transition.setAlpha(view: self.pageIndicatorBackgorund, alpha: 1.0) + transition.setAlpha(view: self.pageIndicatorBackground, alpha: 1.0) self.pageTimer?.invalidate() self.pageTimer = nil diff --git a/submodules/BrowserUI/Sources/BrowserScreen.swift b/submodules/BrowserUI/Sources/BrowserScreen.swift index aeb2e445..9eac12ee 100644 --- a/submodules/BrowserUI/Sources/BrowserScreen.swift +++ b/submodules/BrowserUI/Sources/BrowserScreen.swift @@ -20,6 +20,7 @@ import InstantPageUI import NavigationStackComponent import LottieComponent import WebKit +import GlassBarButtonComponent private let settingsTag = GenericComponentViewTag() @@ -85,6 +86,8 @@ private final class BrowserScreenComponent: CombinedComponent { let navigationBarExternalState = BrowserNavigationBarComponent.ExternalState() + let moreButtonPlayOnce = ActionSlot() + return { context in let environment = context.environment[ViewControllerComponentContainer.Environment.self].value let performAction = context.component.performAction @@ -123,6 +126,8 @@ private final class BrowserScreenComponent: CombinedComponent { url: context.component.contentState?.url ?? "", isSecure: context.component.contentState?.isSecure ?? false, isExpanded: context.component.presentationState.addressFocused, + readingProgress: context.component.contentState?.readingProgress ?? 0.0, + loadingProgress: context.component.contentState?.estimatedProgress, performAction: performAction ) ) @@ -134,7 +139,9 @@ private final class BrowserScreenComponent: CombinedComponent { component: AnyComponent( TitleBarContentComponent( theme: environment.theme, - title: title + title: title, + readingProgress: context.component.contentState?.readingProgress ?? 0.0, + loadingProgress: context.component.contentState?.estimatedProgress ) ) ) @@ -150,37 +157,40 @@ private final class BrowserScreenComponent: CombinedComponent { component: AnyComponent( Button( content: AnyComponent( - MultilineTextComponent(text: .plain(NSAttributedString(string: environment.strings.WebBrowser_Done, font: Font.semibold(17.0), textColor: environment.theme.rootController.navigationBar.accentTextColor, paragraphAlignment: .center)), horizontalAlignment: .left, maximumNumberOfLines: 1) + BundleIconComponent( + name: "Navigation/Close", + tintColor: environment.theme.chat.inputPanel.panelControlColor + ) ), action: { performAction.invoke(.close) } - ) + ).minSize(CGSize(width: 44.0, height: 44.0)) ) ) ] if isTablet { - #if DEBUG - navigationLeftItems.append( - AnyComponentWithIdentity( - id: "minimize", - component: AnyComponent( - Button( - content: AnyComponent( - BundleIconComponent( - name: "Media Gallery/PictureInPictureButton", - tintColor: environment.theme.rootController.navigationBar.accentTextColor - ) - ), - action: { - performAction.invoke(.close) - } - ) - ) - ) - ) - #endif +// #if DEBUG +// navigationLeftItems.append( +// AnyComponentWithIdentity( +// id: "minimize", +// component: AnyComponent( +// Button( +// content: AnyComponent( +// BundleIconComponent( +// name: "Media Gallery/PictureInPictureButton", +// tintColor: environment.theme.rootController.navigationBar.accentTextColor +// ) +// ), +// action: { +// performAction.invoke(.close) +// } +// ) +// ) +// ) +// ) +// #endif let canGoBack = context.component.contentState?.canGoBack ?? false let canGoForward = context.component.contentState?.canGoForward ?? false @@ -193,13 +203,13 @@ private final class BrowserScreenComponent: CombinedComponent { content: AnyComponent( BundleIconComponent( name: "Instant View/Back", - tintColor: environment.theme.rootController.navigationBar.accentTextColor.withAlphaComponent(canGoBack ? 1.0 : 0.4) + tintColor: environment.theme.chat.inputPanel.panelControlColor.withAlphaComponent(canGoBack ? 1.0 : 0.4) ) ), action: { performAction.invoke(.navigateBack) } - ) + ).minSize(CGSize(width: 44.0, height: 44.0)) ) ) ) @@ -212,13 +222,13 @@ private final class BrowserScreenComponent: CombinedComponent { content: AnyComponent( BundleIconComponent( name: "Instant View/Forward", - tintColor: environment.theme.rootController.navigationBar.accentTextColor.withAlphaComponent(canGoForward ? 1.0 : 0.4) + tintColor: environment.theme.chat.inputPanel.panelControlColor.withAlphaComponent(canGoForward ? 1.0 : 0.4) ) ), action: { performAction.invoke(.navigateForward) } - ) + ).minSize(CGSize(width: 44.0, height: 44.0)) ) ) ) @@ -228,21 +238,22 @@ private final class BrowserScreenComponent: CombinedComponent { AnyComponentWithIdentity( id: "settings", component: AnyComponent( - ReferenceButtonComponent( + Button( content: AnyComponent( LottieComponent( content: LottieComponent.AppBundleContent( - name: "anim_moredots" + name: "anim_morewide" ), - color: environment.theme.rootController.navigationBar.accentTextColor, - size: CGSize(width: 30.0, height: 30.0) + color: environment.theme.chat.inputPanel.panelControlColor, + size: CGSize(width: 34.0, height: 34.0), + playOnce: moreButtonPlayOnce ) ), - tag: settingsTag, action: { performAction.invoke(.openSettings) + moreButtonPlayOnce.invoke(Void()) } - ) + ).minSize(CGSize(width: 44.0, height: 44.0)).tagged(settingsTag) ) ) ] @@ -256,13 +267,13 @@ private final class BrowserScreenComponent: CombinedComponent { content: AnyComponent( BundleIconComponent( name: "Instant View/Bookmark", - tintColor: environment.theme.rootController.navigationBar.accentTextColor + tintColor: environment.theme.chat.inputPanel.panelControlColor ) ), action: { performAction.invoke(.openBookmarks) } - ) + ).minSize(CGSize(width: 44.0, height: 44.0)) ) ), at: 0 @@ -275,14 +286,14 @@ private final class BrowserScreenComponent: CombinedComponent { Button( content: AnyComponent( BundleIconComponent( - name: "Chat List/NavigationShare", - tintColor: environment.theme.rootController.navigationBar.accentTextColor + name: "Instant View/Share", + tintColor: environment.theme.chat.inputPanel.panelControlColor ) ), action: { performAction.invoke(.share) } - ) + ).minSize(CGSize(width: 44.0, height: 44.0)) ) ), at: 0 @@ -297,13 +308,13 @@ private final class BrowserScreenComponent: CombinedComponent { content: AnyComponent( BundleIconComponent( name: "Instant View/Browser", - tintColor: environment.theme.rootController.navigationBar.accentTextColor + tintColor: environment.theme.chat.inputPanel.panelControlColor ) ), action: { performAction.invoke(.openIn) } - ) + ).minSize(CGSize(width: 44.0, height: 44.0)) ) ) ) @@ -316,21 +327,15 @@ private final class BrowserScreenComponent: CombinedComponent { let navigationBar = navigationBar.update( component: BrowserNavigationBarComponent( - backgroundColor: environment.theme.rootController.navigationBar.blurredBackgroundColor, - separatorColor: environment.theme.rootController.navigationBar.separatorColor, - textColor: environment.theme.rootController.navigationBar.primaryTextColor, - progressColor: environment.theme.rootController.navigationBar.segmentedBackgroundColor, - accentColor: environment.theme.rootController.navigationBar.accentTextColor, + theme: environment.theme, topInset: environment.statusBarHeight, - height: environment.navigationHeight - environment.statusBarHeight, + height: environment.navigationHeight - environment.statusBarHeight + 8.0, sideInset: environment.safeInsets.left, metrics: environment.metrics, externalState: navigationBarExternalState, leftItems: navigationLeftItems, rightItems: navigationRightItems, centerItem: navigationContent, - readingProgress: context.component.contentState?.readingProgress ?? 0.0, - loadingProgress: context.component.contentState?.estimatedProgress, collapseFraction: collapseFraction, activate: { performAction.invoke(.expand) @@ -339,9 +344,6 @@ private final class BrowserScreenComponent: CombinedComponent { availableSize: context.availableSize, transition: context.transition ) - context.add(navigationBar - .position(CGPoint(x: context.availableSize.width / 2.0, y: navigationBar.size.height / 2.0)) - ) let toolbarContent: AnyComponentWithIdentity? if context.component.presentationState.isSearching { @@ -349,8 +351,8 @@ private final class BrowserScreenComponent: CombinedComponent { id: "search", component: AnyComponent( SearchToolbarContentComponent( + theme: environment.theme, strings: environment.strings, - textColor: environment.theme.rootController.navigationBar.primaryTextColor, index: context.component.presentationState.searchResultIndex, count: context.component.presentationState.searchResultCount, isEmpty: context.component.presentationState.searchQueryIsEmpty, @@ -363,8 +365,7 @@ private final class BrowserScreenComponent: CombinedComponent { id: "navigation", component: AnyComponent( NavigationToolbarContentComponent( - accentColor: environment.theme.rootController.navigationBar.accentTextColor, - textColor: environment.theme.rootController.navigationBar.primaryTextColor, + theme: environment.theme, canGoBack: context.component.contentState?.canGoBack ?? false, canGoForward: context.component.contentState?.canGoForward ?? false, canOpenIn: canOpenIn, @@ -384,15 +385,12 @@ private final class BrowserScreenComponent: CombinedComponent { toolbarBottomInset = environment.safeInsets.bottom } - var toolbarSize: CGFloat = 0.0 if isTablet && !context.component.presentationState.isSearching { } else { let toolbar = toolbar.update( component: BrowserToolbarComponent( - backgroundColor: environment.theme.rootController.navigationBar.blurredBackgroundColor, - separatorColor: environment.theme.rootController.navigationBar.separatorColor, - textColor: environment.theme.rootController.navigationBar.primaryTextColor, + theme: environment.theme, bottomInset: toolbarBottomInset, sideInset: environment.safeInsets.left, item: toolbarContent, @@ -412,16 +410,9 @@ private final class BrowserScreenComponent: CombinedComponent { }) }) ) - toolbarSize = toolbar.size.height } if context.component.presentationState.addressFocused { - let addressListSize: CGSize - if isTablet { - addressListSize = context.availableSize - } else { - addressListSize = CGSize(width: context.availableSize.width, height: context.availableSize.height - navigationBar.size.height - toolbarSize) - } let controller = environment.controller let addressList = addressList.update( component: BrowserAddressListComponent( @@ -431,12 +422,13 @@ private final class BrowserScreenComponent: CombinedComponent { insets: UIEdgeInsets(top: 0.0, left: environment.safeInsets.left, bottom: 0.0, right: environment.safeInsets.right), metrics: environment.metrics, addressBarFrame: navigationBarExternalState.centerItemFrame, + navigationBarHeight: navigationBar.size.height, performAction: performAction, presentInGlobalOverlay: { c in controller()?.presentInGlobalOverlay(c) } ), - availableSize: addressListSize, + availableSize: context.availableSize, transition: context.transition ) @@ -448,7 +440,7 @@ private final class BrowserScreenComponent: CombinedComponent { ) } else { context.add(addressList - .position(CGPoint(x: context.availableSize.width / 2.0, y: navigationBar.size.height + addressList.size.height / 2.0)) + .position(CGPoint(x: context.availableSize.width / 2.0, y: addressList.size.height / 2.0)) .clipsToBounds(true) .appear(.default(alpha: true)) .disappear(.default(alpha: true)) @@ -456,6 +448,10 @@ private final class BrowserScreenComponent: CombinedComponent { } } + context.add(navigationBar + .position(CGPoint(x: context.availableSize.width / 2.0, y: navigationBar.size.height / 2.0)) + ) + return context.availableSize } } @@ -1075,7 +1071,7 @@ public class BrowserScreen: ViewController, MinimizableController { } func openSettings() { - guard let referenceView = self.componentHost.findTaggedView(tag: settingsTag) as? ReferenceButtonComponent.View else { + guard let referenceView = self.componentHost.findTaggedView(tag: settingsTag) else { return } @@ -1083,10 +1079,6 @@ public class BrowserScreen: ViewController, MinimizableController { return } - if let animationComponentView = referenceView.componentView.view as? LottieComponent.View { - animationComponentView.playOnce() - } - if let webContent = content as? BrowserWebContent { webContent.requestInstantView() } @@ -1103,7 +1095,7 @@ public class BrowserScreen: ViewController, MinimizableController { } } - let source: ContextContentSource = .reference(BrowserReferenceContentSource(controller: controller, sourceView: referenceView.referenceNode.view)) + let source: ContextContentSource = .reference(BrowserReferenceContentSource(controller: controller, sourceView: referenceView)) let items: Signal = combineLatest( queue: Queue.mainQueue(), @@ -1549,6 +1541,8 @@ public class BrowserScreen: ViewController, MinimizableController { super.init(navigationBarPresentationData: nil) + self._hasGlassStyle = true + self.navigationPresentation = .modalInCompactLayout self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .allButUpsideDown) @@ -1731,7 +1725,7 @@ private final class BrowserContentComponent: Component { self.addSubview(component.content) } - let collapsedHeight: CGFloat = 24.0 + let collapsedHeight: CGFloat = 54.0 let topInset: CGFloat = component.navigationBarHeight * (1.0 - component.scrollingPanelOffsetFraction) + (component.insets.top + collapsedHeight) * component.scrollingPanelOffsetFraction let bottomInset = component.hasBottomPanel ? (49.0 + component.insets.bottom) * (1.0 - component.scrollingPanelOffsetFraction) : 0.0 let insets = UIEdgeInsets(top: topInset, left: component.insets.left, bottom: bottomInset, right: component.insets.right) diff --git a/submodules/BrowserUI/Sources/BrowserSearchBarComponent.swift b/submodules/BrowserUI/Sources/BrowserSearchBarComponent.swift index 9678ab3b..104ecaaa 100644 --- a/submodules/BrowserUI/Sources/BrowserSearchBarComponent.swift +++ b/submodules/BrowserUI/Sources/BrowserSearchBarComponent.swift @@ -7,6 +7,7 @@ import ComponentFlow import TelegramPresentationData import AccountContext import BundleIconComponent +import SearchInputPanelComponent final class SearchBarContentComponent: Component { public typealias EnvironmentType = BrowserNavigationBarEnvironment @@ -35,112 +36,16 @@ final class SearchBarContentComponent: Component { return true } - final class View: UIView, UITextFieldDelegate { - private final class SearchTextField: UITextField { - override func textRect(forBounds bounds: CGRect) -> CGRect { - return bounds.integral - } - } - - private struct Params: Equatable { - var theme: PresentationTheme - var strings: PresentationStrings - var size: CGSize - - static func ==(lhs: Params, rhs: Params) -> Bool { - if lhs.theme !== rhs.theme { - return false - } - if lhs.strings !== rhs.strings { - return false - } - if lhs.size != rhs.size { - return false - } - return true - } - } - + final class View: UIView { private let queryPromise = ValuePromise() private var queryDisposable: Disposable? - private let backgroundLayer: SimpleLayer + private let searchInput = ComponentView() - private let iconView: UIImageView - - private let clearIconView: UIImageView - private let clearIconButton: HighlightTrackingButton - - private let cancelButtonTitle: ComponentView - private let cancelButton: HighlightTrackingButton - - private var placeholderContent = ComponentView() - - private var textFrame: CGRect? - private var textField: SearchTextField? - - private var tapRecognizer: UITapGestureRecognizer? - - private var params: Params? private var component: SearchBarContentComponent? - init() { - self.backgroundLayer = SimpleLayer() - - self.iconView = UIImageView() - - self.clearIconView = UIImageView() - self.clearIconButton = HighlightableButton() - self.clearIconView.isHidden = true - self.clearIconButton.isHidden = true - - self.cancelButtonTitle = ComponentView() - self.cancelButton = HighlightTrackingButton() - - super.init(frame: CGRect()) - - self.layer.addSublayer(self.backgroundLayer) - - self.addSubview(self.iconView) - self.addSubview(self.clearIconView) - self.addSubview(self.clearIconButton) - - self.addSubview(self.cancelButton) - self.clipsToBounds = true - - let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))) - self.tapRecognizer = tapRecognizer - self.addGestureRecognizer(tapRecognizer) - - self.cancelButton.highligthedChanged = { [weak self] highlighted in - if let strongSelf = self { - if highlighted { - if let cancelButtonTitleView = strongSelf.cancelButtonTitle.view { - cancelButtonTitleView.layer.removeAnimation(forKey: "opacity") - cancelButtonTitleView.alpha = 0.4 - } - } else { - if let cancelButtonTitleView = strongSelf.cancelButtonTitle.view { - cancelButtonTitleView.alpha = 1.0 - cancelButtonTitleView.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) - } - } - } - } - self.cancelButton.addTarget(self, action: #selector(self.cancelPressed), for: .touchUpInside) - - self.clearIconButton.highligthedChanged = { [weak self] highlighted in - if let strongSelf = self { - if highlighted { - strongSelf.clearIconView.layer.removeAnimation(forKey: "opacity") - strongSelf.clearIconView.alpha = 0.4 - } else { - strongSelf.clearIconView.alpha = 1.0 - strongSelf.clearIconView.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) - } - } - } - self.clearIconButton.addTarget(self, action: #selector(self.clearPressed), for: .touchUpInside) + override init(frame: CGRect) { + super.init(frame: frame) let throttledSearchQuery = self.queryPromise.get() |> mapToSignal { query -> Signal in @@ -164,194 +69,46 @@ final class SearchBarContentComponent: Component { fatalError("init(coder:) has not been implemented") } - @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { - if case .ended = recognizer.state { - self.activateTextInput() - } - } - - private func activateTextInput() { - if self.textField == nil, let textFrame = self.textFrame { - let backgroundFrame = self.backgroundLayer.frame - let textFieldFrame = CGRect(origin: CGPoint(x: textFrame.minX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.maxX - textFrame.minX - 32.0, height: backgroundFrame.height)) - - let textField = SearchTextField(frame: textFieldFrame) - textField.clipsToBounds = true - textField.autocorrectionType = .no - textField.returnKeyType = .search - self.textField = textField - self.insertSubview(textField, belowSubview: self.clearIconView) - textField.delegate = self - textField.addTarget(self, action: #selector(self.textFieldChanged(_:)), for: .editingChanged) - } - - guard !(self.textField?.isFirstResponder ?? false) else { - return - } - - self.textField?.becomeFirstResponder() - } - - @objc private func cancelPressed() { - self.clearIconView.isHidden = true - self.clearIconButton.isHidden = true - - let textField = self.textField - self.textField = nil - - self.component?.performAction.invoke(.updateSearchActive(false)) - - if let textField { - textField.resignFirstResponder() - textField.removeFromSuperview() - } - } - - @objc private func clearPressed() { - guard let textField = self.textField else { - return - } - textField.text = "" - self.textFieldChanged(textField) - } - - func deactivate() { - if let text = self.textField?.text, !text.isEmpty { - self.textField?.endEditing(true) - } else { - self.cancelPressed() - } - } - - public func textFieldDidBeginEditing(_ textField: UITextField) { - } - - public func textFieldDidEndEditing(_ textField: UITextField) { - } - - public func textFieldShouldReturn(_ textField: UITextField) -> Bool { - textField.endEditing(true) - return false - } - - @objc private func textFieldChanged(_ textField: UITextField) { - let text = textField.text ?? "" - - self.clearIconView.isHidden = text.isEmpty - self.clearIconButton.isHidden = text.isEmpty - self.placeholderContent.view?.isHidden = !text.isEmpty - - self.queryPromise.set(text) - - if let params = self.params { - self.update(theme: params.theme, strings: params.strings, size: params.size, transition: .immediate) - } - } - func update(component: SearchBarContentComponent, availableSize: CGSize, transition: ComponentTransition) -> CGSize { self.component = component - self.update(theme: component.theme, strings: component.strings, size: availableSize, transition: transition) - self.activateTextInput() - - return availableSize - } - - public func update(theme: PresentationTheme, strings: PresentationStrings, size: CGSize, transition: ComponentTransition) { - let params = Params( - theme: theme, - strings: strings, - size: size - ) - - if self.params == params { - return - } - - let isActiveWithText = true - - if self.params?.theme !== theme { - self.iconView.image = generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Loupe"), color: .white)?.withRenderingMode(.alwaysTemplate) - self.iconView.tintColor = theme.rootController.navigationSearchBar.inputIconColor - self.clearIconView.image = generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: .white)?.withRenderingMode(.alwaysTemplate) - self.clearIconView.tintColor = theme.rootController.navigationSearchBar.inputClearButtonColor - } - - self.params = params - - let sideInset: CGFloat = 10.0 - let inputHeight: CGFloat = 36.0 - let topInset: CGFloat = (size.height - inputHeight) / 2.0 - - let sideTextInset: CGFloat = sideInset + 4.0 + 17.0 - - self.backgroundLayer.backgroundColor = theme.rootController.navigationSearchBar.inputFillColor.cgColor - self.backgroundLayer.cornerRadius = 10.5 - - let cancelTextSize = self.cancelButtonTitle.update( - transition: .immediate, - component: AnyComponent(Text( - text: strings.Common_Cancel, - font: Font.regular(17.0), - color: theme.rootController.navigationBar.accentTextColor - )), - environment: {}, - containerSize: CGSize(width: size.width - 32.0, height: 100.0) - ) - - let cancelButtonSpacing: CGFloat = 8.0 - - var backgroundFrame = CGRect(origin: CGPoint(x: sideInset, y: topInset), size: CGSize(width: size.width - sideInset * 2.0, height: inputHeight)) - if isActiveWithText { - backgroundFrame.size.width -= cancelTextSize.width + cancelButtonSpacing - } - transition.setFrame(layer: self.backgroundLayer, frame: backgroundFrame) - - transition.setFrame(view: self.cancelButton, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX, y: 0.0), size: CGSize(width: cancelButtonSpacing + cancelTextSize.width, height: size.height))) - - let textX: CGFloat = backgroundFrame.minX + sideTextInset - let textFrame = CGRect(origin: CGPoint(x: textX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.maxX - textX, height: backgroundFrame.height)) - self.textFrame = textFrame - - if let image = self.iconView.image { - let iconFrame = CGRect(origin: CGPoint(x: backgroundFrame.minX + 5.0, y: backgroundFrame.minY + floor((backgroundFrame.height - image.size.height) / 2.0)), size: image.size) - transition.setFrame(view: self.iconView, frame: iconFrame) - } - - let placeholderSize = self.placeholderContent.update( + let searchInputSize = self.searchInput.update( transition: transition, component: AnyComponent( - Text(text: strings.Common_Search, font: Font.regular(17.0), color: theme.rootController.navigationSearchBar.inputPlaceholderTextColor) + SearchInputPanelComponent( + theme: component.theme, + strings: component.strings, + metrics: .init(widthClass: .compact, heightClass: .compact, orientation: nil), + safeInsets: UIEdgeInsets(), + placeholder: component.strings.Common_Search, + hasEdgeEffect: false, + updated: { [weak self] query in + guard let self else { + return + } + self.queryPromise.set(query) + }, + cancel: { [weak self] in + guard let self else { + return + } + self.component?.performAction.invoke(.updateSearchActive(false)) + } + ) ), environment: {}, - containerSize: size + containerSize: availableSize ) - if let placeholderContentView = self.placeholderContent.view { - if placeholderContentView.superview == nil { - self.addSubview(placeholderContentView) + if let searchInputView = self.searchInput.view as? SearchInputPanelComponent.View { + if searchInputView.superview == nil { + self.addSubview(searchInputView) + + searchInputView.activateInput() } - let placeholderContentFrame = CGRect(origin: CGPoint(x: textFrame.minX, y: backgroundFrame.midY - placeholderSize.height / 2.0), size: placeholderSize) - transition.setFrame(view: placeholderContentView, frame: placeholderContentFrame) - } - - if let image = self.clearIconView.image { - let iconFrame = CGRect(origin: CGPoint(x: backgroundFrame.maxX - image.size.width - 4.0, y: backgroundFrame.minY + floor((backgroundFrame.height - image.size.height) / 2.0)), size: image.size) - transition.setFrame(view: self.clearIconView, frame: iconFrame) - transition.setFrame(view: self.clearIconButton, frame: iconFrame.insetBy(dx: -8.0, dy: -10.0)) - } - - if let cancelButtonTitleComponentView = self.cancelButtonTitle.view { - if cancelButtonTitleComponentView.superview == nil { - self.addSubview(cancelButtonTitleComponentView) - cancelButtonTitleComponentView.isUserInteractionEnabled = false - } - transition.setFrame(view: cancelButtonTitleComponentView, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX + cancelButtonSpacing, y: floor((size.height - cancelTextSize.height) / 2.0)), size: cancelTextSize)) - } - - if let textField = self.textField { - textField.textColor = theme.rootController.navigationSearchBar.inputTextColor - transition.setFrame(view: textField, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + sideTextInset, y: backgroundFrame.minY - UIScreenPixel), size: CGSize(width: backgroundFrame.width - sideTextInset - 32.0, height: backgroundFrame.height))) + transition.setFrame(view: searchInputView, frame: CGRect(origin: .zero, size: searchInputSize)) } + + return availableSize } } diff --git a/submodules/BrowserUI/Sources/BrowserTitleBarComponent.swift b/submodules/BrowserUI/Sources/BrowserTitleBarComponent.swift index a362da7d..d21be0fa 100644 --- a/submodules/BrowserUI/Sources/BrowserTitleBarComponent.swift +++ b/submodules/BrowserUI/Sources/BrowserTitleBarComponent.swift @@ -9,19 +9,26 @@ import AccountContext import BundleIconComponent import MultilineTextComponent import UrlEscaping +import GlassBackgroundComponent final class TitleBarContentComponent: Component { public typealias EnvironmentType = BrowserNavigationBarEnvironment let theme: PresentationTheme let title: String + let readingProgress: CGFloat + let loadingProgress: Double? init( theme: PresentationTheme, - title: String + title: String, + readingProgress: CGFloat, + loadingProgress: Double? ) { self.theme = theme self.title = title + self.readingProgress = readingProgress + self.loadingProgress = loadingProgress } static func ==(lhs: TitleBarContentComponent, rhs: TitleBarContentComponent) -> Bool { @@ -31,15 +38,30 @@ final class TitleBarContentComponent: Component { if lhs.title != rhs.title { return false } + if lhs.readingProgress != rhs.readingProgress { + return false + } + if lhs.loadingProgress != rhs.loadingProgress { + return false + } return true } final class View: UIView { + private let backgroundView = GlassBackgroundView() + private let clippingView = UIView() + private let readingProgressView = UIView() private var titleContent = ComponentView() private var component: TitleBarContentComponent? init() { super.init(frame: CGRect()) + + self.clippingView.clipsToBounds = true + + self.addSubview(self.backgroundView) + self.backgroundView.contentView.addSubview(self.clippingView) + self.clippingView.addSubview(self.readingProgressView) } required public init?(coder: NSCoder) { @@ -48,7 +70,9 @@ final class TitleBarContentComponent: Component { func update(component: TitleBarContentComponent, availableSize: CGSize, environment: Environment, transition: ComponentTransition) -> CGSize { self.component = component - + + let collapseFraction = environment[BrowserNavigationBarEnvironment.self].fraction + let titleSize = self.titleContent.update( transition: transition, component: AnyComponent( @@ -60,7 +84,7 @@ final class TitleBarContentComponent: Component { ) ), environment: {}, - containerSize: CGSize(width: availableSize.width - 36.0, height: availableSize.height) + containerSize: CGSize(width: availableSize.width - 42.0, height: availableSize.height) ) let titleContentFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - titleSize.width) / 2.0), y: floorToScreenPixels((availableSize.height - titleSize.height) / 2.0)), size: titleSize) if let titleContentView = self.titleContent.view { @@ -71,6 +95,18 @@ final class TitleBarContentComponent: Component { titleContentView.bounds = CGRect(origin: .zero, size: titleContentFrame.size) } + let expandedBackgroundWidth = availableSize.width - 14.0 * 2.0 + let collapsedBackgroundWidth = titleSize.width + 32.0 + let backgroundSize = CGSize(width: expandedBackgroundWidth * (1.0 - collapseFraction) + collapsedBackgroundWidth * collapseFraction, height: 44.0) + self.backgroundView.update(size: backgroundSize, cornerRadius: backgroundSize.height * 0.5, isDark: component.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: component.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), transition: transition) + let backgroundFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - backgroundSize.width) / 2.0), y: floor((availableSize.height - backgroundSize.height) / 2.0)), size: backgroundSize) + transition.setFrame(view: self.backgroundView, frame: backgroundFrame) + transition.setFrame(view: self.clippingView, frame: CGRect(origin: .zero, size: backgroundFrame.size)) + transition.setCornerRadius(layer: self.clippingView.layer, cornerRadius: backgroundFrame.size.height * 0.5) + + self.readingProgressView.backgroundColor = component.theme.rootController.navigationBar.primaryTextColor.withMultipliedAlpha(0.07) + self.readingProgressView.frame = CGRect(origin: .zero, size: CGSize(width: backgroundSize.width * component.readingProgress, height: backgroundSize.height)) + return availableSize } } diff --git a/submodules/BrowserUI/Sources/BrowserToolbarComponent.swift b/submodules/BrowserUI/Sources/BrowserToolbarComponent.swift index 10834cdf..8f695ffc 100644 --- a/submodules/BrowserUI/Sources/BrowserToolbarComponent.swift +++ b/submodules/BrowserUI/Sources/BrowserToolbarComponent.swift @@ -6,28 +6,24 @@ import BlurredBackgroundComponent import BundleIconComponent import TelegramPresentationData import ContextReferenceButtonComponent +import GlassBackgroundComponent +import EdgeEffect final class BrowserToolbarComponent: CombinedComponent { - let backgroundColor: UIColor - let separatorColor: UIColor - let textColor: UIColor + let theme: PresentationTheme let bottomInset: CGFloat let sideInset: CGFloat let item: AnyComponentWithIdentity? let collapseFraction: CGFloat init( - backgroundColor: UIColor, - separatorColor: UIColor, - textColor: UIColor, + theme: PresentationTheme, bottomInset: CGFloat, sideInset: CGFloat, item: AnyComponentWithIdentity?, collapseFraction: CGFloat ) { - self.backgroundColor = backgroundColor - self.separatorColor = separatorColor - self.textColor = textColor + self.theme = theme self.bottomInset = bottomInset self.sideInset = sideInset self.item = item @@ -35,13 +31,7 @@ final class BrowserToolbarComponent: CombinedComponent { } static func ==(lhs: BrowserToolbarComponent, rhs: BrowserToolbarComponent) -> Bool { - if lhs.backgroundColor != rhs.backgroundColor { - return false - } - if lhs.separatorColor != rhs.separatorColor { - return false - } - if lhs.textColor != rhs.textColor { + if lhs.theme !== rhs.theme { return false } if lhs.bottomInset != rhs.bottomInset { @@ -60,54 +50,70 @@ final class BrowserToolbarComponent: CombinedComponent { } static var body: Body { - let background = Child(BlurredBackgroundComponent.self) - let separator = Child(Rectangle.self) + let edgeEffect = Child(EdgeEffectComponent.self) + let background = Child(GlassBackgroundComponent.self) let centerItems = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self) return { context in - let contentHeight: CGFloat = 49.0 + let contentHeight: CGFloat = 56.0 let totalHeight = contentHeight + context.component.bottomInset let offset = context.component.collapseFraction * totalHeight let size = CGSize(width: context.availableSize.width, height: totalHeight) - let background = background.update( - component: BlurredBackgroundComponent(color: context.component.backgroundColor), - availableSize: CGSize(width: size.width, height: size.height), + let backgroundHeight: CGFloat = 48.0 + let edgeEffectHeight = totalHeight + let edgeEffect = edgeEffect.update( + component: EdgeEffectComponent( + color: .clear, + blur: true, + alpha: 1.0, + size: CGSize(width: size.width, height: edgeEffectHeight), + edge: .bottom, + edgeSize: edgeEffectHeight + ), + availableSize: CGSize(width: size.width, height: edgeEffectHeight), transition: context.transition ) - - let separator = separator.update( - component: Rectangle(color: context.component.separatorColor, height: UIScreenPixel), - availableSize: CGSize(width: size.width, height: size.height), - transition: context.transition + context.add(edgeEffect + .position(CGPoint(x: size.width / 2.0, y: size.height / 2.0 + offset)) ) - + let item = context.component.item.flatMap { item in return centerItems[item.id].update( component: item.component, - availableSize: CGSize(width: context.availableSize.width - context.component.sideInset * 2.0, height: contentHeight), + availableSize: CGSize(width: context.availableSize.width - context.component.sideInset * 2.0, height: backgroundHeight), transition: context.transition ) } + let contentWidth = item?.size.width ?? 0.0 + + let backgroundSize = CGSize(width: contentWidth, height: backgroundHeight) + let background = background.update( + component: GlassBackgroundComponent( + size: backgroundSize, + cornerRadius: backgroundHeight * 0.5, + isDark: context.component.theme.overallDarkAppearance, + tintColor: .init(kind: .panel, color: UIColor(white: context.component.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), + isInteractive: true + ), + availableSize: backgroundSize, + transition: context.transition + ) context.add(background - .position(CGPoint(x: size.width / 2.0, y: size.height / 2.0 + offset)) + .position(CGPoint(x: size.width / 2.0, y: backgroundSize.height / 2.0 + offset)) ) - context.add(separator - .position(CGPoint(x: size.width / 2.0, y: 0.0 + offset)) - ) - if let centerItem = item { context.add(centerItem - .position(CGPoint(x: context.availableSize.width / 2.0, y: contentHeight / 2.0 + offset)) + .position(CGPoint(x: context.availableSize.width / 2.0, y: backgroundSize.height / 2.0 + offset)) .appear(ComponentTransition.Appear({ _, view, transition in - transition.animatePosition(view: view, from: CGPoint(x: 0.0, y: size.height), to: .zero, additive: true) + transition.animateBlur(layer: view.layer, fromRadius: 10.0, toRadius: 0.0) + transition.animateAlpha(view: view, from: 0.0, to: 1.0) })) .disappear(ComponentTransition.Disappear({ view, transition, completion in - let from = view.center - view.center = from.offsetBy(dx: 0.0, dy: size.height) - transition.animatePosition(view: view, from: from, to: view.center, completion: { _ in + transition.animateBlur(layer: view.layer, fromRadius: 0.0, toRadius: 10.0) + transition.setAlpha(view: view, alpha: 0.0, completion: { _ in completion() }) })) @@ -120,8 +126,7 @@ final class BrowserToolbarComponent: CombinedComponent { } final class NavigationToolbarContentComponent: CombinedComponent { - let accentColor: UIColor - let textColor: UIColor + let theme: PresentationTheme let canGoBack: Bool let canGoForward: Bool let canOpenIn: Bool @@ -131,8 +136,7 @@ final class NavigationToolbarContentComponent: CombinedComponent { let performHoldAction: (UIView, ContextGesture?, BrowserScreen.Action) -> Void init( - accentColor: UIColor, - textColor: UIColor, + theme: PresentationTheme, canGoBack: Bool, canGoForward: Bool, canOpenIn: Bool, @@ -141,8 +145,7 @@ final class NavigationToolbarContentComponent: CombinedComponent { performAction: ActionSlot, performHoldAction: @escaping (UIView, ContextGesture?, BrowserScreen.Action) -> Void ) { - self.accentColor = accentColor - self.textColor = textColor + self.theme = theme self.canGoBack = canGoBack self.canGoForward = canGoForward self.canOpenIn = canOpenIn @@ -153,10 +156,7 @@ final class NavigationToolbarContentComponent: CombinedComponent { } static func ==(lhs: NavigationToolbarContentComponent, rhs: NavigationToolbarContentComponent) -> Bool { - if lhs.accentColor != rhs.accentColor { - return false - } - if lhs.textColor != rhs.textColor { + if lhs.theme !== rhs.theme { return false } if lhs.canGoBack != rhs.canGoBack { @@ -191,26 +191,20 @@ final class NavigationToolbarContentComponent: CombinedComponent { let performAction = context.component.performAction let performHoldAction = context.component.performHoldAction - let sideInset: CGFloat = 5.0 - let buttonSize = CGSize(width: 50.0, height: availableSize.height) + var size = CGSize(width: 0.0, height: 48.0) + let buttonSize = CGSize(width: 50.0, height: size.height) - var buttonCount = 3 - if context.component.canShare { - buttonCount += 1 - } - if context.component.canOpenIn { - buttonCount += 1 - } - - let spacing = (availableSize.width - buttonSize.width * CGFloat(buttonCount) - sideInset * 2.0) / CGFloat(buttonCount - 1) + let sideInset: CGFloat = 34.0 + let spacing: CGFloat = 66.0 + let textColor = context.component.theme.rootController.navigationBar.primaryTextColor let canShare = context.component.canShare let share = share.update( component: Button( content: AnyComponent( BundleIconComponent( - name: "Chat List/NavigationShare", - tintColor: context.component.accentColor + name: "Instant View/Share", + tintColor: textColor ) ), action: { @@ -224,22 +218,14 @@ final class NavigationToolbarContentComponent: CombinedComponent { ) if context.component.isDocument { - if !context.component.canShare { - context.add(share - .position(CGPoint(x: availableSize.width / 2.0, y: 10000.0)) - ) - } else { - context.add(share - .position(CGPoint(x: availableSize.width / 2.0, y: availableSize.height / 2.0)) - ) - } - + var originX: CGFloat = sideInset + let search = search.update( component: Button( content: AnyComponent( BundleIconComponent( - name: "Chat List/SearchIcon", - tintColor: context.component.accentColor + name: "Instant View/Search", + tintColor: textColor ) ), action: { @@ -250,15 +236,27 @@ final class NavigationToolbarContentComponent: CombinedComponent { transition: .easeInOut(duration: 0.2) ) context.add(search - .position(CGPoint(x: sideInset + search.size.width / 2.0, y: availableSize.height / 2.0)) + .position(CGPoint(x: originX, y: availableSize.height / 2.0)) ) + originX += spacing + + if !context.component.canShare { + context.add(share + .position(CGPoint(x: availableSize.width / 2.0, y: 10000.0)) + ) + } else { + context.add(share + .position(CGPoint(x: originX, y: availableSize.height / 2.0)) + ) + originX += spacing + } let quickLook = quickLook.update( component: Button( content: AnyComponent( BundleIconComponent( name: "Instant View/OpenDocument", - tintColor: context.component.accentColor + tintColor: textColor ) ), action: { @@ -269,16 +267,19 @@ final class NavigationToolbarContentComponent: CombinedComponent { transition: .easeInOut(duration: 0.2) ) context.add(quickLook - .position(CGPoint(x: context.availableSize.width - sideInset - quickLook.size.width / 2.0, y: availableSize.height / 2.0)) + .position(CGPoint(x: originX, y: availableSize.height / 2.0)) ) + size.width = originX + sideInset } else { + var originX: CGFloat = sideInset + let canGoBack = context.component.canGoBack let back = back.update( component: ContextReferenceButtonComponent( content: AnyComponent( BundleIconComponent( name: "Instant View/Back", - tintColor: canGoBack ? context.component.accentColor : context.component.accentColor.withAlphaComponent(0.4) + tintColor: canGoBack ? textColor : textColor.withAlphaComponent(0.4) ) ), minSize: buttonSize, @@ -297,8 +298,9 @@ final class NavigationToolbarContentComponent: CombinedComponent { transition: .easeInOut(duration: 0.2) ) context.add(back - .position(CGPoint(x: sideInset + back.size.width / 2.0, y: availableSize.height / 2.0)) + .position(CGPoint(x: sideInset, y: availableSize.height / 2.0)) ) + originX += spacing let canGoForward = context.component.canGoForward let forward = forward.update( @@ -306,7 +308,7 @@ final class NavigationToolbarContentComponent: CombinedComponent { content: AnyComponent( BundleIconComponent( name: "Instant View/Forward", - tintColor: canGoForward ? context.component.accentColor : context.component.accentColor.withAlphaComponent(0.4) + tintColor: canGoForward ? textColor : textColor.withAlphaComponent(0.4) ) ), minSize: buttonSize, @@ -325,19 +327,21 @@ final class NavigationToolbarContentComponent: CombinedComponent { transition: .easeInOut(duration: 0.2) ) context.add(forward - .position(CGPoint(x: sideInset + back.size.width + spacing + forward.size.width / 2.0, y: availableSize.height / 2.0)) + .position(CGPoint(x: originX, y: availableSize.height / 2.0)) ) + originX += spacing context.add(share - .position(CGPoint(x: sideInset + back.size.width + spacing + forward.size.width + spacing + share.size.width / 2.0, y: availableSize.height / 2.0)) + .position(CGPoint(x: originX, y: availableSize.height / 2.0)) ) + originX += spacing let bookmark = bookmark.update( component: Button( content: AnyComponent( BundleIconComponent( name: "Instant View/Bookmark", - tintColor: context.component.accentColor + tintColor: textColor ) ), action: { @@ -348,16 +352,18 @@ final class NavigationToolbarContentComponent: CombinedComponent { transition: .easeInOut(duration: 0.2) ) context.add(bookmark - .position(CGPoint(x: sideInset + back.size.width + spacing + forward.size.width + spacing + share.size.width + spacing + bookmark.size.width / 2.0, y: availableSize.height / 2.0)) + .position(CGPoint(x: originX, y: availableSize.height / 2.0)) ) if context.component.canOpenIn { + originX += spacing + let openIn = openIn.update( component: Button( content: AnyComponent( BundleIconComponent( name: "Instant View/Browser", - tintColor: context.component.accentColor + tintColor: textColor ) ), action: { @@ -368,34 +374,36 @@ final class NavigationToolbarContentComponent: CombinedComponent { transition: .easeInOut(duration: 0.2) ) context.add(openIn - .position(CGPoint(x: sideInset + back.size.width + spacing + forward.size.width + spacing + share.size.width + spacing + bookmark.size.width + spacing + openIn.size.width / 2.0, y: availableSize.height / 2.0)) + .position(CGPoint(x: originX, y: availableSize.height / 2.0)) ) } + + size.width = originX + sideInset } - return availableSize + return size } } } final class SearchToolbarContentComponent: CombinedComponent { + let theme: PresentationTheme let strings: PresentationStrings - let textColor: UIColor let index: Int let count: Int let isEmpty: Bool let performAction: ActionSlot init( + theme: PresentationTheme, strings: PresentationStrings, - textColor: UIColor, index: Int, count: Int, isEmpty: Bool, performAction: ActionSlot ) { + self.theme = theme self.strings = strings - self.textColor = textColor self.index = index self.count = count self.isEmpty = isEmpty @@ -403,10 +411,10 @@ final class SearchToolbarContentComponent: CombinedComponent { } static func ==(lhs: SearchToolbarContentComponent, rhs: SearchToolbarContentComponent) -> Bool { - if lhs.strings !== rhs.strings { + if lhs.theme !== rhs.theme { return false } - if lhs.textColor != rhs.textColor { + if lhs.strings !== rhs.strings { return false } if lhs.index != rhs.index { @@ -430,15 +438,17 @@ final class SearchToolbarContentComponent: CombinedComponent { let availableSize = context.availableSize let performAction = context.component.performAction - let sideInset: CGFloat = 3.0 + let sideInset: CGFloat = 60.0 let buttonSize = CGSize(width: 50.0, height: availableSize.height) + let textColor = context.component.theme.rootController.navigationBar.primaryTextColor + let down = down.update( component: Button( content: AnyComponent( BundleIconComponent( name: "Chat/Input/Search/DownButton", - tintColor: context.component.textColor + tintColor: textColor ) ), isEnabled: context.component.count > 0, @@ -458,7 +468,7 @@ final class SearchToolbarContentComponent: CombinedComponent { content: AnyComponent( BundleIconComponent( name: "Chat/Input/Search/UpButton", - tintColor: context.component.textColor + tintColor: textColor ) ), isEnabled: context.component.count > 0, @@ -486,7 +496,7 @@ final class SearchToolbarContentComponent: CombinedComponent { component: Text( text: currentText, font: Font.regular(15.0), - color: context.component.textColor + color: textColor ), availableSize: availableSize, transition: .easeInOut(duration: 0.2) @@ -495,7 +505,7 @@ final class SearchToolbarContentComponent: CombinedComponent { .position(CGPoint(x: availableSize.width - sideInset - down.size.width - up.size.width - text.size.width / 2.0, y: availableSize.height / 2.0)) ) - return availableSize + return CGSize(width: availableSize.width - 60.0, height: 48.0) } } } diff --git a/submodules/BrowserUI/Sources/BrowserWebContent.swift b/submodules/BrowserUI/Sources/BrowserWebContent.swift index 20a3dbc5..21107048 100644 --- a/submodules/BrowserUI/Sources/BrowserWebContent.swift +++ b/submodules/BrowserUI/Sources/BrowserWebContent.swift @@ -23,6 +23,7 @@ import SaveProgressScreen import DeviceModel import LegacyMediaPickerUI import PassKit +import AlertComponent private final class TonSchemeHandler: NSObject, WKURLSchemeHandler { private final class PendingTask { @@ -1258,12 +1259,20 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) { let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } var completed = false - let alertController = textAlertController(context: self.context, updatedPresentationData: nil, title: nil, text: message, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { - if !completed { - completed = true - completionHandler() - } - })]) + + let alertController = AlertScreen( + context: self.context, + title: nil, + text: message, + actions: [ + .init(title: presentationData.strings.Common_OK, type: .default, action: { + if !completed { + completed = true + completionHandler() + } + }) + ] + ) alertController.dismissed = { byOutsideTap in if byOutsideTap { if !completed { @@ -1278,17 +1287,26 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) { let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } var completed = false - let alertController = textAlertController(context: self.context, updatedPresentationData: nil, title: nil, text: message, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - if !completed { - completed = true - completionHandler(false) - } - }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { - if !completed { - completed = true - completionHandler(true) - } - })]) + + let alertController = AlertScreen( + context: self.context, + title: nil, + text: message, + actions: [ + .init(title: presentationData.strings.Common_Cancel, action: { + if !completed { + completed = true + completionHandler(false) + } + }), + .init(title: presentationData.strings.Common_OK, type: .default, action: { + if !completed { + completed = true + completionHandler(true) + } + }) + ] + ) alertController.dismissed = { byOutsideTap in if byOutsideTap { if !completed { @@ -1302,24 +1320,28 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) { var completed = false - let promptController = promptController(sharedContext: self.context.sharedContext, updatedPresentationData: nil, text: prompt, value: defaultText, apply: { value in - if !completed { - completed = true - if let value = value { - completionHandler(value) - } else { - completionHandler(nil) + let promptController = promptController( + context: self.context, + updatedPresentationData: nil, + text: prompt, + value: defaultText, + apply: { value in + if !completed { + completed = true + if let value = value { + completionHandler(value) + } else { + completionHandler(nil) + } } - } - }) - promptController.dismissed = { byOutsideTap in - if byOutsideTap { + }, + dismissed: { if !completed { completed = true completionHandler(nil) } } - } + ) self.present(promptController, nil) } @@ -1356,17 +1378,25 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU private func presentDownloadConfirmation(fileName: String, proceed: @escaping (Bool) -> Void) { let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } var completed = false - let alertController = textAlertController(context: self.context, updatedPresentationData: nil, title: nil, text: presentationData.strings.WebBrowser_Download_Confirmation(fileName).string, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - if !completed { - completed = true - proceed(false) - } - }), TextAlertAction(type: .defaultAction, title: presentationData.strings.WebBrowser_Download_Download, action: { - if !completed { - completed = true - proceed(true) - } - })]) + let alertController = AlertScreen( + context: self.context, + title: nil, + text: presentationData.strings.WebBrowser_Download_Confirmation(fileName).string, + actions: [ + .init(title: presentationData.strings.Common_Cancel, action: { + if !completed { + completed = true + proceed(false) + } + }), + .init(title: presentationData.strings.WebBrowser_Download_Download, type: .default, action: { + if !completed { + completed = true + proceed(true) + } + }) + ] + ) alertController.dismissed = { byOutsideTap in if byOutsideTap { if !completed { diff --git a/submodules/CalendarMessageScreen/Sources/CalendarMessageScreen.swift b/submodules/CalendarMessageScreen/Sources/CalendarMessageScreen.swift index cb97a18d..eb51c84e 100644 --- a/submodules/CalendarMessageScreen/Sources/CalendarMessageScreen.swift +++ b/submodules/CalendarMessageScreen/Sources/CalendarMessageScreen.swift @@ -1603,11 +1603,12 @@ public final class CalendarMessageScreen: ViewController { } frames[i] = monthFrame } + contentHeight += navigationHeight self.scrollLayout = (layout.size.width, contentHeight, frames) - self.contextGestureContainerNode.frame = CGRect(origin: CGPoint(x: 0.0, y: navigationHeight), size: CGSize(width: layout.size.width, height: layout.size.height - navigationHeight)) - self.scrollView.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height - navigationHeight)) + self.contextGestureContainerNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height)) + self.scrollView.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height)) self.scrollView.contentSize = CGSize(width: layout.size.width, height: contentHeight) self.scrollView.verticalScrollIndicatorInsets = UIEdgeInsets(top: max(layout.intrinsicInsets.bottom, self.scrollView.contentInset.top), left: 0.0, bottom: 0.0, right: layout.size.width - 3.0 - 6.0) @@ -1862,8 +1863,9 @@ public final class CalendarMessageScreen: ViewController { self.presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData)) + super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData, style: .glass)) + self._hasGlassStyle = true self.navigationPresentation = .modal self.navigationItem.setLeftBarButton(UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(dismissPressed)), animated: false) diff --git a/submodules/CallListUI/BUILD b/submodules/CallListUI/BUILD index 20d63668..21a966fc 100644 --- a/submodules/CallListUI/BUILD +++ b/submodules/CallListUI/BUILD @@ -34,6 +34,9 @@ swift_library( "//submodules/InviteLinksUI", "//submodules/UndoUI", "//submodules/TelegramCallsUI", + "//submodules/TelegramUI/Components/EdgeEffect", + "//submodules/ComponentFlow", + "//submodules/Components/ComponentDisplayAdapters", ], visibility = [ "//visibility:public", diff --git a/submodules/CallListUI/Sources/CallListCallItem.swift b/submodules/CallListUI/Sources/CallListCallItem.swift index 41f076af..507eeb5d 100644 --- a/submodules/CallListUI/Sources/CallListCallItem.swift +++ b/submodules/CallListUI/Sources/CallListCallItem.swift @@ -243,7 +243,7 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode { self.accessibilityArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.backgroundNode) self.addSubnode(self.containerNode) @@ -769,7 +769,8 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode { } transition.updateAlpha(node: strongSelf.infoButtonNode, alpha: item.editing ? 0.0 : 1.0) - let topHighlightInset: CGFloat = (first || !nodeLayout.insets.top.isZero) ? 0.0 : separatorHeight + var topHighlightInset: CGFloat = (first || !nodeLayout.insets.top.isZero) ? 0.0 : separatorHeight + topHighlightInset -= nodeLayout.insets.top strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: nodeLayout.contentSize.width, height: nodeLayout.contentSize.height)) strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: strongSelf.backgroundNode.frame.size) strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -nodeLayout.insets.top - topHighlightInset), size: CGSize(width: nodeLayout.size.width, height: nodeLayout.size.height + topHighlightInset)) diff --git a/submodules/CallListUI/Sources/CallListController.swift b/submodules/CallListUI/Sources/CallListController.swift index b8e54aec..17435c66 100644 --- a/submodules/CallListUI/Sources/CallListController.swift +++ b/submodules/CallListUI/Sources/CallListController.swift @@ -43,7 +43,7 @@ private final class DeleteAllButtonNode: ASDisplayNode { self.buttonNode.addSubnode(self.titleNode) self.contentNode.contentNode.addSubnode(self.buttonNode) - self.titleNode.attributedText = NSAttributedString(string: presentationData.strings.CallList_DeleteAll, font: Font.regular(17.0), textColor: presentationData.theme.rootController.navigationBar.accentTextColor) + self.titleNode.attributedText = NSAttributedString(string: presentationData.strings.CallList_DeleteAll, font: Font.medium(17.0), textColor: presentationData.theme.chat.inputPanel.panelControlColor) //self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside) } @@ -54,9 +54,10 @@ private final class DeleteAllButtonNode: ASDisplayNode { override public func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { let titleSize = self.titleNode.updateLayout(constrainedSize) - self.titleNode.frame = CGRect(origin: CGPoint(), size: titleSize) - self.buttonNode.frame = CGRect(origin: CGPoint(), size: titleSize) - return titleSize + let size = CGSize(width: 10.0 * 2.0 + titleSize.width, height: 44.0) + self.titleNode.frame = CGRect(origin: CGPoint(x: 10.0, y: floorToScreenPixels((size.height - titleSize.height) * 0.5)), size: titleSize) + self.buttonNode.frame = CGRect(origin: CGPoint(), size: size) + return size } override public func layout() { @@ -102,7 +103,7 @@ public final class CallListController: TelegramBaseController { self.segmentedTitleView = ItemListControllerSegmentedTitleView(theme: self.presentationData.theme, segments: [self.presentationData.strings.Calls_All, self.presentationData.strings.Calls_Missed], selectedIndex: 0) - super.init(context: context, navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), mediaAccessoryPanelVisibility: .none, locationBroadcastPanelSource: .none, groupCallPanelSource: .none) + super.init(context: context, navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData, style: .glass)) self.tabBarItemContextActionType = .always @@ -155,6 +156,8 @@ public final class CallListController: TelegramBaseController { if case .navigation = self.mode { self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) } + + self.updateTabBarSearchState(ViewController.TabBarSearchState(isActive: false), transition: .immediate) } required public init(coder aDecoder: NSCoder) { @@ -203,7 +206,7 @@ public final class CallListController: TelegramBaseController { } self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData, style: .glass), transition: .immediate) if self.isNodeLoaded { self.controllerNode.updateThemeAndStrings(presentationData: self.presentationData) @@ -359,10 +362,10 @@ public final class CallListController: TelegramBaseController { if empty { switch strongSelf.mode { case .tab: - strongSelf.navigationItem.setLeftBarButton(nil, animated: true) - strongSelf.navigationItem.setRightBarButton(nil, animated: true) + strongSelf.navigationItem.setLeftBarButton(nil, animated: strongSelf.controllerNode.didSetReady) + strongSelf.navigationItem.setRightBarButton(nil, animated: strongSelf.controllerNode.didSetReady) case .navigation: - strongSelf.navigationItem.setRightBarButton(nil, animated: true) + strongSelf.navigationItem.setRightBarButton(nil, animated: strongSelf.controllerNode.didSetReady) } } else { var pressedImpl: (() -> Void)? @@ -379,25 +382,24 @@ public final class CallListController: TelegramBaseController { switch strongSelf.mode { case .tab: if strongSelf.editingMode { - strongSelf.navigationItem.setLeftBarButton(UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Done, style: .done, target: strongSelf, action: #selector(strongSelf.donePressed)), animated: true) - strongSelf.navigationItem.setRightBarButton(UIBarButtonItem(customDisplayNode: buttonNode), animated: true) + strongSelf.navigationItem.setLeftBarButton(UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Done, style: .done, target: strongSelf, action: #selector(strongSelf.donePressed)), animated: strongSelf.controllerNode.didSetReady) + strongSelf.navigationItem.setRightBarButton(UIBarButtonItem(customDisplayNode: buttonNode), animated: strongSelf.controllerNode.didSetReady) strongSelf.navigationItem.rightBarButtonItem?.setCustomAction({ pressedImpl?() }) } else { - strongSelf.navigationItem.setLeftBarButton(UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Edit, style: .plain, target: strongSelf, action: #selector(strongSelf.editPressed)), animated: true) - //strongSelf.navigationItem.setRightBarButton(UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(strongSelf.presentationData.theme), style: .plain, target: self, action: #selector(strongSelf.callPressed)), animated: true) - strongSelf.navigationItem.setRightBarButton(nil, animated: true) + strongSelf.navigationItem.setLeftBarButton(UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Edit, style: .plain, target: strongSelf, action: #selector(strongSelf.editPressed)), animated: strongSelf.controllerNode.didSetReady) + strongSelf.navigationItem.setRightBarButton(nil, animated: strongSelf.controllerNode.didSetReady) } case .navigation: if strongSelf.editingMode { - strongSelf.navigationItem.setLeftBarButton(UIBarButtonItem(customDisplayNode: buttonNode), animated: true) + strongSelf.navigationItem.setLeftBarButton(UIBarButtonItem(customDisplayNode: buttonNode), animated: strongSelf.controllerNode.didSetReady) strongSelf.navigationItem.leftBarButtonItem?.setCustomAction({ pressedImpl?() }) - strongSelf.navigationItem.setRightBarButton(UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Done, style: .done, target: strongSelf, action: #selector(strongSelf.donePressed)), animated: true) + strongSelf.navigationItem.setRightBarButton(UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Done, style: .done, target: strongSelf, action: #selector(strongSelf.donePressed)), animated: strongSelf.controllerNode.didSetReady) } else { - strongSelf.navigationItem.setRightBarButton(UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Edit, style: .plain, target: strongSelf, action: #selector(strongSelf.editPressed)), animated: true) + strongSelf.navigationItem.setRightBarButton(UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Edit, style: .plain, target: strongSelf, action: #selector(strongSelf.editPressed)), animated: strongSelf.controllerNode.didSetReady) } } } @@ -421,10 +423,15 @@ public final class CallListController: TelegramBaseController { self.displayNodeDidLoad() } + override public var navigationEdgeEffectExtension: CGFloat { + return self.controllerNode.navigationEdgeEffectExtension + } + override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { super.containerLayoutUpdated(layout, transition: transition) - self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition) + let navigationLayout = self.navigationLayout(layout: layout) + self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: navigationLayout.navigationFrame.maxY, transition: transition) } @objc func callPressed() { @@ -778,6 +785,10 @@ public final class CallListController: TelegramBaseController { let controller = ContextController(presentationData: self.presentationData, source: .reference(CallListTabBarContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture) self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller) } + + override public func tabBarActivateSearch() { + self.beginCallImpl() + } } private final class CallListTabBarContextReferenceContentSource: ContextReferenceContentSource { diff --git a/submodules/CallListUI/Sources/CallListControllerNode.swift b/submodules/CallListUI/Sources/CallListControllerNode.swift index 9b248b7a..412302fd 100644 --- a/submodules/CallListUI/Sources/CallListControllerNode.swift +++ b/submodules/CallListUI/Sources/CallListControllerNode.swift @@ -15,6 +15,9 @@ import AnimatedStickerNode import TelegramAnimatedStickerNode import AppBundle import ItemListPeerActionItem +import EdgeEffect +import ComponentFlow +import ComponentDisplayAdapters private struct CallListNodeListViewTransition { let callListView: CallListNodeView @@ -185,7 +188,7 @@ final class CallListControllerNode: ASDisplayNode { private var containerLayout: (ContainerViewLayout, CGFloat)? private let _ready = ValuePromise() - private var didSetReady = false + private(set) var didSetReady = false var ready: Signal { return _ready.get() } @@ -220,6 +223,8 @@ final class CallListControllerNode: ASDisplayNode { private let emptyButtonIconNode: ASImageNode private let emptyButtonTextNode: ImmediateTextNode + private let edgeEffectView: EdgeEffectView + private let call: (EngineMessage) -> Void private let joinGroupCall: (EnginePeer.Id, EngineGroupCallDescription) -> Void private let openNewCall: () -> Void @@ -230,6 +235,10 @@ final class CallListControllerNode: ASDisplayNode { private let openGroupCallDisposable = MetaDisposable() + var navigationEdgeEffectExtension: CGFloat { + return max(0.0, self.listNode.edgeEffectExtension) + } + private var previousContentOffset: ListViewVisibleContentOffset? init(controller: CallListController, context: AccountContext, mode: CallListControllerMode, presentationData: PresentationData, call: @escaping (EngineMessage) -> Void, joinGroupCall: @escaping (EnginePeer.Id, EngineGroupCallDescription) -> Void, openInfo: @escaping (EnginePeer.Id, [EngineMessage]) -> Void, emptyStateUpdated: @escaping (Bool) -> Void, openNewCall: @escaping () -> Void) { @@ -277,6 +286,8 @@ final class CallListControllerNode: ASDisplayNode { self.emptyButtonIconNode.displaysAsynchronously = false self.emptyButtonIconNode.isUserInteractionEnabled = false + self.edgeEffectView = EdgeEffectView() + super.init() self.setViewBlock({ @@ -289,6 +300,8 @@ final class CallListControllerNode: ASDisplayNode { self.addSubnode(self.emptyButtonTextNode) self.addSubnode(self.emptyButtonIconNode) self.addSubnode(self.emptyButtonNode) + + self.view.addSubview(self.edgeEffectView) switch self.mode { case .tab: @@ -673,6 +686,13 @@ final class CallListControllerNode: ASDisplayNode { } } } + + self.listNode.onEdgeEffectExtensionUpdated = { [weak self] transition in + guard let self else { + return + } + self.controller?.updateNavigationEdgeEffectExtension(transition: transition) + } } deinit { @@ -945,5 +965,12 @@ final class CallListControllerNode: ASDisplayNode { self.dequeuedInitialTransitionOnLayout = true self.dequeueTransition() } + + let edgeEffectHeight: CGFloat = layout.intrinsicInsets.bottom + let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - edgeEffectHeight), size: CGSize(width: layout.size.width, height: edgeEffectHeight)) + transition.updateFrame(view: self.edgeEffectView, frame: edgeEffectFrame) + self.edgeEffectView.update(content: self.presentationData.theme.list.plainBackgroundColor, rect: edgeEffectFrame, edge: .bottom, edgeSize: edgeEffectFrame.height, transition: ComponentTransition(transition)) + + self.controller?.updateNavigationEdgeEffectExtension(transition: transition) } } diff --git a/submodules/CallListUI/Sources/CallListGroupCallItem.swift b/submodules/CallListUI/Sources/CallListGroupCallItem.swift index 5875b229..069e6134 100644 --- a/submodules/CallListUI/Sources/CallListGroupCallItem.swift +++ b/submodules/CallListUI/Sources/CallListGroupCallItem.swift @@ -208,7 +208,7 @@ class CallListGroupCallItemNode: ItemListRevealOptionsItemNode { self.accessibilityArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.backgroundNode) self.addSubnode(self.indicatorNode) @@ -434,7 +434,8 @@ class CallListGroupCallItemNode: ItemListRevealOptionsItemNode { let _ = joinTitleApply() transition.updateFrameAdditive(node: strongSelf.joinTitleNode, frame: CGRect(origin: CGPoint(x: floor((joinButtonSize.width - joinTitleLayout.size.width) / 2.0), y: floor((joinButtonSize.height - joinTitleLayout.size.height) / 2.0) + 1.0), size: joinTitleLayout.size)) - let topHighlightInset: CGFloat = (first || !nodeLayout.insets.top.isZero) ? 0.0 : separatorHeight + var topHighlightInset: CGFloat = (first || !nodeLayout.insets.top.isZero) ? 0.0 : separatorHeight + topHighlightInset -= nodeLayout.insets.top strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: nodeLayout.contentSize.width, height: nodeLayout.contentSize.height)) strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -nodeLayout.insets.top - topHighlightInset), size: CGSize(width: nodeLayout.size.width, height: nodeLayout.size.height + topHighlightInset)) diff --git a/submodules/CallListUI/Sources/CallListHoleItem.swift b/submodules/CallListUI/Sources/CallListHoleItem.swift index 1dd52fda..bf71192f 100644 --- a/submodules/CallListUI/Sources/CallListHoleItem.swift +++ b/submodules/CallListUI/Sources/CallListHoleItem.swift @@ -70,7 +70,7 @@ class CallListHoleItemNode: ListViewItemNode { self.labelNode = TextNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.separatorNode) self.addSubnode(self.labelNode) diff --git a/submodules/ChatListFilterSettingsHeaderItem/Sources/ChatListFilterSettingsHeaderItem.swift b/submodules/ChatListFilterSettingsHeaderItem/Sources/ChatListFilterSettingsHeaderItem.swift index ebe6c50e..4fc9515c 100644 --- a/submodules/ChatListFilterSettingsHeaderItem/Sources/ChatListFilterSettingsHeaderItem.swift +++ b/submodules/ChatListFilterSettingsHeaderItem/Sources/ChatListFilterSettingsHeaderItem.swift @@ -86,7 +86,7 @@ class ChatListFilterSettingsHeaderItemNode: ListViewItemNode { self.animationNode = DefaultAnimatedStickerNodeImpl() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.titleNode) self.addSubnode(self.animationNode) diff --git a/submodules/ChatListSearchItemHeader/BUILD b/submodules/ChatListSearchItemHeader/BUILD index 8154e629..288c626e 100644 --- a/submodules/ChatListSearchItemHeader/BUILD +++ b/submodules/ChatListSearchItemHeader/BUILD @@ -10,9 +10,12 @@ swift_library( "-warnings-as-errors", ], deps = [ - "//submodules/Display:Display", - "//submodules/TelegramPresentationData:TelegramPresentationData", - "//submodules/ListSectionHeaderNode:ListSectionHeaderNode", + "//submodules/Display", + "//submodules/TelegramPresentationData", + "//submodules/ListSectionHeaderNode", + "//submodules/ComponentFlow", + "//submodules/Components/ComponentDisplayAdapters", + "//submodules/TelegramUI/Components/EdgeEffect", ], visibility = [ "//visibility:public", diff --git a/submodules/ChatListSearchItemHeader/Sources/ChatListSearchItemHeader.swift b/submodules/ChatListSearchItemHeader/Sources/ChatListSearchItemHeader.swift index 71b378f3..202513da 100644 --- a/submodules/ChatListSearchItemHeader/Sources/ChatListSearchItemHeader.swift +++ b/submodules/ChatListSearchItemHeader/Sources/ChatListSearchItemHeader.swift @@ -4,6 +4,9 @@ import AsyncDisplayKit import Display import TelegramPresentationData import ListSectionHeaderNode +import EdgeEffect +import ComponentFlow +import ComponentDisplayAdapters public enum ChatListSearchItemHeaderType { case localPeers @@ -214,6 +217,7 @@ public final class ChatListSearchItemHeader: ListViewItemHeader { public let action: ((ASDisplayNode) -> Void)? public let height: CGFloat = 28.0 + public let isSticky: Bool = false public init(type: ChatListSearchItemHeaderType, theme: PresentationTheme, strings: PresentationStrings, actionTitle: String? = nil, action: ((ASDisplayNode) -> Void)? = nil) { self.type = type @@ -250,6 +254,7 @@ public final class ChatListSearchItemHeaderNode: ListViewItemHeaderNode { private var validLayout: (size: CGSize, leftInset: CGFloat, rightInset: CGFloat)? + private var edgeEffectView: EdgeEffectView? private let sectionHeaderNode: ListSectionHeaderNode public init(type: ChatListSearchItemHeaderType, theme: PresentationTheme, strings: PresentationStrings, actionTitle: String?, action: ((ASDisplayNode) -> Void)?) { @@ -263,6 +268,8 @@ public final class ChatListSearchItemHeaderNode: ListViewItemHeaderNode { super.init() + //self.contributesToEdgeEffect = true + self.sectionHeaderNode.title = type.title(strings: strings).uppercased() self.sectionHeaderNode.action = actionTitle self.sectionHeaderNode.activateAction = action diff --git a/submodules/ChatListUI/BUILD b/submodules/ChatListUI/BUILD index a28100bb..87652ea3 100644 --- a/submodules/ChatListUI/BUILD +++ b/submodules/ChatListUI/BUILD @@ -29,7 +29,6 @@ swift_library( "//submodules/ActivityIndicator:ActivityIndicator", "//submodules/SearchBarNode:SearchBarNode", "//submodules/ChatListSearchRecentPeersNode:ChatListSearchRecentPeersNode", - "//submodules/ChatListSearchItemNode:ChatListSearchItemNode", "//submodules/ChatListSearchItemHeader:ChatListSearchItemHeader", "//submodules/TemporaryCachedPeerDataManager:TemporaryCachedPeerDataManager", "//submodules/PeerPresenceStatusManager:PeerPresenceStatusManager", @@ -118,6 +117,14 @@ swift_library( "//submodules/TelegramUI/Components/ButtonComponent", "//submodules/TelegramUI/Components/AnimatedTextComponent", "//submodules/TelegramUI/Components/EdgeEffect", + "//submodules/TelegramUI/Components/ChatList/ChatListFilterTabContainerNode", + "//submodules/TelegramUI/Components/HeaderPanelContainerComponent", + "//submodules/TelegramUI/Components/HorizontalTabsComponent", + "//submodules/TelegramUI/Components/GlobalControlPanelsContext", + "//submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent", + "//submodules/TelegramUI/Components/LiveLocationHeaderPanelComponent", + "//submodules/TelegramUI/Components/ChatList/ChatListSearchFiltersContainerNode", + "//submodules/TelegramUI/Components/ChatList/ChatListHeaderNoticeComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/ChatListUI/Sources/ChatListAdditionalCategoryItem.swift b/submodules/ChatListUI/Sources/ChatListAdditionalCategoryItem.swift index a1614b28..0dd445b2 100644 --- a/submodules/ChatListUI/Sources/ChatListAdditionalCategoryItem.swift +++ b/submodules/ChatListUI/Sources/ChatListAdditionalCategoryItem.swift @@ -169,7 +169,7 @@ public class ChatListAdditionalCategoryItemNode: ItemListRevealOptionsItemNode { self.titleNode = TextNode() - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.isAccessibilityElement = true diff --git a/submodules/ChatListUI/Sources/ChatListContainerItemNode.swift b/submodules/ChatListUI/Sources/ChatListContainerItemNode.swift index 9b6974a7..55f028e0 100644 --- a/submodules/ChatListUI/Sources/ChatListContainerItemNode.swift +++ b/submodules/ChatListUI/Sources/ChatListContainerItemNode.swift @@ -450,10 +450,10 @@ final class ChatListContainerItemNode: ASDisplayNode { self.layoutAdditionalPanels(transition: transition) - let edgeEffectHeight: CGFloat = insets.bottom + let edgeEffectHeight: CGFloat = insets.bottom + 8.0 let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - edgeEffectHeight), size: CGSize(width: size.width, height: edgeEffectHeight)) transition.updateFrame(view: self.edgeEffectView, frame: edgeEffectFrame) - self.edgeEffectView.update(content: self.presentationData.theme.list.plainBackgroundColor, rect: edgeEffectFrame, edge: .bottom, edgeSize: edgeEffectFrame.height, transition: ComponentTransition(transition)) + self.edgeEffectView.update(content: self.presentationData.theme.list.plainBackgroundColor, rect: edgeEffectFrame, edge: .bottom, edgeSize: min(edgeEffectFrame.height, 40.0), transition: ComponentTransition(transition)) transition.updateAlpha(layer: self.edgeEffectView.layer, alpha: edgeEffectHeight > 21.0 ? 1.0 : 0.0) } diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 1dae5215..c020faa7 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -55,6 +55,11 @@ import TextFormat import AvatarUploadToastScreen import AdsInfoScreen import AdsReportScreen +import SearchBarNode +import ChatListFilterTabContainerNode +import HeaderPanelContainerComponent +import HorizontalTabsComponent +import GlobalControlPanelsContext private final class ContextControllerContentSourceImpl: ContextControllerContentSource { let controller: ViewController @@ -150,9 +155,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController private let isReorderingTabsValue = ValuePromise(false) - let tabsNode: SparseNode - private let tabContainerNode: ChatListFilterTabContainerNode - private var tabContainerData: ([ChatListFilterTabEntry], Bool, Int32?)? + private(set) var tabContainerData: ([ChatListFilterTabEntry], Bool, Int32?)? var hasTabs: Bool { if let tabContainerData = self.tabContainerData { let isEmpty = tabContainerData.0.count <= 1 || tabContainerData.1 @@ -162,8 +165,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } } - var searchTabsNode: SparseNode? - private var hasDownloads: Bool = false private var activeDownloadsDisposable: Disposable? private var clearUnseenDownloadsTimer: SwiftSignalKit.Timer? @@ -221,6 +222,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController private var fullScreenEffectView: RippleEffectView? + let globalControlPanelsContext: GlobalControlPanelsContext + private(set) var globalControlPanelsContextState: GlobalControlPanelsContext.State? + private var globalControlPanelsContextStateDisposable: Disposable? + public override func updateNavigationCustomData(_ data: Any?, progress: CGFloat, transition: ContainedViewLayoutTransition) { if self.isNodeLoaded { self.chatListDisplayNode.effectiveContainerNode.updateSelectedChatLocation(data: data as? ChatLocation, progress: progress, transition: transition) @@ -241,21 +246,28 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self.animationCache = context.animationCache self.animationRenderer = context.animationRenderer - let groupCallPanelSource: GroupCallPanelSource + var groupCallPanelSource: EnginePeer.Id? + var chatListNotices = false switch self.location { - case .chatList: - groupCallPanelSource = .all + case let .chatList(groupId): + if case .root = groupId { + chatListNotices = true + } case let .forum(peerId): - groupCallPanelSource = .peer(peerId) + groupCallPanelSource = peerId case .savedMessagesChats: - groupCallPanelSource = .none + break } - self.tabsNode = SparseNode() - self.tabContainerNode = ChatListFilterTabContainerNode(context: context) - self.tabsNode.addSubnode(self.tabContainerNode) + self.globalControlPanelsContext = GlobalControlPanelsContext( + context: context, + mediaPlayback: true, + liveLocationMode: .all, + groupCalls: groupCallPanelSource, + chatListNotices: chatListNotices + ) - super.init(context: context, navigationBarPresentationData: nil, mediaAccessoryPanelVisibility: .always, locationBroadcastPanelSource: .summary, groupCallPanelSource: groupCallPanelSource) + super.init(context: context, navigationBarPresentationData: nil) self.accessoryPanelContainer = ASDisplayNode() @@ -437,23 +449,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController }).strict() if !previewing { - /* - self.searchContentNode = NavigationBarSearchContentNode(theme: self.presentationData.theme, placeholder: placeholder, compactPlaceholder: compactPlaceholder, activate: { [weak self] in - self?.chatListDisplayNode.mainContainerNode.currentItemNode.cancelTracking() - self?.activateSearch(filter: isForum ? .topics : .chats) - }) - self.searchContentNode?.updateExpansionProgress(0.0) - self.navigationBar?.setContentNode(self.searchContentNode, animated: false)*/ - - let tabsIsEmpty: Bool - if let (resolvedItems, displayTabsAtBottom, _) = self.tabContainerData { - tabsIsEmpty = resolvedItems.count <= 1 || displayTabsAtBottom - } else { - tabsIsEmpty = true - } - - self.navigationBar?.secondaryContentHeight = !tabsIsEmpty ? NavigationBar.defaultSecondaryContentHeight : 0.0 - enum State: Equatable { case empty(hasDownloads: Bool) case downloading(progress: Double) @@ -736,16 +731,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController guard let strongSelf = self else { return } - guard let layout = strongSelf.validLayout else { - return + + if let navigationBarView = strongSelf.chatListDisplayNode.navigationBarView.view as? ChatListNavigationBar.View, let headerPanelsView = navigationBarView.headerPanels as? HeaderPanelContainerComponent.View, let tabsView = headerPanelsView.tabs as? HorizontalTabsComponent.View { + tabsView.updateTabSwitchFraction(fraction: fraction, isDragging: strongSelf.chatListDisplayNode.mainContainerNode.isSwitchingCurrentItemFilterByDragging, transition: ComponentTransition(transition)) } - guard let tabContainerData = strongSelf.tabContainerData else { - return - } - if force { - strongSelf.tabContainerNode.cancelAnimations() - } - strongSelf.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: tabContainerData.0, selectedFilter: filter, isReordering: strongSelf.chatListDisplayNode.isReorderingFilters || (strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.currentState.editing && !strongSelf.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.currentState.editing, canReorderAllChats: strongSelf.isPremium, filtersLimit: tabContainerData.2, transitionFraction: fraction, presentationData: strongSelf.presentationData, transition: transition) } self.reloadFilters() } @@ -769,6 +758,17 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController }) self.updateNavigationMetadata() + + self.updateTabBarSearchState(ViewController.TabBarSearchState(isActive: false), transition: .immediate) + + self.globalControlPanelsContextStateDisposable = (self.globalControlPanelsContext.state + |> deliverOnMainQueue).startStrict(next: { [weak self] state in + guard let self else { + return + } + self.globalControlPanelsContextState = state + self.requestLayout(transition: .animated(duration: 0.4, curve: .spring)) + }) } required public init(coder aDecoder: NSCoder) { @@ -802,6 +802,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController for (_, disposable) in self.preloadStoryResourceDisposables { disposable.dispose() } + self.globalControlPanelsContextStateDisposable?.dispose() } private func updateNavigationMetadata() { @@ -934,11 +935,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) - - if let layout = self.validLayout { - self.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: self.chatListDisplayNode.effectiveContainerNode.currentItemFilter, isReordering: self.chatListDisplayNode.isReorderingFilters || (self.chatListDisplayNode.effectiveContainerNode.currentItemNode.currentState.editing && !self.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: self.chatListDisplayNode.effectiveContainerNode.currentItemNode.currentState.editing, canReorderAllChats: self.isPremium, filtersLimit: self.tabContainerData?.2, transitionFraction: self.chatListDisplayNode.effectiveContainerNode.transitionFraction, presentationData: self.presentationData, transition: .immediate) - } + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData), transition: .immediate) if self.isNodeLoaded { self.chatListDisplayNode.updatePresentationData(self.presentationData) @@ -947,6 +944,356 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self.requestLayout(transition: .immediate) } + func tabContextGesture(id: Int32?, sourceNode: ContextExtractedContentContainingNode?, sourceView: ContextExtractedContentContainingView?, gesture: ContextGesture?, keepInPlace: Bool, isDisabled: Bool) { + let context = self.context + let filterPeersAreMuted: Signal<(areMuted: Bool, peerIds: [EnginePeer.Id])?, NoError> = self.context.engine.peers.currentChatListFilters() + |> take(1) + |> mapToSignal { filters -> Signal<(areMuted: Bool, peerIds: [EnginePeer.Id])?, NoError> in + guard let filter = filters.first(where: { $0.id == id }) else { + return .single(nil) + } + guard case let .filter(_, _, _, data) = filter else { + return .single(nil) + } + + let filterPredicate: ChatListFilterPredicate = chatListFilterPredicate(filter: data, accountPeerId: context.account.peerId) + return context.engine.peers.getChatListPeers(filterPredicate: filterPredicate) + |> mapToSignal { peers -> Signal<(areMuted: Bool, peerIds: [EnginePeer.Id])?, NoError> in + let peerIds = peers.map(\.id) + return context.engine.data.get( + EngineDataMap(peerIds.map(TelegramEngine.EngineData.Item.Peer.NotificationSettings.init(id:))), + TelegramEngine.EngineData.Item.NotificationSettings.Global() + ) + |> map { list, globalSettings -> (areMuted: Bool, peerIds: [EnginePeer.Id])? in + for peer in peers { + switch list[peer.id]?.muteState { + case .unmuted: + return (false, peerIds) + case .default: + let globalValue: EngineGlobalNotificationSettings.CategorySettings + switch peer { + case .user, .secretChat: + globalValue = globalSettings.privateChats + case .legacyGroup: + globalValue = globalSettings.groupChats + case let .channel(channel): + if case .broadcast = channel.info { + globalValue = globalSettings.channels + } else { + globalValue = globalSettings.groupChats + } + } + if globalValue.enabled { + return (false, peerIds) + } + default: + break + } + } + return (true, peerIds) + } + } + } + + let _ = combineLatest( + queue: Queue.mainQueue(), + self.context.engine.peers.currentChatListFilters(), + self.context.engine.data.get( + TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: true) + ), + filterPeersAreMuted + ).startStandalone(next: { [weak self] filters, premiumLimits, filterPeersAreMuted in + guard let self else { + return + } + var items: [ContextMenuItem] = [] + if let id = id { + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.ChatList_EditFolder, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) + }, action: { c, f in + c?.dismiss(completion: { [weak self] in + guard let self else { + return + } + if isDisabled { + let context = self.context + var replaceImpl: ((ViewController) -> Void)? + let controller = PremiumLimitScreen(context: context, subject: .folders, count: Int32(self.tabContainerData?.0.count ?? 0), action: { + let controller = PremiumIntroScreen(context: context, source: .folders) + replaceImpl?(controller) + return true + }) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + self.push(controller) + } else { + let _ = (self.context.engine.peers.currentChatListFilters() + |> deliverOnMainQueue).startStandalone(next: { [weak self] presetList in + guard let self else { + return + } + var found = false + for filter in presetList { + if filter.id == id { + self.push(chatListFilterPresetController(context: self.context, currentPreset: filter, updated: { _ in })) + f(.dismissWithoutContent) + found = true + break + } + } + if !found { + f(.default) + } + }) + } + }) + }))) + + if let _ = filters.first(where: { $0.id == id }) { + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.ChatList_AddChatsToFolder, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Add"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] c, f in + c?.dismiss(completion: { + guard let self else { + return + } + + if isDisabled { + let context = self.context + var replaceImpl: ((ViewController) -> Void)? + let controller = PremiumLimitScreen(context: context, subject: .folders, count: Int32(self.tabContainerData?.0.count ?? 0), action: { + let controller = PremiumIntroScreen(context: context, source: .folders) + replaceImpl?(controller) + return true + }) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + self.push(controller) + } else { + let _ = combineLatest( + queue: Queue.mainQueue(), + self.context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId), + TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: false), + TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: true) + ), + self.context.engine.peers.currentChatListFilters() + ).startStandalone(next: { [weak self] result, presetList in + guard let self else { + return + } + var found = false + for filter in presetList { + if filter.id == id, case let .filter(_, _, _, data) = filter { + let (accountPeer, limits, premiumLimits) = result + let isPremium = accountPeer?.isPremium ?? false + + let limit = limits.maxFolderChatsCount + let premiumLimit = premiumLimits.maxFolderChatsCount + + if data.includePeers.peers.count >= premiumLimit { + let controller = PremiumLimitScreen(context: self.context, subject: .chatsPerFolder, count: Int32(data.includePeers.peers.count), action: { + return true + }) + self.push(controller) + f(.dismissWithoutContent) + return + } else if data.includePeers.peers.count >= limit && !isPremium { + var replaceImpl: ((ViewController) -> Void)? + let controller = PremiumLimitScreen(context: self.context, subject: .chatsPerFolder, count: Int32(data.includePeers.peers.count), action: { + let controller = PremiumIntroScreen(context: self.context, source: .chatsPerFolder) + replaceImpl?(controller) + return true + }) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + self.push(controller) + f(.dismissWithoutContent) + return + } + + let _ = (self.context.engine.peers.currentChatListFilters() + |> deliverOnMainQueue).startStandalone(next: { [weak self] filters in + guard let self else { + return + } + self.push(chatListFilterAddChatsController(context: self.context, filter: filter, allFilters: filters, limit: limits.maxFolderChatsCount, premiumLimit: premiumLimits.maxFolderChatsCount, isPremium: isPremium, presentUndo: { [weak self] content in + guard let self else { + return + } + self.present(UndoOverlayController(presentationData: self.presentationData, content: content, elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root)) + })) + f(.dismissWithoutContent) + }) + found = true + break + } + } + if !found { + f(.default) + } + }) + } + }) + }))) + + if let filterEntries = self.tabContainerData?.0 { + for filter in filterEntries { + if case let .filter(filterId, _, unread) = filter, filterId == id { + if unread.value > 0 { + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.ChatList_ReadAll, textColor: .primary, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReadAll"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] c, f in + c?.dismiss(completion: { + guard let self else { + return + } + self.readAllInFilter(id: id) + }) + }))) + } + + for filter in filters { + if filter.id == filterId, case let .filter(_, title, _, data) = filter { + if let filterPeersAreMuted, filterPeersAreMuted.peerIds.count <= 200 { + items.append(.action(ContextMenuActionItem(text: filterPeersAreMuted.areMuted ? self.presentationData.strings.ChatList_ContextUnmuteAll : self.presentationData.strings.ChatList_ContextMuteAll, textColor: .primary, badge: nil, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: filterPeersAreMuted.areMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] c, f in + c?.dismiss(completion: { + }) + + guard let self else { + return + } + + let _ = (self.context.engine.peers.updateMultiplePeerMuteSettings(peerIds: filterPeersAreMuted.peerIds, muted: !filterPeersAreMuted.areMuted) + |> deliverOnMainQueue).startStandalone(completed: { [weak self] in + guard let self else { + return + } + + let iconColor: UIColor = .white + let overlayController: UndoOverlayController + if !filterPeersAreMuted.areMuted { + let text = NSMutableAttributedString(string: self.presentationData.strings.ChatList_ToastFolderMutedV2) + let folderNameRange = (text.string as NSString).range(of: "{folder}") + if folderNameRange.location != NSNotFound { + text.replaceCharacters(in: folderNameRange, with: "") + text.insert(title.attributedString(attributes: [ + ChatTextInputAttributes.bold: true + ]), at: folderNameRange.location) + } + + overlayController = UndoOverlayController(presentationData: self.presentationData, content: .universalWithEntities(context: self.context, animation: "anim_profilemute", scale: 0.075, colors: [ + "Middle.Group 1.Fill 1": iconColor, + "Top.Group 1.Fill 1": iconColor, + "Bottom.Group 1.Fill 1": iconColor, + "EXAMPLE.Group 1.Fill 1": iconColor, + "Line.Group 1.Stroke 1": iconColor + ], title: nil, text: text, animateEntities: title.enableAnimations, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }) + } else { + let text = NSMutableAttributedString(string: self.presentationData.strings.ChatList_ToastFolderUnmutedV2) + let folderNameRange = (text.string as NSString).range(of: "{folder}") + if folderNameRange.location != NSNotFound { + text.replaceCharacters(in: folderNameRange, with: "") + text.insert(title.attributedString(attributes: [ + ChatTextInputAttributes.bold: true + ]), at: folderNameRange.location) + } + + overlayController = UndoOverlayController(presentationData: self.presentationData, content: .universalWithEntities(context: self.context, animation: "anim_profileunmute", scale: 0.075, colors: [ + "Middle.Group 1.Fill 1": iconColor, + "Top.Group 1.Fill 1": iconColor, + "Bottom.Group 1.Fill 1": iconColor, + "EXAMPLE.Group 1.Fill 1": iconColor, + "Line.Group 1.Stroke 1": iconColor + ], title: nil, text: text, animateEntities: title.enableAnimations, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }) + } + self.present(overlayController, in: .current) + }) + }))) + } + + if !data.includePeers.peers.isEmpty && data.categories.isEmpty && !data.excludeRead && !data.excludeMuted && !data.excludeArchived && data.excludePeers.isEmpty { + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.ChatList_ContextMenuShare, textColor: .primary, badge: nil, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] c, f in + c?.dismiss(completion: { + guard let self else { + return + } + self.shareFolder(filterId: filterId, data: data, title: title) + }) + }))) + } + + break + } + } + + break + } + } + } + + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.ChatList_RemoveFolder, textColor: .destructive, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) + }, action: { [weak self] c, f in + c?.dismiss(completion: { + guard let self else { + return + } + self.askForFilterRemoval(id: id) + }) + }))) + } + } else { + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.ChatList_EditFolders, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] c, f in + c?.dismiss(completion: { + guard let self else { + return + } + self.openFilterSettings() + }) + }))) + } + + if filters.count > 1 { + items.append(.separator) + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.ChatList_ReorderTabs, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] c, f in + c?.dismiss(completion: { + guard let self else { + return + } + + self.chatListDisplayNode.isReorderingFilters = true + self.isReorderingTabsValue.set(true) + + (self.parent as? TabBarController)?.updateIsTabBarEnabled(false, transition: .animated(duration: 0.2, curve: .easeInOut)) + if let layout = self.validLayout { + self.updateLayout(layout: layout, transition: .animated(duration: 0.2, curve: .easeInOut)) + } + }) + }))) + } + + if let sourceNode { + let controller = ContextController(presentationData: self.presentationData, source: .extracted(ChatListHeaderBarContextExtractedContentSource(controller: self, sourceNode: sourceNode, sourceView: sourceView, keepInPlace: keepInPlace)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture) + self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller) + } else if let sourceView { + let controller = ContextController(presentationData: self.presentationData, source: .reference(ChatListHeaderBarContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture) + self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller) + } + }) + } + override public func loadDisplayNode() { self.displayNode = ChatListControllerNode(context: self.context, location: self.location, previewing: self.previewing, controlsHistoryPreload: self.controlsHistoryPreload, presentationData: self.presentationData, animationCache: self.animationCache, animationRenderer: self.animationRenderer, controller: self) @@ -1653,7 +2000,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } else { let contextContentSource: ContextContentSource if peer.id.namespace == Namespaces.Peer.SecretChat, let node = node.subnodes?.first as? ContextExtractedContentContainingNode { - contextContentSource = .extracted(ChatListHeaderBarContextExtractedContentSource(controller: strongSelf, sourceNode: node, keepInPlace: false)) + contextContentSource = .extracted(ChatListHeaderBarContextExtractedContentSource(controller: strongSelf, sourceNode: node, sourceView: nil, keepInPlace: false)) } else { var subject: ChatControllerSubject? if case let .search(messageId) = source, let id = messageId { @@ -1669,406 +2016,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } } - self.tabContainerNode.tabSelected = { [weak self] id, isDisabled in - guard let strongSelf = self else { - return - } - if isDisabled { - let context = strongSelf.context - var replaceImpl: ((ViewController) -> Void)? - let controller = PremiumLimitScreen(context: context, subject: .folders, count: strongSelf.tabContainerNode.filtersCount, action: { - let controller = PremiumIntroScreen(context: context, source: .folders) - replaceImpl?(controller) - return true - }) - replaceImpl = { [weak controller] c in - controller?.replace(with: c) - } - strongSelf.push(controller) - } else { - strongSelf.selectTab(id: id) - } - } - - self.tabContainerNode.tabRequestedDeletion = { [weak self] id in - if case let .filter(id) = id { - self?.askForFilterRemoval(id: id) - } - } - self.tabContainerNode.presentPremiumTip = { [weak self] in - if let strongSelf = self { - strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_reorder", scale: 0.05, colors: [:], title: nil, text: strongSelf.presentationData.strings.ChatListFolderSettings_SubscribeToMoveAll, customUndoText: strongSelf.presentationData.strings.ChatListFolderSettings_SubscribeToMoveAllAction, timeout: nil), elevatedLayout: false, position: .top, animateInAsReplacement: false, action: { action in - if case .undo = action { - let context = strongSelf.context - var replaceImpl: ((ViewController) -> Void)? - let controller = PremiumDemoScreen(context: context, subject: .advancedChatManagement, action: { - let controller = PremiumIntroScreen(context: context, source: .folders) - replaceImpl?(controller) - }) - replaceImpl = { [weak controller] c in - controller?.replace(with: c) - } - strongSelf.push(controller) - } - return false }), in: .current) - } - } - - let tabContextGesture: (Int32?, ContextExtractedContentContainingNode, ContextGesture, Bool, Bool) -> Void = { [weak self] id, sourceNode, gesture, keepInPlace, isDisabled in - guard let strongSelf = self else { - return - } - - let context = strongSelf.context - let filterPeersAreMuted: Signal<(areMuted: Bool, peerIds: [EnginePeer.Id])?, NoError> = strongSelf.context.engine.peers.currentChatListFilters() - |> take(1) - |> mapToSignal { filters -> Signal<(areMuted: Bool, peerIds: [EnginePeer.Id])?, NoError> in - guard let filter = filters.first(where: { $0.id == id }) else { - return .single(nil) - } - guard case let .filter(_, _, _, data) = filter else { - return .single(nil) - } - - let filterPredicate: ChatListFilterPredicate = chatListFilterPredicate(filter: data, accountPeerId: context.account.peerId) - return context.engine.peers.getChatListPeers(filterPredicate: filterPredicate) - |> mapToSignal { peers -> Signal<(areMuted: Bool, peerIds: [EnginePeer.Id])?, NoError> in - let peerIds = peers.map(\.id) - return context.engine.data.get( - EngineDataMap(peerIds.map(TelegramEngine.EngineData.Item.Peer.NotificationSettings.init(id:))), - TelegramEngine.EngineData.Item.NotificationSettings.Global() - ) - |> map { list, globalSettings -> (areMuted: Bool, peerIds: [EnginePeer.Id])? in - for peer in peers { - switch list[peer.id]?.muteState { - case .unmuted: - return (false, peerIds) - case .default: - let globalValue: EngineGlobalNotificationSettings.CategorySettings - switch peer { - case .user, .secretChat: - globalValue = globalSettings.privateChats - case .legacyGroup: - globalValue = globalSettings.groupChats - case let .channel(channel): - if case .broadcast = channel.info { - globalValue = globalSettings.channels - } else { - globalValue = globalSettings.groupChats - } - } - if globalValue.enabled { - return (false, peerIds) - } - default: - break - } - } - return (true, peerIds) - } - } - } - - let _ = combineLatest( - queue: Queue.mainQueue(), - strongSelf.context.engine.peers.currentChatListFilters(), - strongSelf.context.engine.data.get( - TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: true) - ), - filterPeersAreMuted - ).startStandalone(next: { [weak self] filters, premiumLimits, filterPeersAreMuted in - guard let strongSelf = self else { - return - } - var items: [ContextMenuItem] = [] - if let id = id { - items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChatList_EditFolder, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) - }, action: { c, f in - c?.dismiss(completion: { - guard let strongSelf = self else { - return - } - if isDisabled { - let context = strongSelf.context - var replaceImpl: ((ViewController) -> Void)? - let controller = PremiumLimitScreen(context: context, subject: .folders, count: strongSelf.tabContainerNode.filtersCount, action: { - let controller = PremiumIntroScreen(context: context, source: .folders) - replaceImpl?(controller) - return true - }) - replaceImpl = { [weak controller] c in - controller?.replace(with: c) - } - strongSelf.push(controller) - } else { - let _ = (strongSelf.context.engine.peers.currentChatListFilters() - |> deliverOnMainQueue).startStandalone(next: { presetList in - guard let strongSelf = self else { - return - } - var found = false - for filter in presetList { - if filter.id == id { - strongSelf.push(chatListFilterPresetController(context: strongSelf.context, currentPreset: filter, updated: { _ in })) - f(.dismissWithoutContent) - found = true - break - } - } - if !found { - f(.default) - } - }) - } - }) - }))) - - if let _ = filters.first(where: { $0.id == id }) { - items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChatList_AddChatsToFolder, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Add"), color: theme.contextMenu.primaryColor) - }, action: { c, f in - c?.dismiss(completion: { - guard let strongSelf = self else { - return - } - - if isDisabled { - let context = strongSelf.context - var replaceImpl: ((ViewController) -> Void)? - let controller = PremiumLimitScreen(context: context, subject: .folders, count: strongSelf.tabContainerNode.filtersCount, action: { - let controller = PremiumIntroScreen(context: context, source: .folders) - replaceImpl?(controller) - return true - }) - replaceImpl = { [weak controller] c in - controller?.replace(with: c) - } - strongSelf.push(controller) - } else { - let _ = combineLatest( - queue: Queue.mainQueue(), - strongSelf.context.engine.data.get( - TelegramEngine.EngineData.Item.Peer.Peer(id: strongSelf.context.account.peerId), - TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: false), - TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: true) - ), - strongSelf.context.engine.peers.currentChatListFilters() - ).startStandalone(next: { result, presetList in - guard let strongSelf = self else { - return - } - var found = false - for filter in presetList { - if filter.id == id, case let .filter(_, _, _, data) = filter { - let (accountPeer, limits, premiumLimits) = result - let isPremium = accountPeer?.isPremium ?? false - - let limit = limits.maxFolderChatsCount - let premiumLimit = premiumLimits.maxFolderChatsCount - - if data.includePeers.peers.count >= premiumLimit { - let controller = PremiumLimitScreen(context: strongSelf.context, subject: .chatsPerFolder, count: Int32(data.includePeers.peers.count), action: { - return true - }) - strongSelf.push(controller) - f(.dismissWithoutContent) - return - } else if data.includePeers.peers.count >= limit && !isPremium { - var replaceImpl: ((ViewController) -> Void)? - let controller = PremiumLimitScreen(context: strongSelf.context, subject: .chatsPerFolder, count: Int32(data.includePeers.peers.count), action: { - let controller = PremiumIntroScreen(context: strongSelf.context, source: .chatsPerFolder) - replaceImpl?(controller) - return true - }) - replaceImpl = { [weak controller] c in - controller?.replace(with: c) - } - strongSelf.push(controller) - f(.dismissWithoutContent) - return - } - - let _ = (strongSelf.context.engine.peers.currentChatListFilters() - |> deliverOnMainQueue).startStandalone(next: { filters in - guard let strongSelf = self else { - return - } - strongSelf.push(chatListFilterAddChatsController(context: strongSelf.context, filter: filter, allFilters: filters, limit: limits.maxFolderChatsCount, premiumLimit: premiumLimits.maxFolderChatsCount, isPremium: isPremium, presentUndo: { content in - guard let strongSelf = self else { - return - } - strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: content, elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root)) - })) - f(.dismissWithoutContent) - }) - found = true - break - } - } - if !found { - f(.default) - } - }) - } - }) - }))) - - if let filterEntries = strongSelf.tabContainerData?.0 { - for filter in filterEntries { - if case let .filter(filterId, _, unread) = filter, filterId == id { - if unread.value > 0 { - items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChatList_ReadAll, textColor: .primary, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReadAll"), color: theme.contextMenu.primaryColor) - }, action: { c, f in - c?.dismiss(completion: { - guard let strongSelf = self else { - return - } - strongSelf.readAllInFilter(id: id) - }) - }))) - } - - for filter in filters { - if filter.id == filterId, case let .filter(_, title, _, data) = filter { - if let filterPeersAreMuted, filterPeersAreMuted.peerIds.count <= 200 { - items.append(.action(ContextMenuActionItem(text: filterPeersAreMuted.areMuted ? strongSelf.presentationData.strings.ChatList_ContextUnmuteAll : strongSelf.presentationData.strings.ChatList_ContextMuteAll, textColor: .primary, badge: nil, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: filterPeersAreMuted.areMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor) - }, action: { c, f in - c?.dismiss(completion: { - }) - - guard let strongSelf = self else { - return - } - - let _ = (strongSelf.context.engine.peers.updateMultiplePeerMuteSettings(peerIds: filterPeersAreMuted.peerIds, muted: !filterPeersAreMuted.areMuted) - |> deliverOnMainQueue).startStandalone(completed: { - guard let strongSelf = self else { - return - } - - let iconColor: UIColor = .white - let overlayController: UndoOverlayController - if !filterPeersAreMuted.areMuted { - let text = NSMutableAttributedString(string: strongSelf.presentationData.strings.ChatList_ToastFolderMutedV2) - let folderNameRange = (text.string as NSString).range(of: "{folder}") - if folderNameRange.location != NSNotFound { - text.replaceCharacters(in: folderNameRange, with: "") - text.insert(title.attributedString(attributes: [ - ChatTextInputAttributes.bold: true - ]), at: folderNameRange.location) - } - - overlayController = UndoOverlayController(presentationData: strongSelf.presentationData, content: .universalWithEntities(context: strongSelf.context, animation: "anim_profilemute", scale: 0.075, colors: [ - "Middle.Group 1.Fill 1": iconColor, - "Top.Group 1.Fill 1": iconColor, - "Bottom.Group 1.Fill 1": iconColor, - "EXAMPLE.Group 1.Fill 1": iconColor, - "Line.Group 1.Stroke 1": iconColor - ], title: nil, text: text, animateEntities: title.enableAnimations, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }) - } else { - let text = NSMutableAttributedString(string: strongSelf.presentationData.strings.ChatList_ToastFolderUnmutedV2) - let folderNameRange = (text.string as NSString).range(of: "{folder}") - if folderNameRange.location != NSNotFound { - text.replaceCharacters(in: folderNameRange, with: "") - text.insert(title.attributedString(attributes: [ - ChatTextInputAttributes.bold: true - ]), at: folderNameRange.location) - } - - overlayController = UndoOverlayController(presentationData: strongSelf.presentationData, content: .universalWithEntities(context: strongSelf.context, animation: "anim_profileunmute", scale: 0.075, colors: [ - "Middle.Group 1.Fill 1": iconColor, - "Top.Group 1.Fill 1": iconColor, - "Bottom.Group 1.Fill 1": iconColor, - "EXAMPLE.Group 1.Fill 1": iconColor, - "Line.Group 1.Stroke 1": iconColor - ], title: nil, text: text, animateEntities: title.enableAnimations, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }) - } - strongSelf.present(overlayController, in: .current) - }) - }))) - } - - if !data.includePeers.peers.isEmpty && data.categories.isEmpty && !data.excludeRead && !data.excludeMuted && !data.excludeArchived && data.excludePeers.isEmpty { - items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChatList_ContextMenuShare, textColor: .primary, badge: data.hasSharedLinks ? nil : ContextMenuActionBadge(value: strongSelf.presentationData.strings.ChatList_ContextMenuBadgeNew, color: .accent, style: .label), icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor) - }, action: { c, f in - c?.dismiss(completion: { - guard let strongSelf = self else { - return - } - strongSelf.shareFolder(filterId: filterId, data: data, title: title) - }) - }))) - } - - break - } - } - - break - } - } - } - - items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChatList_RemoveFolder, textColor: .destructive, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) - }, action: { c, f in - c?.dismiss(completion: { - guard let strongSelf = self else { - return - } - strongSelf.askForFilterRemoval(id: id) - }) - }))) - } - } else { - items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChatList_EditFolders, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) - }, action: { c, f in - c?.dismiss(completion: { - guard let strongSelf = self else { - return - } - strongSelf.openFilterSettings() - }) - }))) - } - - if filters.count > 1 { - items.append(.separator) - items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChatList_ReorderTabs, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.contextMenu.primaryColor) - }, action: { c, f in - c?.dismiss(completion: { - guard let strongSelf = self else { - return - } - - strongSelf.chatListDisplayNode.isReorderingFilters = true - strongSelf.isReorderingTabsValue.set(true) - - //TODO:update search enabled - //strongSelf.searchContentNode?.setIsEnabled(false, animated: true) - - (strongSelf.parent as? TabBarController)?.updateIsTabBarEnabled(false, transition: .animated(duration: 0.2, curve: .easeInOut)) - if let layout = strongSelf.validLayout { - strongSelf.updateLayout(layout: layout, transition: .animated(duration: 0.2, curve: .easeInOut)) - } - }) - }))) - } - - let controller = ContextController(presentationData: strongSelf.presentationData, source: .extracted(ChatListHeaderBarContextExtractedContentSource(controller: strongSelf, sourceNode: sourceNode, keepInPlace: keepInPlace)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture) - strongSelf.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller) - }) - } - self.tabContainerNode.contextGesture = { id, sourceNode, gesture, isDisabled in - tabContextGesture(id, sourceNode, gesture, false, isDisabled) - } - if case .chatList(.root) = self.location { self.ready.set(combineLatest([self.mainReady.get(), self.storiesReady.get()]) |> map { values -> Bool in @@ -2433,7 +2380,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } let context = strongSelf.context var replaceImpl: ((ViewController) -> Void)? - let controller = PremiumLimitScreen(context: context, subject: .folders, count: strongSelf.tabContainerNode.filtersCount, action: { + let controller = PremiumLimitScreen(context: context, subject: .folders, count: Int32(strongSelf.tabContainerData?.0.count ?? 0), action: { let controller = PremiumIntroScreen(context: context, source: .folders) replaceImpl?(controller) return true @@ -2598,13 +2545,25 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController guard let strongSelf, currentValues.contains(.setupPasskey) else { return } - if let navigationController = strongSelf.navigationController as? NavigationController { - let controller = strongSelf.context.sharedContext.makePasskeySetupController(context: strongSelf.context, displaySkip: true, navigationController: navigationController, completion: { - let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupPasskey.id).startStandalone() - }, dismiss: { - let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupPasskey.id).startStandalone() - }) - navigationController.pushViewController(controller) + + Task { @MainActor [weak strongSelf] in + guard let strongSelf else { + return + } + + let passkeysData = await strongSelf.context.engine.auth.passkeysData().get() + if !passkeysData.isEmpty { + return + } + + if let navigationController = strongSelf.navigationController as? NavigationController { + let controller = strongSelf.context.sharedContext.makePasskeySetupController(context: strongSelf.context, displaySkip: true, navigationController: navigationController, completion: { + let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupPasskey.id).startStandalone() + }, dismiss: { + let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupPasskey.id).startStandalone() + }) + navigationController.pushViewController(controller) + } } }) @@ -2618,7 +2577,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController return } strongSelf.didSuggestAutoarchive = true - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_AutoarchiveSuggestion_Title, text: strongSelf.presentationData.strings.ChatList_AutoarchiveSuggestion_Text, actions: [ + strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.ChatList_AutoarchiveSuggestion_Title, text: strongSelf.presentationData.strings.ChatList_AutoarchiveSuggestion_Text, actions: [ TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { guard let strongSelf = self else { return @@ -2670,7 +2629,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController return true } - controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ForcedPasswordSetup_Intro_DismissTitle, text: strongSelf.presentationData.strings.ForcedPasswordSetup_Intro_DismissText(value), actions: [ + controller.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.ForcedPasswordSetup_Intro_DismissTitle, text: strongSelf.presentationData.strings.ForcedPasswordSetup_Intro_DismissText(value), actions: [ TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.ForcedPasswordSetup_Intro_DismissActionCancel, action: { }), TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ForcedPasswordSetup_Intro_DismissActionOK, action: { [weak controller] in @@ -2863,14 +2822,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController func updateHeaderContent() -> (primaryContent: ChatListHeaderComponent.Content?, secondaryContent: ChatListHeaderComponent.Content?) { var primaryContent: ChatListHeaderComponent.Content? if let primaryContext = self.primaryContext { - var backTitle: String? - if let previousItem = self.previousItem { - switch previousItem { - case let .item(item): - backTitle = item.title ?? self.presentationData.strings.Common_Back - case .close: - backTitle = self.presentationData.strings.Common_Close - } + var displayBackButton: Bool = false + if self.previousItem != nil { + displayBackButton = true } var navigationBackTitle: String? if case .chatList(.archive) = self.location { @@ -2883,8 +2837,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController chatListTitle: primaryContext.chatListTitle, leftButton: primaryContext.leftButton, rightButtons: primaryContext.rightButtons, - backTitle: backTitle, - backPressed: backTitle != nil ? { [weak self] in + backPressed: displayBackButton ? { [weak self] in guard let self else { return } @@ -2901,7 +2854,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController chatListTitle: secondaryContext.chatListTitle, leftButton: secondaryContext.leftButton, rightButtons: secondaryContext.rightButtons, - backTitle: nil, backPressed: { [weak self] in guard let self else { return @@ -2934,7 +2886,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController self.push(controller) return } - + var reachedCountLimit = false var premiumNeeded = false var hasActiveCall = false @@ -3083,7 +3035,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } func displayContinueLiveStream() { - self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: self.presentationData.strings.ChatList_AlertResumeLiveStreamTitle, text: self.presentationData.strings.ChatList_AlertResumeLiveStreamText, actions: [ + self.present(textAlertController(context: self.context, title: self.presentationData.strings.ChatList_AlertResumeLiveStreamTitle, text: self.presentationData.strings.ChatList_AlertResumeLiveStreamText, actions: [ TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { }), TextAlertAction(type: .defaultAction, title: presentationData.strings.ChatList_AlertResumeLiveStreamAction, action: { [weak self] in @@ -3473,7 +3425,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController }))) } - let controller = ContextController(presentationData: self.presentationData, source: .extracted(ChatListHeaderBarContextExtractedContentSource(controller: self, sourceNode: sourceNode, keepInPlace: false)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture) + let controller = ContextController(presentationData: self.presentationData, source: .extracted(ChatListHeaderBarContextExtractedContentSource(controller: self, sourceNode: sourceNode, sourceView: nil, keepInPlace: false)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture) self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller) }) } @@ -3550,14 +3502,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController tabContainerOffset += 44.0 + 20.0 } - let navigationBarHeight: CGFloat = 0.0//self.navigationBar?.frame.maxY ?? 0.0 - //let secondaryContentHeight = self.navigationBar?.secondaryContentHeight ?? 0.0 - - transition.updateFrame(node: self.tabContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: 46.0))) - - if !skipTabContainerUpdate { - self.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: self.chatListDisplayNode.mainContainerNode.currentItemFilter, isReordering: self.chatListDisplayNode.isReorderingFilters || (self.chatListDisplayNode.effectiveContainerNode.currentItemNode.currentState.editing && !self.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: self.chatListDisplayNode.effectiveContainerNode.currentItemNode.currentState.editing, canReorderAllChats: self.isPremium, filtersLimit: self.tabContainerData?.2, transitionFraction: self.chatListDisplayNode.effectiveContainerNode.transitionFraction, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring)) - } + let navigationBarHeight: CGFloat = 0.0 self.chatListDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: navigationBarHeight, cleanNavigationBarHeight: navigationBarHeight, storiesInset: 0.0, transition: transition) } @@ -3643,10 +3588,35 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController return id } } + let _ = defaultFilterIds var reorderedFilterIdsValue: [Int32]? - if let reorderedFilterIds = self.tabContainerNode.reorderedFilterIds, reorderedFilterIds != defaultFilterIds { - reorderedFilterIdsValue = reorderedFilterIds + if let navigationBarView = self.chatListDisplayNode.navigationBarView.view as? ChatListNavigationBar.View, let headerPanelsView = navigationBarView.headerPanels as? HeaderPanelContainerComponent.View, let tabsView = headerPanelsView.tabs as? HorizontalTabsComponent.View, let reorderedItemIds = tabsView.reorderedItemIds { + reorderedFilterIdsValue = reorderedItemIds.compactMap { item -> Int32? in + guard let value = item.base as? Int32 else { + return nil + } + if value == Int32.min { + return 0 + } + return value + } + } + + if let reorderedFilterIdsValue, let tabContainerData = self.tabContainerData { + var entries: [ChatListFilterTabEntry] = [] + for id in reorderedFilterIdsValue { + let mappedId: ChatListFilterTabEntryId + if id == 0 { + mappedId = .all + } else { + mappedId = .filter(id) + } + if let entry = tabContainerData.0.first(where: { $0.id == mappedId }) { + entries.append(entry) + } + } + self.tabContainerData?.0 = entries } let completion = { [weak self] in @@ -4095,11 +4065,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController if let layout = strongSelf.validLayout { if wasEmpty != isEmpty { let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.2, curve: .easeInOut) : .immediate - transition.updateAlpha(node: strongSelf.tabContainerNode, alpha: isEmpty ? 0.0 : 1.0) strongSelf.containerLayoutUpdated(layout, transition: transition) (strongSelf.parent as? TabBarController)?.updateLayout(transition: transition) - } else { - strongSelf.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: resolvedItems, selectedFilter: selectedEntryId, isReordering: strongSelf.chatListDisplayNode.isReorderingFilters || (strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.currentState.editing && !strongSelf.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: strongSelf.chatListDisplayNode.mainContainerNode.currentItemNode.currentState.editing, canReorderAllChats: strongSelf.isPremium, filtersLimit: filtersLimit, transitionFraction: strongSelf.chatListDisplayNode.mainContainerNode.transitionFraction, presentationData: strongSelf.presentationData, transition: .animated(duration: 0.4, curve: .spring)) } } @@ -4114,7 +4081,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController })) } - private func selectTab(id: ChatListFilterTabEntryId, switchToChatsIfNeeded: Bool = true) { + func selectTab(id: ChatListFilterTabEntryId, switchToChatsIfNeeded: Bool = true) { if self.parent == nil, switchToChatsIfNeeded { if let navigationController = self.context.sharedContext.mainWindow?.viewController as? NavigationController { for controller in navigationController.viewControllers { @@ -4510,7 +4477,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController }) } - private func askForFilterRemoval(id: Int32) { + func askForFilterRemoval(id: Int32) { let apply: () -> Void = { [weak self] in guard let strongSelf = self else { return @@ -4621,7 +4588,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } if hasLinks { - self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.ChatList_AlertDeleteFolderTitle, text: presentationData.strings.ChatList_AlertDeleteFolderText, actions: [ + self.present(textAlertController(context: self.context, title: presentationData.strings.ChatList_AlertDeleteFolderTitle, text: presentationData.strings.ChatList_AlertDeleteFolderText, actions: [ TextAlertAction(type: .destructiveAction, title: presentationData.strings.Common_Delete, action: { confirmDeleteFolder() }), @@ -4658,14 +4625,16 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController public private(set) var isSearchActive: Bool = false public func activateSearch(filter: ChatListSearchFilter, query: String? = nil) { + self.activateSearchInternal(isFromTabBar: false, filter: filter, query: query) + } + + public func activateSearchInternal(isFromTabBar: Bool, filter: ChatListSearchFilter, query: String? = nil) { var searchContentNode: NavigationBarSearchContentNode? - if let navigationBarView = self.chatListDisplayNode.navigationBarView.view as? ChatListNavigationBar.View { + if !isFromTabBar, let navigationBarView = self.chatListDisplayNode.navigationBarView.view as? ChatListNavigationBar.View { searchContentNode = navigationBarView.searchContentNode } - if let searchContentNode { - self.activateSearch(filter: filter, query: query, skipScrolling: false, searchContentNode: searchContentNode) - } + self.activateSearch(filter: filter, query: query, skipScrolling: false, searchContentNode: searchContentNode) } public func activateSearch(query: String? = nil) { @@ -4679,88 +4648,70 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } private var previousSearchToggleTimestamp: Double? - func activateSearch(filter: ChatListSearchFilter = .chats, query: String? = nil, skipScrolling: Bool = false, searchContentNode: NavigationBarSearchContentNode) { - let currentTimestamp = CACurrentMediaTime() - if let previousSearchActivationTimestamp = self.previousSearchToggleTimestamp, currentTimestamp < previousSearchActivationTimestamp + 0.6 { - return - } - self.previousSearchToggleTimestamp = currentTimestamp - - if let storyTooltip = self.storyTooltip { - storyTooltip.dismiss() - } - - var filter = filter - if case .forum = self.chatListDisplayNode.effectiveContainerNode.location { - filter = .topics - } - - if self.chatListDisplayNode.searchDisplayController == nil { - /*if !skipScrolling, let searchContentNode = self.searchContentNode, searchContentNode.expansionProgress != 1.0 { - self.scrollToTop?() - DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.2, execute: { [weak self] in - self?.activateSearch(filter: filter, query: query, skipScrolling: true) - }) + func activateSearch(filter: ChatListSearchFilter = .chats, query: String? = nil, skipScrolling: Bool = false, searchContentNode: NavigationBarSearchContentNode?) { + Task { @MainActor [weak self] in + guard let self else { return - }*/ - //TODO:scroll to top? + } + + let currentTimestamp = CACurrentMediaTime() + if let previousSearchActivationTimestamp = self.previousSearchToggleTimestamp, currentTimestamp < previousSearchActivationTimestamp + 0.6 { + return + } + self.previousSearchToggleTimestamp = currentTimestamp - let _ = (combineLatest(self.chatListDisplayNode.mainContainerNode.currentItemNode.contentsReady |> take(1), self.context.account.postbox.tailChatListView(groupId: .root, count: 16, summaryComponents: ChatListEntrySummaryComponents(components: [:])) |> take(1)) - |> deliverOnMainQueue).startStandalone(next: { [weak self] _, chatListView in - Task { @MainActor in - guard let strongSelf = self else { - return - } - - /*if let scrollToTop = strongSelf.scrollToTop { - scrollToTop() - }*/ - - let tabsIsEmpty: Bool - if let (resolvedItems, displayTabsAtBottom, _) = strongSelf.tabContainerData { - tabsIsEmpty = resolvedItems.count <= 1 || displayTabsAtBottom - } else { - tabsIsEmpty = true - } - let _ = tabsIsEmpty - //TODO:swap tabs - + if let storyTooltip = self.storyTooltip { + storyTooltip.dismiss() + } + + var filter = filter + if case .forum = self.chatListDisplayNode.effectiveContainerNode.location { + filter = .topics + } + + if self.chatListDisplayNode.searchDisplayController == nil { + let (_, _) = await combineLatest(self.chatListDisplayNode.mainContainerNode.currentItemNode.contentsReady |> take(1), self.context.account.postbox.tailChatListView(groupId: .root, count: 16, summaryComponents: ChatListEntrySummaryComponents(components: [:])) |> take(1)).get() + + do { let displaySearchFilters = true - if let filterContainerNodeAndActivate = await strongSelf.chatListDisplayNode.activateSearch(placeholderNode: searchContentNode.placeholderNode, displaySearchFilters: displaySearchFilters, hasDownloads: strongSelf.hasDownloads, initialFilter: filter, navigationController: strongSelf.navigationController as? NavigationController) { - let (filterContainerNode, activate) = filterContainerNodeAndActivate - if displaySearchFilters { - let searchTabsNode = SparseNode() - strongSelf.searchTabsNode = searchTabsNode - searchTabsNode.addSubnode(filterContainerNode) - } + if let filterContainerNodeAndActivate = await self.chatListDisplayNode.activateSearch(placeholderNode: searchContentNode?.placeholderNode, displaySearchFilters: displaySearchFilters, hasDownloads: self.hasDownloads, initialFilter: filter, navigationController: self.navigationController as? NavigationController, searchBarIsExternal: searchContentNode == nil) { + let activate = filterContainerNodeAndActivate activate(filter != .downloads) - if let searchContentNode = strongSelf.chatListDisplayNode.searchDisplayController?.contentNode as? ChatListSearchContainerNode { + if let searchContentNode = self.chatListDisplayNode.searchDisplayController?.contentNode as? ChatListSearchContainerNode { searchContentNode.search(filter: filter, query: query) } } let transition: ContainedViewLayoutTransition = .animated(duration: 0.4, curve: .spring) - strongSelf.setDisplayNavigationBar(false, transition: transition) - - (strongSelf.parent as? TabBarController)?.updateIsTabBarHidden(true, transition: .animated(duration: 0.4, curve: .spring)) - } - }) - - self.isSearchActive = true - if let navigationController = self.navigationController as? NavigationController { - for controller in navigationController.globalOverlayControllers { - if let controller = controller as? VoiceChatOverlayController { - controller.updateVisibility() - break + self.setDisplayNavigationBar(false, transition: transition) + if searchContentNode == nil { + self.updateTabBarSearchState(ViewController.TabBarSearchState(isActive: true), transition: transition) + + if let searchBarNode = self.currentTabBarSearchNode?() as? SearchBarNode { + self.chatListDisplayNode.searchDisplayController?.setSearchBar(searchBarNode) + searchBarNode.activate() + } + } else { + (self.parent as? TabBarController)?.updateIsTabBarHidden(true, transition: transition) + } + + self.isSearchActive = true + if let navigationController = self.navigationController as? NavigationController { + for controller in navigationController.globalOverlayControllers { + if let controller = controller as? VoiceChatOverlayController { + controller.updateVisibility() + break + } + } } } - } - } else if self.isSearchActive { - if let searchContentNode = self.chatListDisplayNode.searchDisplayController?.contentNode as? ChatListSearchContainerNode { - searchContentNode.search(filter: filter, query: query) + } else if self.isSearchActive { + if let searchContentNode = self.chatListDisplayNode.searchDisplayController?.contentNode as? ChatListSearchContainerNode { + searchContentNode.search(filter: filter, query: query) + } } } } @@ -4777,8 +4728,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController var completion: (() -> Void)? - self.searchTabsNode = nil - var searchContentNode: NavigationBarSearchContentNode? if let navigationBarView = self.chatListDisplayNode.navigationBarView.view as? ChatListNavigationBar.View { searchContentNode = navigationBarView.searchContentNode @@ -4791,21 +4740,21 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController } completion = self.chatListDisplayNode.deactivateSearch(placeholderNode: searchContentNode.placeholderNode, animated: animated) searchContentNode.placeholderNode.frame = previousFrame + } else { + completion = self.chatListDisplayNode.deactivateSearch(placeholderNode: nil, animated: animated) } self.chatListDisplayNode.tempAllowAvatarExpansion = true self.requestLayout(transition: .animated(duration: 0.5, curve: .spring)) self.chatListDisplayNode.tempAllowAvatarExpansion = false - //TODO:swap tabs - let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.4, curve: .spring) : .immediate - //transition.updateAlpha(node: self.tabContainerNode, alpha: tabsIsEmpty ? 0.0 : 1.0) self.setDisplayNavigationBar(true, transition: transition) completion?() - (self.parent as? TabBarController)?.updateIsTabBarHidden(false, transition: .animated(duration: 0.4, curve: .spring)) + self.updateTabBarSearchState(ViewController.TabBarSearchState(isActive: false), transition: transition) + (self.parent as? TabBarController)?.updateIsTabBarHidden(false, transition: transition) self.isSearchActive = false if let navigationController = self.navigationController as? NavigationController { @@ -5498,7 +5447,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController deleteForAllConfirmation = strongSelf.presentationData.strings.ChannelInfo_DeleteGroupConfirmation } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationTitle, text: deleteForAllConfirmation, actions: [ + strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationTitle, text: deleteForAllConfirmation, actions: [ TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { }), TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationAction, action: { @@ -5613,7 +5562,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController ]) strongSelf.present(actionSheet, in: .window(.root)) } else { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationText, actions: [ + strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationText, actions: [ TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { }), TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationAction, action: { @@ -5681,7 +5630,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController deleteForAllConfirmation = strongSelf.presentationData.strings.ChatList_DeleteForAllMembersConfirmationText } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationTitle, text: deleteForAllConfirmation, actions: [ + strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationTitle, text: deleteForAllConfirmation, actions: [ TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { }), TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationAction, action: { @@ -5889,7 +5838,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController guard let strongSelf = self else { return } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationText, actions: [ + strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationText, actions: [ TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { completion(false) }), @@ -5913,7 +5862,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController ]) self.present(actionSheet, in: .window(.root)) } else if peer.peerId == self.context.account.peerId { - self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: self.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle, text: self.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationText, actions: [ + self.present(textAlertController(context: self.context, title: self.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle, text: self.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationText, actions: [ TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: { completion(false) }), @@ -6237,7 +6186,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController if isDisabled { let context = strongSelf.context var replaceImpl: ((ViewController) -> Void)? - let controller = PremiumLimitScreen(context: context, subject: .folders, count: strongSelf.tabContainerNode.filtersCount, action: { + let controller = PremiumLimitScreen(context: context, subject: .folders, count: Int32(strongSelf.tabContainerData?.0.count ?? 0), action: { let controller = PremiumIntroScreen(context: context, source: .folders) replaceImpl?(controller) return true @@ -6261,6 +6210,14 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController strongSelf.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller) }) } + + override public func tabBarActivateSearch() { + self.activateSearchInternal(isFromTabBar: true, filter: .chats, query: nil) + } + + override public func tabBarDeactivateSearch() { + self.deactivateSearch(animated: true) + } private var playedSignUpCompletedAnimation = false public func playSignUpCompletedAnimation() { @@ -6541,22 +6498,49 @@ private final class ChatListTabBarContextReferenceContentSource: ContextReferenc } } +private final class ChatListHeaderBarContextReferenceContentSource: ContextReferenceContentSource { + let keepInPlace: Bool = true + let actionsHorizontalAlignment: ContextActionsHorizontalAlignment = .center + + private let controller: ChatListController + private let sourceView: ContextExtractedContentContainingView + + init(controller: ChatListController, sourceView: ContextExtractedContentContainingView) { + self.controller = controller + self.sourceView = sourceView + } + + func transitionInfo() -> ContextControllerReferenceViewInfo? { + return ContextControllerReferenceViewInfo( + referenceView: self.sourceView.contentView, + contentAreaInScreenSpace: UIScreen.main.bounds, + actionsPosition: .bottom + ) + } +} + private final class ChatListHeaderBarContextExtractedContentSource: ContextExtractedContentSource { let keepInPlace: Bool let ignoreContentTouches: Bool = true let blurBackground: Bool = true private let controller: ChatListController - private let sourceNode: ContextExtractedContentContainingNode + private let sourceNode: ContextExtractedContentContainingNode? + private let sourceView: ContextExtractedContentContainingView? - init(controller: ChatListController, sourceNode: ContextExtractedContentContainingNode, keepInPlace: Bool) { + init(controller: ChatListController, sourceNode: ContextExtractedContentContainingNode?, sourceView: ContextExtractedContentContainingView?, keepInPlace: Bool) { self.controller = controller self.sourceNode = sourceNode + self.sourceView = sourceView self.keepInPlace = keepInPlace } func takeView() -> ContextControllerTakeViewInfo? { - return ContextControllerTakeViewInfo(containingItem: .node(self.sourceNode), contentAreaInScreenSpace: UIScreen.main.bounds) + if let sourceNode = self.sourceNode { + return ContextControllerTakeViewInfo(containingItem: .node(sourceNode), contentAreaInScreenSpace: UIScreen.main.bounds) + } else { + return ContextControllerTakeViewInfo(containingItem: .view(self.sourceView!), contentAreaInScreenSpace: UIScreen.main.bounds) + } } func putBack() -> ContextControllerPutBackViewInfo? { @@ -6954,6 +6938,7 @@ private final class ChatListLocationContext { } if strongSelf.toolbar != toolbar { strongSelf.toolbar = toolbar + transition = .animated(duration: 0.4, curve: .spring) if parentController.effectiveContext === strongSelf { parentController.setToolbar(toolbar, transition: transition) } @@ -7221,7 +7206,10 @@ private final class ChatListLocationContext { strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, - content: .custom(presentationData.strings.ChatList_SelectedTopics(Int32(stateAndFilterId.state.selectedThreadIds.count)), nil, false), + displayBackground: false, + content: .custom(title: [ChatTitleContent.TitleTextItem(id: AnyHashable(0), content: .text(presentationData.strings.ChatList_SelectedTopics(Int32(stateAndFilterId.state.selectedThreadIds.count))))], subtitle: nil, isEnabled: false), + activities: nil, + networkState: nil, tapped: { }, longTapped: { @@ -7234,7 +7222,10 @@ private final class ChatListLocationContext { strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, + displayBackground: false, content: .peer(peerView: ChatTitleContent.PeerData(peerView: peerView), customTitle: nil, customSubtitle: nil, onlineMemberCount: onlineMemberCount, isScheduledMessages: false, isMuted: nil, customMessageCount: nil, isEnabled: true), + activities: nil, + networkState: nil, tapped: { [weak self] in guard let self else { return diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift index 3a835731..9847039d 100644 --- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift @@ -21,7 +21,13 @@ import ChatFolderLinkPreviewScreen import ChatListHeaderComponent import StoryPeerListComponent import TelegramNotices -import EdgeEffect +import HeaderPanelContainerComponent +import HorizontalTabsComponent +import PremiumUI +import MediaPlaybackHeaderPanelComponent +import LiveLocationHeaderPanelComponent +import ChatListHeaderNoticeComponent +import ChatListFilterTabContainerNode public enum ChatListContainerNodeFilter: Equatable { case all @@ -132,6 +138,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele } public var currentItemFilterUpdated: ((ChatListFilterTabEntryId, CGFloat, ContainedViewLayoutTransition, Bool) -> Void)? + public private(set) var isSwitchingCurrentItemFilterByDragging: Bool = false public var currentItemFilter: ChatListFilterTabEntryId { return self.currentItemNode.chatListFilter.flatMap { .filter($0.id) } ?? .all } @@ -575,6 +582,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele itemNode.layer.removeAllAnimations() } self.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, originalNavigationHeight: originalNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: insets, isReorderingFilters: isReorderingFilters, isEditing: isEditing, inlineNavigationLocation: inlineNavigationLocation, inlineNavigationTransitionFraction: inlineNavigationTransitionFraction, storiesInset: storiesInset, transition: .immediate) + self.isSwitchingCurrentItemFilterByDragging = true self.currentItemFilterUpdated?(self.currentItemFilter, self.transitionFraction, .immediate, true) } } @@ -651,6 +659,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele } } self.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, originalNavigationHeight: originalNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: insets, isReorderingFilters: isReorderingFilters, isEditing: isEditing, inlineNavigationLocation: inlineNavigationLocation, inlineNavigationTransitionFraction: inlineNavigationTransitionFraction, storiesInset: storiesInset, transition: .immediate) + self.isSwitchingCurrentItemFilterByDragging = true self.currentItemFilterUpdated?(self.currentItemFilter, self.transitionFraction, transition, false) } case .cancelled, .ended: @@ -712,6 +721,7 @@ public final class ChatListContainerNode: ASDisplayNode, ASGestureRecognizerDele if let switchToId = applyNodeAsCurrent, let itemNode = self.itemNodes[switchToId] { self.applyItemNodeAsCurrent(id: switchToId, itemNode: itemNode) } + self.isSwitchingCurrentItemFilterByDragging = false self.currentItemFilterUpdated?(self.currentItemFilter, self.transitionFraction, transition, false) } default: @@ -1091,9 +1101,10 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { private var toolbarNode: ToolbarNode? var toolbarActionSelected: ((ToolbarActionOption) -> Void)? - private var isSearchDisplayControllerActive: Bool = false + private var isSearchDisplayControllerActive: ChatListNavigationBar.ActiveSearch? private var skipSearchDisplayControllerLayout: Bool = false private(set) var searchDisplayController: SearchDisplayController? + private var disappearingSearchDisplayController: SearchDisplayController? var isReorderingFilters: Bool = false var didBeginSelectingChatsWhileEditing: Bool = false @@ -1353,14 +1364,226 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { private func updateNavigationBar(layout: ContainerViewLayout, deferScrollApplication: Bool, transition: ComponentTransition) -> (navigationHeight: CGFloat, storiesInset: CGFloat) { let headerContent = self.controller?.updateHeaderContent() - var tabsNode: ASDisplayNode? - var tabsNodeIsSearch = false + var panels: [HeaderPanelContainerComponent.Panel] = [] + if let chatListNotice = self.controller?.globalControlPanelsContextState?.chatListNotice { + panels.append(HeaderPanelContainerComponent.Panel( + key: "chatListNotice", + orderIndex: 0, + component: AnyComponent(ChatListHeaderNoticeComponent( + context: self.context, + theme: self.presentationData.theme, + strings: self.presentationData.strings, + data: chatListNotice, + activateAction: { [weak self] notice in + guard let self else { + return + } + switch notice { + case .clearStorage: + self.effectiveContainerNode.currentItemNode.interaction?.openStorageManagement() + case .setupPassword: + self.effectiveContainerNode.currentItemNode.interaction?.openPasswordSetup() + case .premiumUpgrade, .premiumAnnualDiscount, .premiumRestore: + self.effectiveContainerNode.currentItemNode.interaction?.openPremiumIntro() + case .xmasPremiumGift: + self.effectiveContainerNode.currentItemNode.interaction?.openPremiumGift([], nil) + case .premiumGrace: + self.effectiveContainerNode.currentItemNode.interaction?.openPremiumManagement() + case .setupBirthday: + self.effectiveContainerNode.currentItemNode.interaction?.openBirthdaySetup() + case let .birthdayPremiumGift(peers, birthdays): + self.effectiveContainerNode.currentItemNode.interaction?.openPremiumGift(peers, birthdays) + case .reviewLogin: + break + case let .starsSubscriptionLowBalance(amount, _): + self.effectiveContainerNode.currentItemNode.interaction?.openStarsTopup(amount.value) + case .setupPhoto: + self.effectiveContainerNode.currentItemNode.interaction?.openPhotoSetup() + case .accountFreeze: + self.effectiveContainerNode.currentItemNode.interaction?.openAccountFreezeInfo() + case let .link(_, url, _, _): + self.effectiveContainerNode.currentItemNode.interaction?.openUrl(url) + } + }, + dismissAction: { [weak self] notice in + guard let self, let controller = self.controller else { + return + } + controller.globalControlPanelsContext.dismissChatListNotice(parentController: controller, notice: notice) + }, + selectAction: { [weak self] notice, isPositive in + guard let self else { + return + } + switch notice { + case let .reviewLogin(newSessionReview, _): + self.effectiveContainerNode.currentItemNode.interaction?.performActiveSessionAction(newSessionReview, isPositive) + default: + break + } + } + ))) + ) + } + if let mediaPlayback = self.controller?.globalControlPanelsContextState?.mediaPlayback { + panels.append(HeaderPanelContainerComponent.Panel( + key: "media", + orderIndex: 1, + component: AnyComponent(MediaPlaybackHeaderPanelComponent( + context: self.context, + theme: self.presentationData.theme, + strings: self.presentationData.strings, + data: mediaPlayback, + controller: { [weak self] in + return self?.controller + } + ))) + ) + } + if let liveLocation = self.controller?.globalControlPanelsContextState?.liveLocation { + panels.append(HeaderPanelContainerComponent.Panel( + key: "liveLocation", + orderIndex: 2, + component: AnyComponent(LiveLocationHeaderPanelComponent( + context: self.context, + theme: self.presentationData.theme, + strings: self.presentationData.strings, + data: liveLocation, + controller: { [weak self] in + return self?.controller + } + ))) + ) + } - if let value = self.controller?.searchTabsNode { - tabsNode = value - tabsNodeIsSearch = true - } else if let value = self.controller?.tabsNode, self.controller?.hasTabs == true { - tabsNode = value + var navigationHeaderPanels: AnyComponent? + if self.controller?.tabContainerData != nil || !panels.isEmpty { + var tabs: AnyComponent? + if let tabContainerData = self.controller?.tabContainerData, tabContainerData.0.count > 1 { + let selectedTab: HorizontalTabsComponent.Tab.Id + switch self.effectiveContainerNode.currentItemFilter { + case .all: + selectedTab = AnyHashable(Int32.min) + case let .filter(id): + selectedTab = AnyHashable(id) + } + + let isEditing = self.isReorderingFilters || (self.mainContainerNode.currentItemNode.currentState.editing && !self.didBeginSelectingChatsWhileEditing) + + tabs = AnyComponent(HorizontalTabsComponent( + context: self.context, + theme: self.presentationData.theme, + tabs: tabContainerData.0.map { entry -> HorizontalTabsComponent.Tab in + let id: HorizontalTabsComponent.Tab.Id + let title: HorizontalTabsComponent.Tab.Title + var badge: HorizontalTabsComponent.Tab.Badge? + var isMainTab = false + switch entry { + case .all: + id = Int32.min + title = HorizontalTabsComponent.Tab.Title(text: self.presentationData.strings.ChatList_Tabs_All, entities: [], enableAnimations: false) + isMainTab = true + case let .filter(idValue, text, unread): + id = AnyHashable(idValue) + title = HorizontalTabsComponent.Tab.Title(text: text.text, entities: text.entities, enableAnimations: text.enableAnimations) + if unread.value != 0 { + badge = HorizontalTabsComponent.Tab.Badge( + title: "\(unread.value)", + isAccent: unread.hasUnmuted + ) + } + } + + return HorizontalTabsComponent.Tab( + id: id, + content: .title(title), + badge: badge, + action: { [weak self] in + guard let self, let tabContainerData = self.controller?.tabContainerData else { + return + } + + let isPremium = self.context.isPremium + + let mappedId: ChatListFilterTabEntryId = entry.id + + var isDisabled = false + if let filtersLimit = tabContainerData.2 { + guard let folderIndex = tabContainerData.0.firstIndex(where: { $0.id == mappedId }) else { + return + } + isDisabled = !isPremium && folderIndex >= filtersLimit + } + + if isDisabled { + let filtersCount = tabContainerData.0.count(where: { item in + if case .all = item { + return false + } else { + return true + } + }) + let context = self.context + var replaceImpl: ((ViewController) -> Void)? + let controller = PremiumLimitScreen(context: context, subject: .folders, count: Int32(filtersCount), action: { + let controller = PremiumIntroScreen(context: context, source: .folders) + replaceImpl?(controller) + return true + }) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + self.controller?.push(controller) + } else { + self.controller?.selectTab(id: mappedId) + } + }, + contextAction: { [weak self] sourceView, gesture in + guard let self, let tabContainerData = self.controller?.tabContainerData else { + return + } + + let isPremium = self.context.isPremium + + let mappedId: Int32? + switch entry { + case .all: + mappedId = nil + case let .filter(idValue, _, _): + mappedId = idValue + } + + var isDisabled = false + if let filtersLimit = tabContainerData.2 { + guard let folderIndex = tabContainerData.0.firstIndex(where: { $0.id == entry.id }) else { + return + } + isDisabled = !isPremium && folderIndex >= filtersLimit + } + + self.controller?.tabContextGesture(id: mappedId, sourceNode: nil, sourceView: sourceView, gesture: gesture, keepInPlace: false, isDisabled: isDisabled) + }, + deleteAction: (!isEditing || isMainTab) ? nil : { [weak self] in + guard let self else { + return + } + if case let .filter(id) = entry.id { + self.controller?.askForFilterRemoval(id: id) + } + } + ) + }, + selectedTab: selectedTab, + isEditing: isEditing, + liftWhileSwitching: layout.deviceMetrics.type != .tablet + )) + } + + navigationHeaderPanels = AnyComponent(HeaderPanelContainerComponent( + theme: self.presentationData.theme, + tabs: tabs, + panels: panels + )) } var effectiveStorySubscriptions: EngineStorySubscriptions? @@ -1382,16 +1605,17 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { strings: self.presentationData.strings, statusBarHeight: layout.statusBarHeight ?? 0.0, sideInset: layout.safeInsets.left, - isSearchActive: self.isSearchDisplayControllerActive, - isSearchEnabled: true, + search: ChatListNavigationBar.Search(isEnabled: true), + activeSearch: self.isSearchDisplayControllerActive, primaryContent: headerContent?.primaryContent, secondaryContent: headerContent?.secondaryContent, secondaryTransition: self.inlineStackContainerTransitionFraction, storySubscriptions: effectiveStorySubscriptions, storiesIncludeHidden: self.location == .chatList(groupId: .archive), uploadProgress: self.controller?.storyUploadProgress ?? [:], - tabsNode: tabsNode, - tabsNodeIsSearch: tabsNodeIsSearch, + headerPanels: navigationHeaderPanels, + tabsNode: nil, + tabsNodeIsSearch: false, accessoryPanelContainer: self.controller?.accessoryPanelContainer, accessoryPanelContainerHeight: self.controller?.accessoryPanelContainerHeight ?? 0.0, activateSearch: { [weak self] searchContentNode in @@ -1479,7 +1703,7 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { } var offset = resultingOffset - if self.isSearchDisplayControllerActive { + if self.isSearchDisplayControllerActive != nil { offset = 0.0 } @@ -1656,6 +1880,9 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: cleanNavigationBarHeight, transition: transition) } } + if let disappearingSearchDisplayController = self.disappearingSearchDisplayController { + disappearingSearchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: cleanNavigationBarHeight, transition: transition) + } self.updateNavigationScrolling(navigationHeight: navigationBarLayout.navigationHeight, transition: transition) @@ -1666,7 +1893,7 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { } @MainActor - func activateSearch(placeholderNode: SearchBarPlaceholderNode, displaySearchFilters: Bool, hasDownloads: Bool, initialFilter: ChatListSearchFilter, navigationController: NavigationController?) async -> (ASDisplayNode, (Bool) -> Void)? { + func activateSearch(placeholderNode: SearchBarPlaceholderNode?, displaySearchFilters: Bool, hasDownloads: Bool, initialFilter: ChatListSearchFilter, navigationController: NavigationController?, searchBarIsExternal: Bool) async -> ((Bool) -> Void)? { guard let (containerLayout, _, _, cleanNavigationBarHeight, _) = self.containerLayout, self.searchDisplayController == nil else { return nil } @@ -1712,16 +1939,16 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { if let requestDeactivateSearch = self?.requestDeactivateSearch { requestDeactivateSearch() } - }) + }, fieldStyle: placeholderNode?.fieldStyle ?? .modern, searchBarIsExternal: searchBarIsExternal) self.mainContainerNode.accessibilityElementsHidden = true self.inlineStackContainerNode?.accessibilityElementsHidden = true - return (contentNode.filterContainerNode, { [weak self] focus in + return ({ [weak self] focus in guard let strongSelf = self else { return } - strongSelf.isSearchDisplayControllerActive = true + strongSelf.isSearchDisplayControllerActive = ChatListNavigationBar.ActiveSearch(isExternal: placeholderNode == nil) strongSelf.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: cleanNavigationBarHeight, transition: .immediate) strongSelf.searchDisplayController?.activate(insertSubnode: { [weak self] subnode, isSearchBar in @@ -1731,7 +1958,7 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { if isSearchBar { if let navigationBarComponentView = self.navigationBarView.view as? ChatListNavigationBar.View { - navigationBarComponentView.addSubnode(subnode) + navigationBarComponentView.searchContentNode?.addSubnode(subnode) } } else { self.insertSubnode(subnode, aboveSubnode: self.debugListView) @@ -1742,21 +1969,31 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { }) } - func deactivateSearch(placeholderNode: SearchBarPlaceholderNode, animated: Bool) -> (() -> Void)? { + func deactivateSearch(placeholderNode: SearchBarPlaceholderNode?, animated: Bool) -> (() -> Void)? { if let searchDisplayController = self.searchDisplayController { - self.isSearchDisplayControllerActive = false + self.isSearchDisplayControllerActive = nil self.searchDisplayController = nil + self.disappearingSearchDisplayController = searchDisplayController self.mainContainerNode.accessibilityElementsHidden = false self.inlineStackContainerNode?.accessibilityElementsHidden = false return { [weak self, weak placeholderNode] in - if let strongSelf = self, let placeholderNode, let (layout, _, _, cleanNavigationBarHeight, _) = strongSelf.containerLayout { - searchDisplayController.deactivate(placeholder: placeholderNode, animated: animated) - - searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: cleanNavigationBarHeight, transition: .animated(duration: 0.4, curve: .spring)) - - strongSelf.controller?.requestLayout(transition: .animated(duration: 0.4, curve: .spring)) + guard let self, let (layout, _, _, cleanNavigationBarHeight, _) = self.containerLayout else { + return } + let placeholderNode = placeholderNode + searchDisplayController.deactivate(placeholder: placeholderNode, animated: animated, completion: { [weak self, weak searchDisplayController] in + guard let self, let searchDisplayController else { + return + } + if self.disappearingSearchDisplayController === searchDisplayController { + self.disappearingSearchDisplayController = nil + } + }) + + searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: cleanNavigationBarHeight, transition: .animated(duration: 0.4, curve: .spring)) + + self.controller?.requestLayout(transition: .animated(duration: 0.4, curve: .spring)) } } else { return nil diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetCategoryItem.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetCategoryItem.swift index 24e3612d..6b75d9a1 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetCategoryItem.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetCategoryItem.swift @@ -148,7 +148,7 @@ class ChatListFilterPresetCategoryItemNode: ItemListRevealOptionsItemNode, ItemL self.highlightedBackgroundNode = ASDisplayNode() self.highlightedBackgroundNode.isLayerBacked = true - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.isAccessibilityElement = true diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift index 30ffe647..da255877 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift @@ -1283,7 +1283,6 @@ private final class ChatListFilterPresetController: ItemListController { pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, - importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, @@ -1753,14 +1752,14 @@ func chatListFilterPresetController(context: AccountContext, currentPreset initi if initialPreset == nil { let presentationData = context.sharedContext.currentPresentationData.with { $0 } let text = presentationData.strings.ChatListFilter_AlertCreateFolderBeforeSharingText - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) } else { let presentationData = context.sharedContext.currentPresentationData.with { $0 } let state = stateValue.with({ $0 }) if state.additionallyIncludePeers.isEmpty { let text = presentationData.strings.ChatListFilter_ErrorShareInvalidFolder - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) return } @@ -1785,7 +1784,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset initi statusController?.dismiss() let presentationData = context.sharedContext.currentPresentationData.with { $0 } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: unavailableText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + presentControllerImpl?(textAlertController(context: context, title: nil, text: unavailableText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) return } @@ -2360,7 +2359,7 @@ func openCreateChatListFolderLink(context: AccountContext, folderId: Int32, chec case .someUserTooManyChannels: text = presentationData.strings.ChatListFilter_CreateLinkErrorSomeoneHasChannelLimit } - presentController(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])) + presentController(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])) }) } }) diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift index 56c5c9ba..8750c6f8 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift @@ -5,6 +5,7 @@ import SwiftSignalKit import TelegramCore import TelegramPresentationData import TelegramUIPreferences +import PresentationDataUtils import ItemListUI import AccountContext import ItemListPeerActionItem @@ -516,13 +517,13 @@ public func chatListFilterPresetListController(context: AccountContext, mode: Ch } if hasLinks { - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.ChatList_AlertDeleteFolderTitle, text: presentationData.strings.ChatList_AlertDeleteFolderText, actions: [ + presentControllerImpl?(textAlertController(context: context, title: presentationData.strings.ChatList_AlertDeleteFolderTitle, text: presentationData.strings.ChatList_AlertDeleteFolderText, actions: [ TextAlertAction(type: .destructiveAction, title: presentationData.strings.Common_Delete, action: { confirmDeleteFolder() }), - TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: { + TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { }) - ])) + ], actionLayout: .vertical)) } else { confirmDeleteFolder() } diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetListItem.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetListItem.swift index 55540442..84e53950 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetListItem.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetListItem.swift @@ -201,7 +201,7 @@ final class ChatListFilterPresetListItemNode: ItemListRevealOptionsItemNode { self.highlightedBackgroundNode = ASDisplayNode() self.highlightedBackgroundNode.isLayerBacked = true - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.containerNode) self.containerNode.addSubnode(self.titleNode.textNode) diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetListSuggestedItem.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetListSuggestedItem.swift index 34f96f72..4cce378d 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetListSuggestedItem.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetListSuggestedItem.swift @@ -132,7 +132,7 @@ public class ChatListFilterPresetListSuggestedItemNode: ListViewItemNode, ItemLi self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.titleNode) self.addSubnode(self.labelNode) diff --git a/submodules/ChatListUI/Sources/ChatListFilterTagSectionHeaderItem.swift b/submodules/ChatListUI/Sources/ChatListFilterTagSectionHeaderItem.swift index 93438c9a..3c7fd1fc 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterTagSectionHeaderItem.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterTagSectionHeaderItem.swift @@ -113,7 +113,7 @@ public class ChatListFilterTagSectionHeaderItemNode: ListViewItemNode { self.activateArea = AccessibilityAreaNode() self.activateArea.accessibilityTraits = [.staticText, .header] - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.titleNode) self.addSubnode(self.accessoryTextNode) diff --git a/submodules/ChatListUI/Sources/ChatListRecentPeersListItem.swift b/submodules/ChatListUI/Sources/ChatListRecentPeersListItem.swift index 52541007..0cac0a1b 100644 --- a/submodules/ChatListUI/Sources/ChatListRecentPeersListItem.swift +++ b/submodules/ChatListUI/Sources/ChatListRecentPeersListItem.swift @@ -79,7 +79,7 @@ class ChatListRecentPeersListItemNode: ListViewItemNode { self.separatorNode = ASDisplayNode() self.separatorNode.isLayerBacked = true - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.backgroundNode) self.addSubnode(self.separatorNode) diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index aab2bb49..11af4ee9 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -36,6 +36,10 @@ import MultiAnimationRenderer import PremiumUI import AvatarNode import StoryContainerScreen +import ChatListSearchFiltersContainerNode +import EdgeEffect +import ComponentFlow +import ComponentDisplayAdapters private enum ChatListTokenId: Int32 { case archive @@ -107,8 +111,9 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo var dismissSearch: (() -> Void)? var openAdInfo: ((ASDisplayNode, AdPeer) -> Void)? - private let dimNode: ASDisplayNode - let filterContainerNode: ChatListSearchFiltersContainerNode + private let edgeEffectView: EdgeEffectView + + private let filterContainerNode: ChatListSearchFiltersContainerNode private let paneContainerNode: ChatListSearchPaneContainerNode private var selectionPanelNode: ChatListSearchMessageSelectionPanelNode? @@ -181,9 +186,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo self.openMessage = originalOpenMessage self.present = present self.presentInGlobalOverlay = presentInGlobalOverlay - - self.dimNode = ASDisplayNode() - self.dimNode.backgroundColor = UIColor.black.withAlphaComponent(0.5) + + self.edgeEffectView = EdgeEffectView() self.filterContainerNode = ChatListSearchFiltersContainerNode() self.paneContainerNode = ChatListSearchPaneContainerNode(context: context, animationCache: animationCache, animationRenderer: animationRenderer, updatedPresentationData: updatedPresentationData, peersFilter: self.peersFilter, requestPeerType: self.requestPeerType, location: location, searchQuery: self.searchQuery.get(), searchOptions: self.searchOptions.get(), navigationController: navigationController, parentController: parentController()) @@ -193,7 +197,6 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo self.backgroundColor = filter.contains(.excludeRecent) ? nil : self.presentationData.theme.chatList.backgroundColor -// self.addSubnode(self.dimNode) self.addSubnode(self.paneContainerNode) let interaction = ChatListSearchInteraction(openPeer: { peer, chatPeer, threadId, value in @@ -325,6 +328,9 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo parentController()?.view.endEditing(true) } + self.view.addSubview(self.edgeEffectView) + + self.addSubnode(self.filterContainerNode) self.filterContainerNode.filterPressed = { [weak self] filter in guard let strongSelf = self else { return @@ -553,9 +559,6 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo public override func didLoad() { super.didLoad() - - - self.dimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:)))) } public override var hasDim: Bool { @@ -705,18 +708,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo self.transitionFraction = transitionFraction if let (layout, _) = self.validLayout { - let filters: [ChatListSearchFilter] - if let suggestedFilters = self.suggestedFilters, !suggestedFilters.isEmpty { - filters = suggestedFilters - } else { - var isForum = false - if case .forum = self.location { - isForum = true - } - - filters = defaultAvailableSearchPanes(isForum: isForum, hasDownloads: !isForum && self.hasDownloads, hasPublicPosts: self.showPublicPostsTab).map(\.filter) - } - self.filterContainerNode.update(size: CGSize(width: layout.size.width - 40.0, height: 38.0), sideInset: layout.safeInsets.left - 20.0, filters: filters.map { .filter($0) }, displayGlobalPostsNewBadge: self.displayGlobalPostsNewBadge, selectedFilter: self.selectedFilter?.id, transitionFraction: self.transitionFraction, presentationData: self.presentationData, transition: transition) + self.updateFilterContainerNode(layout: layout, transition: transition) } } @@ -762,18 +754,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo self.cancel?() } } - - override public func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { - super.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition) - - let isFirstTime = self.validLayout == nil - self.validLayout = (layout, navigationBarHeight) - - let topInset = navigationBarHeight - - transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset), size: CGSize(width: layout.size.width, height: layout.size.height - topInset))) - transition.updateFrame(node: self.filterContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight + 6.0), size: CGSize(width: layout.size.width, height: 38.0))) - + + private func updateFilterContainerNode(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { var isForum = false if case .forum = self.location { isForum = true @@ -786,8 +768,46 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo filters = defaultAvailableSearchPanes(isForum: isForum, hasDownloads: self.hasDownloads, hasPublicPosts: self.showPublicPostsTab).map(\.filter) } - let overflowInset: CGFloat = 20.0 - self.filterContainerNode.update(size: CGSize(width: layout.size.width - overflowInset * 2.0, height: 38.0), sideInset: layout.safeInsets.left - overflowInset, filters: filters.map { .filter($0) }, displayGlobalPostsNewBadge: self.displayGlobalPostsNewBadge, selectedFilter: self.selectedFilter?.id, transitionFraction: self.transitionFraction, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring)) + var filtersInsets = UIEdgeInsets(top: 0.0, left: 12.0, bottom: layout.insets(options: [.input]).bottom + 34.0, right: 12.0) + if layout.insets(options: [.input]).bottom <= 30.0 { + filtersInsets = ContainerViewLayout.concentricInsets(bottomInset: layout.insets(options: [.input]).bottom, innerDiameter: 40.0, sideInset: 32.0) + } else if layout.insets(options: [.input]).bottom <= 84.0 { + filtersInsets.left = 20.0 + filtersInsets.right = filtersInsets.left + } + + self.filterContainerNode.update(size: CGSize(width: layout.size.width - (layout.safeInsets.left + filtersInsets.left) * 2.0, height: 40.0), sideInset: 0.0, filters: filters.map { .filter($0) }, displayGlobalPostsNewBadge: self.displayGlobalPostsNewBadge, selectedFilter: self.selectedFilter?.id, transitionFraction: self.transitionFraction, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring)) + } + + override public func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { + super.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition) + + let isFirstTime = self.validLayout == nil + self.validLayout = (layout, navigationBarHeight) + + let topInset = navigationBarHeight + + var filtersInsets = UIEdgeInsets(top: 0.0, left: 12.0, bottom: layout.insets(options: [.input]).bottom, right: 12.0) + if filtersInsets.bottom == 84.0 { + filtersInsets.bottom -= 6.0 + } + if layout.insets(options: [.input]).bottom <= 30.0 { + filtersInsets = ContainerViewLayout.concentricInsets(bottomInset: layout.insets(options: [.input]).bottom, innerDiameter: 40.0, sideInset: 32.0) + } else if layout.insets(options: [.input]).bottom <= 84.0 { + filtersInsets.left = 20.0 + filtersInsets.right = filtersInsets.left + } else { + if let inputHeight = layout.inputHeight, filtersInsets.bottom == inputHeight { + filtersInsets.bottom += 8.0 + } + filtersInsets.bottom = max(8.0, filtersInsets.bottom) + } + if self.stateValue.selectedMessageIds != nil { + filtersInsets.bottom += 48.0 + } + + transition.updateFrame(node: self.filterContainerNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + filtersInsets.left, y: layout.size.height - filtersInsets.bottom - 40.0), size: CGSize(width: layout.size.width - (layout.safeInsets.left + filtersInsets.left) * 2.0, height: 40.0))) + self.updateFilterContainerNode(layout: layout, transition: transition) if isFirstTime { self.filterContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) @@ -795,13 +815,13 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo } var bottomIntrinsicInset = layout.intrinsicInsets.bottom - if case .chatList(.root) = self.location { - if layout.safeInsets.left > overflowInset { + /*if case .chatList(.root) = self.location { + if layout.safeInsets.left > 20.0 { bottomIntrinsicInset -= 34.0 } else { bottomIntrinsicInset -= 49.0 } - } + }*/ if let selectedMessageIds = self.stateValue.selectedMessageIds { var wasAdded = false @@ -927,7 +947,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo return strongSelf.context.sharedContext.chatAvailableMessageActions(engine: strongSelf.context.engine, accountPeerId: strongSelf.context.account.peerId, messageIds: messageIds, messages: messages, peers: peers) } self.selectionPanelNode = selectionPanelNode - self.addSubnode(selectionPanelNode) + self.insertSubnode(selectionPanelNode, aboveSubnode: self.filterContainerNode) } selectionPanelNode.selectedMessages = selectedMessageIds @@ -948,25 +968,36 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo }) } - transition.updateFrame(node: self.paneContainerNode, frame: CGRect(x: 0.0, y: topInset, width: layout.size.width, height: layout.size.height - topInset)) + transition.updateFrame(node: self.paneContainerNode, frame: CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height)) var bottomInset = layout.intrinsicInsets.bottom if let inputHeight = layout.inputHeight { bottomInset = inputHeight } else if let _ = self.selectionPanelNode { bottomInset = bottomIntrinsicInset - } else if case .chatList(.root) = self.location { - bottomInset -= bottomIntrinsicInset } + bottomInset += 10.0 let availablePanes: [ChatListSearchPaneKey] + var isForum = false + if case .forum = self.location { + isForum = true + } if self.displaySearchFilters { availablePanes = defaultAvailableSearchPanes(isForum: isForum, hasDownloads: self.hasDownloads, hasPublicPosts: self.hasPublicPostsTab) } else { availablePanes = isForum ? [.topics] : [.chats] } + + bottomInset += 44.0 + + let edgeEffectHeight: CGFloat = bottomInset + 8.0 + let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - edgeEffectHeight), size: CGSize(width: layout.size.width, height: edgeEffectHeight)) + transition.updateFrame(view: self.edgeEffectView, frame: edgeEffectFrame) + self.edgeEffectView.update(content: self.presentationData.theme.list.plainBackgroundColor, rect: edgeEffectFrame, edge: .bottom, edgeSize: min(edgeEffectHeight, 50.0), transition: ComponentTransition(transition)) + transition.updateAlpha(layer: self.edgeEffectView.layer, alpha: edgeEffectHeight > 21.0 ? 1.0 : 0.0) - self.paneContainerNode.update(size: CGSize(width: layout.size.width, height: layout.size.height - topInset), sideInset: layout.safeInsets.left, bottomInset: bottomInset, visibleHeight: layout.size.height - topInset, presentationData: self.presentationData, availablePanes: availablePanes, transition: transition) + self.paneContainerNode.update(size: CGSize(width: layout.size.width, height: layout.size.height), sideInset: layout.safeInsets.left, topInset: topInset, bottomInset: bottomInset, visibleHeight: layout.size.height, presentationData: self.presentationData, availablePanes: availablePanes, transition: transition) } private var currentMessages: ([EnginePeer.Id: EnginePeer], [EngineMessage.Id: EngineMessage]) { @@ -1325,7 +1356,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo title = strongSelf.presentationData.strings.DownloadList_RemoveFileAlertTitle(Int32(messages.count)) text = strongSelf.presentationData.strings.DownloadList_RemoveFileAlertText(Int32(messages.count)) - strongSelf.present?(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: title, text: text, actions: [ + strongSelf.present?(textAlertController(context: strongSelf.context, title: title, text: text, actions: [ TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { }), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.DownloadList_RemoveFileAlertRemove, action: { diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index c3854ee8..88a8cdf0 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -38,6 +38,7 @@ import MultilineTextComponent import ButtonComponent import BundleIconComponent import AnimatedTextComponent +import TextFormat private enum ChatListRecentEntryStableId: Hashable { case topPeers @@ -1696,7 +1697,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { private var emptyRecentAnimationNode: AnimatedStickerNode? private var emptyRecentAnimationSize = CGSize() - private var currentParams: (size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData)? + private var currentParams: (size: CGSize, sideInset: CGFloat, topInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData)? private let ready = Promise() private var didSetReady: Bool = false @@ -3495,7 +3496,6 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { self.interaction.openStories?(id, sourceNode.avatarNode) } }, openStarsTopup: { _ in - }, dismissNotice: { _ in }, editPeer: { _ in }, openWebApp: { _ in }, openPhotoSetup: { @@ -4508,7 +4508,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { return } dismissImpl?() - if let value = attributes[NSAttributedString.Key(rawValue: "URL")] as? String { + if let value = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String { if !value.isEmpty { context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: value, forceExternal: false, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: navigationController, dismissInput: {}) } else { @@ -4536,7 +4536,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { ) interaction.present(alertController, nil) dismissImpl = { [weak alertController] in - alertController?.dismissAnimated() + alertController?.dismiss() } }, isChannelsTabExpanded: recentItems.isChannelsTabExpanded, @@ -4640,8 +4640,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { self.playlistStateAndType = nil } - if let (size, sideInset, bottomInset, visibleHeight, presentationData) = self.currentParams { - self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: true, transition: .animated(duration: 0.4, curve: .spring)) + if let (size, sideInset, topInset, bottomInset, visibleHeight, presentationData) = self.currentParams { + self.update(size: size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: true, transition: .animated(duration: 0.4, curve: .spring)) } } self.playlistLocation = playlistStateAndType?.1.playlistLocation @@ -4758,10 +4758,10 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } } - func update(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) { + func update(size: CGSize, sideInset: CGFloat, topInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) { let hadValidLayout = self.currentParams != nil - let layoutChanged = self.currentParams?.size != size || self.currentParams?.sideInset != sideInset || self.currentParams?.bottomInset != bottomInset || self.currentParams?.visibleHeight != visibleHeight - self.currentParams = (size, sideInset, bottomInset, visibleHeight, presentationData) + let layoutChanged = self.currentParams?.size != size || self.currentParams?.sideInset != sideInset || self.currentParams?.topInset != topInset || self.currentParams?.bottomInset != bottomInset || self.currentParams?.visibleHeight != visibleHeight + self.currentParams = (size, sideInset, topInset, bottomInset, visibleHeight, presentationData) var topPanelHeight: CGFloat = 0.0 if let (item, previousItem, nextItem, order, type, _) = self.playlistStateAndType { @@ -5035,9 +5035,9 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { transition.updateFrame(node: self.mediaAccessoryPanelContainer, frame: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: MediaNavigationAccessoryHeaderNode.minimizedHeight))) - let topInset: CGFloat = topPanelHeight - let overflowInset: CGFloat = 20.0 - let insets = UIEdgeInsets(top: topPanelHeight, left: sideInset, bottom: bottomInset, right: sideInset) + let topInset: CGFloat = topInset + topPanelHeight + let overflowInset: CGFloat = 0.0 + let insets = UIEdgeInsets(top: topInset + topPanelHeight, left: sideInset, bottom: bottomInset, right: sideInset) self.shimmerNode.frame = CGRect(origin: CGPoint(x: overflowInset, y: topInset), size: CGSize(width: size.width - overflowInset * 2.0, height: size.height)) self.shimmerNode.update(context: self.context, size: CGSize(width: size.width - overflowInset * 2.0, height: size.height), presentationData: self.presentationData, animationCache: self.animationCache, animationRenderer: self.animationRenderer, key: !(self.searchQueryValue?.isEmpty ?? true) && self.key == .media ? .chats : self.key, hasSelection: self.selectedMessages != nil, transition: transition) @@ -5480,8 +5480,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { strongSelf.emptyResultsButtonSubtitleText = nil } - if let (size, sideInset, bottomInset, visibleHeight, presentationData) = strongSelf.currentParams { - strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: true, transition: .animated(duration: 0.4, curve: .spring)) + if let (size, sideInset, topInset, bottomInset, visibleHeight, presentationData) = strongSelf.currentParams { + strongSelf.update(size: size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: true, transition: .animated(duration: 0.4, curve: .spring)) } if strongSelf.key == .downloads { @@ -5783,7 +5783,6 @@ public final class ChatListSearchShimmerNode: ASDisplayNode { }, openChatFolderUpdates: {}, hideChatFolderUpdates: { }, openStories: { _, _ in }, openStarsTopup: { _ in - }, dismissNotice: { _ in }, editPeer: { _ in }, openWebApp: { _ in }, openPhotoSetup: { diff --git a/submodules/ChatListUI/Sources/ChatListSearchPaneContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchPaneContainerNode.swift index 7d52bddb..054acc95 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchPaneContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchPaneContainerNode.swift @@ -15,7 +15,7 @@ protocol ChatListSearchPaneNode: ASDisplayNode { var isReady: Signal { get } var isCurrent: Bool { get set } - func update(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) + func update(size: CGSize, sideInset: CGFloat, topInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) func scrollToTop() -> Bool func cancelPreviewGestures() func transitionNodeForGallery(messageId: EngineMessage.Id, media: EngineMedia) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? @@ -32,21 +32,21 @@ final class ChatListSearchPaneWrapper { let key: ChatListSearchPaneKey let node: ChatListSearchPaneNode var isAnimatingOut: Bool = false - private var appliedParams: (CGSize, CGFloat, CGFloat, CGFloat, PresentationData)? + private var appliedParams: (CGSize, CGFloat, CGFloat, CGFloat, CGFloat, PresentationData)? init(key: ChatListSearchPaneKey, node: ChatListSearchPaneNode) { self.key = key self.node = node } - func update(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) { - if let (currentSize, currentSideInset, currentBottomInset, _, currentPresentationData) = self.appliedParams { - if currentSize == size && currentSideInset == sideInset && currentBottomInset == bottomInset && currentPresentationData === presentationData { + func update(size: CGSize, sideInset: CGFloat, topInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) { + if let (currentSize, currentSideInset, currentTopInset, currentBottomInset, _, currentPresentationData) = self.appliedParams { + if currentSize == size && currentSideInset == sideInset && currentTopInset == topInset && currentBottomInset == bottomInset && currentPresentationData === presentationData { return } } - self.appliedParams = (size, sideInset, bottomInset, visibleHeight, presentationData) - self.node.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: synchronous, transition: transition) + self.appliedParams = (size, sideInset, topInset, bottomInset, visibleHeight, presentationData) + self.node.update(size: size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: synchronous, transition: transition) } } @@ -190,7 +190,7 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD var isAdjacentLoadingEnabled = false - private var currentParams: (size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, [ChatListSearchPaneKey])? + private var currentParams: (size: CGSize, sideInset: CGFloat, topInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, [ChatListSearchPaneKey])? private(set) var currentPaneKey: ChatListSearchPaneKey? var pendingSwitchToPaneKey: ChatListSearchPaneKey? @@ -251,8 +251,8 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD if self.currentPanes[key] != nil { self.currentPaneKey = key - if let (size, sideInset, bottomInset, visibleHeight, presentationData, availablePanes) = self.currentParams { - self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, availablePanes: availablePanes, transition: .animated(duration: 0.4, curve: .spring)) + if let (size, sideInset, topInset, bottomInset, visibleHeight, presentationData, availablePanes) = self.currentParams { + self.update(size: size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, availablePanes: availablePanes, transition: .animated(duration: 0.4, curve: .spring)) } if case .apps = key { @@ -261,8 +261,8 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD } else if self.pendingSwitchToPaneKey != key { self.pendingSwitchToPaneKey = key - if let (size, sideInset, bottomInset, visibleHeight, presentationData, availablePanes) = self.currentParams { - self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, availablePanes: availablePanes, transition: .animated(duration: 0.4, curve: .spring)) + if let (size, sideInset, topInset, bottomInset, visibleHeight, presentationData, availablePanes) = self.currentParams { + self.update(size: size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, availablePanes: availablePanes, transition: .animated(duration: 0.4, curve: .spring)) } if case .apps = key { @@ -275,7 +275,7 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD super.didLoad() let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)), allowedDirections: { [weak self] point in - guard let strongSelf = self, let (_, _, _, _, _, availablePanes) = strongSelf.currentParams, let currentPaneKey = strongSelf.currentPaneKey, let index = availablePanes.firstIndex(of: currentPaneKey) else { + guard let strongSelf = self, let (_, _, _, _, _, _, availablePanes) = strongSelf.currentParams, let currentPaneKey = strongSelf.currentPaneKey, let index = availablePanes.firstIndex(of: currentPaneKey) else { return [] } if index == 0 { @@ -321,7 +321,7 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD cancelContextGestures(view: self.view) case .changed: - if let (size, sideInset, bottomInset, visibleHeight, presentationData, availablePanes) = self.currentParams, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) { + if let (size, sideInset, topInset, bottomInset, visibleHeight, presentationData, availablePanes) = self.currentParams, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) { self.isAdjacentLoadingEnabled = true let translation = recognizer.translation(in: self.view) var transitionFraction = translation.x / size.width @@ -332,10 +332,10 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD transitionFraction = max(0.0, transitionFraction) } self.transitionFraction = transitionFraction - self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, availablePanes: availablePanes, transition: .immediate) + self.update(size: size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, availablePanes: availablePanes, transition: .immediate) } case .cancelled, .ended: - if let (size, sideInset, bottomInset, visibleHeight, presentationData, availablePanes) = self.currentParams, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) { + if let (size, sideInset, topInset, bottomInset, visibleHeight, presentationData, availablePanes) = self.currentParams, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) { let translation = recognizer.translation(in: self.view) let velocity = recognizer.velocity(in: self.view) var directionIsToRight: Bool? @@ -364,7 +364,7 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD } } self.transitionFraction = 0.0 - self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, availablePanes: availablePanes, transition: .animated(duration: 0.35, curve: .spring)) + self.update(size: size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, availablePanes: availablePanes, transition: .animated(duration: 0.35, curve: .spring)) } default: break @@ -396,7 +396,7 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD } } - func update(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, availablePanes: [ChatListSearchPaneKey], transition: ContainedViewLayoutTransition) { + func update(size: CGSize, sideInset: CGFloat, topInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, presentationData: PresentationData, availablePanes: [ChatListSearchPaneKey], transition: ContainedViewLayoutTransition) { let previousAvailablePanes = self.currentAvailablePanes ?? [] self.currentAvailablePanes = availablePanes @@ -430,7 +430,7 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD currentIndex = nil } - self.currentParams = (size, sideInset, bottomInset, visibleHeight, presentationData, availablePanes) + self.currentParams = (size, sideInset, topInset, bottomInset, visibleHeight, presentationData, availablePanes) switch self.location { case .forum, .savedMessagesChats: @@ -489,12 +489,12 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD guard let strongSelf = self else { return } - if let (size, sideInset, bottomInset, visibleHeight, presentationData, availablePanes) = strongSelf.currentParams { + if let (size, sideInset, topInset, bottomInset, visibleHeight, presentationData, availablePanes) = strongSelf.currentParams { var transition: ContainedViewLayoutTransition = .immediate if strongSelf.pendingSwitchToPaneKey == key && strongSelf.currentPaneKey != nil { transition = .animated(duration: 0.4, curve: .spring) } - strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, availablePanes: availablePanes, transition: transition) + strongSelf.update(size: size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, availablePanes: availablePanes, transition: transition) } } if leftScope { @@ -504,14 +504,14 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD ) self.pendingPanes[key] = pane pane.pane.node.frame = paneFrame - pane.pane.update(size: paneFrame.size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: true, transition: .immediate) + pane.pane.update(size: paneFrame.size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: true, transition: .immediate) leftScope = true } } for (key, pane) in self.pendingPanes { pane.pane.node.frame = paneFrame - pane.pane.update(size: paneFrame.size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: self.currentPaneKey == nil, transition: .immediate) + pane.pane.update(size: paneFrame.size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: self.currentPaneKey == nil, transition: .immediate) if pane.isReady { self.pendingPanes.removeValue(forKey: key) @@ -587,7 +587,7 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD paneCompletion() }) } - pane.update(size: paneFrame.size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: paneWasAdded, transition: paneTransition) + pane.update(size: paneFrame.size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: paneWasAdded, transition: paneTransition) pane.node.isCurrent = key == self.currentPaneKey if paneWasAdded && key == self.currentPaneKey { pane.node.didBecomeFocused() @@ -598,7 +598,7 @@ final class ChatListSearchPaneContainerNode: ASDisplayNode, ASGestureRecognizerD for (_, pane) in self.pendingPanes { let paneTransition: ContainedViewLayoutTransition = .immediate paneTransition.updateFrame(node: pane.pane.node, frame: paneFrame) - pane.pane.update(size: paneFrame.size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: true, transition: paneTransition) + pane.pane.update(size: paneFrame.size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, visibleHeight: visibleHeight, presentationData: presentationData, synchronous: true, transition: paneTransition) } if !self.didSetIsReady { if let currentPaneKey = self.currentPaneKey, let currentPane = self.currentPanes[currentPaneKey] { diff --git a/submodules/ChatListUI/Sources/ChatListShimmerNode.swift b/submodules/ChatListUI/Sources/ChatListShimmerNode.swift index 2cf134e2..6ca1a33b 100644 --- a/submodules/ChatListUI/Sources/ChatListShimmerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListShimmerNode.swift @@ -157,7 +157,6 @@ public final class ChatListShimmerNode: ASDisplayNode { }, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, _, gesture, _ in gesture?.cancel() }, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: { _, _ in }, openPremiumManagement: {}, openActiveSessions: {}, openBirthdaySetup: {}, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: {}, hideChatFolderUpdates: {}, openStories: { _, _ in }, openStarsTopup: { _ in - }, dismissNotice: { _ in }, editPeer: { _ in }, openWebApp: { _ in }, openPhotoSetup: { diff --git a/submodules/ChatListUI/Sources/ItemListFilterTitleInputItem.swift b/submodules/ChatListUI/Sources/ItemListFilterTitleInputItem.swift index 127a4ae5..2f5e5b51 100644 --- a/submodules/ChatListUI/Sources/ItemListFilterTitleInputItem.swift +++ b/submodules/ChatListUI/Sources/ItemListFilterTitleInputItem.swift @@ -126,7 +126,7 @@ public class ItemListFilterTitleInputItemNode: ListViewItemNode, UITextFieldDele self.maskNode = ASImageNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) } override public func didLoad() { diff --git a/submodules/ChatListUI/Sources/Node/ChatListArchiveInfoItem.swift b/submodules/ChatListUI/Sources/Node/ChatListArchiveInfoItem.swift index 4dda5fe5..411cd295 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListArchiveInfoItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListArchiveInfoItem.swift @@ -172,7 +172,7 @@ class ChatListArchiveInfoItemNode: ListViewItemNode, ASScrollViewDelegate { self.infoPageNodes = (0 ..< 3).map({ _ in InfoPageNode() }) self.pageControlNode.pagesCount = self.infoPageNodes.count - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.scrollNode) self.infoPageNodes.forEach(self.scrollNode.addSubnode) diff --git a/submodules/ChatListUI/Sources/Node/ChatListEmptyHeaderItem.swift b/submodules/ChatListUI/Sources/Node/ChatListEmptyHeaderItem.swift index 9edf523d..a2b5e4ee 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListEmptyHeaderItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListEmptyHeaderItem.swift @@ -55,7 +55,7 @@ class ChatListEmptyHeaderItemNode: ListViewItemNode { private var item: ChatListEmptyHeaderItem? required init() { - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) } override func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) { diff --git a/submodules/ChatListUI/Sources/Node/ChatListEmptyInfoItem.swift b/submodules/ChatListUI/Sources/Node/ChatListEmptyInfoItem.swift index 21363bb4..06615f98 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListEmptyInfoItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListEmptyInfoItem.swift @@ -92,7 +92,7 @@ class ChatListEmptyInfoItemNode: ListViewItemNode { self.animationNode = DefaultAnimatedStickerNodeImpl() self.textNode = TextNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.animationNode) self.addSubnode(self.textNode) @@ -207,7 +207,7 @@ class ChatListSectionHeaderNode: ListViewItemNode { private var headerNode: ListSectionHeaderNode? required init() { - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.zPosition = 1.0 } diff --git a/submodules/ChatListUI/Sources/Node/ChatListHoleItem.swift b/submodules/ChatListUI/Sources/Node/ChatListHoleItem.swift index 1716677a..324f4245 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListHoleItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListHoleItem.swift @@ -56,7 +56,7 @@ class ChatListHoleItemNode: ListViewItemNode { var relativePosition: (first: Bool, last: Bool) = (false, false) required init() { - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) } override func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) { @@ -153,7 +153,7 @@ class ChatListSearchEmptyFooterItemNode: ListViewItemNode { self.searchAllMessagesTitle = TextNode() self.searchAllMessagesTitle.isUserInteractionEnabled = false - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.contentNode) self.contentNode.addSubnode(self.titleNode) diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index 46974fd9..35b8650c 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -15,7 +15,6 @@ import PeerOnlineMarkerNode import LocalizedPeerData import PeerPresenceStatusManager import PhotoResources -import ChatListSearchItemNode import ContextUI import ChatInterfaceState import TextFormat @@ -219,6 +218,7 @@ public enum ChatListItemContent { public var message: EngineMessage? public var unreadCount: Int public var hiddenByDefault: Bool + public var appearsPinned: Bool public var storyState: StoryState? public init( @@ -227,6 +227,7 @@ public enum ChatListItemContent { message: EngineMessage?, unreadCount: Int, hiddenByDefault: Bool, + appearsPinned: Bool, storyState: StoryState? ) { self.groupId = groupId @@ -234,6 +235,7 @@ public enum ChatListItemContent { self.message = message self.unreadCount = unreadCount self.hiddenByDefault = hiddenByDefault + self.appearsPinned = appearsPinned self.storyState = storyState } } @@ -454,7 +456,7 @@ private final class ChatListItemTagListComponent: Component { } } -public class ChatListItem: ListViewItem, ChatListSearchItemNeighbour { +public class ChatListItem: ListViewItem { public enum EnabledContextActions { public struct Actions: OptionSet { public var rawValue: Int32 @@ -1472,7 +1474,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { } else { result += item.presentationData.strings.VoiceOver_ChatList_OutgoingMessage } - let (_, initialHideAuthor, messageText, _, _) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: messages, chatPeer: peer, accountPeerId: item.context.account.peerId, isPeerGroup: false) + let (_, initialHideAuthor, messageText, _, _, _) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: messages, chatPeer: peer, accountPeerId: item.context.account.peerId, isPeerGroup: false) if message.flags.contains(.Incoming), !initialHideAuthor, let author = message.author, case .user = author { result += "\n\(item.presentationData.strings.VoiceOver_ChatList_MessageFrom(author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)).string)" } @@ -1506,7 +1508,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { } else { result += item.presentationData.strings.VoiceOver_ChatList_OutgoingMessage } - let (_, initialHideAuthor, messageText, _, _) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: peerData.messages, chatPeer: peerData.peer, accountPeerId: item.context.account.peerId, isPeerGroup: false) + let (_, initialHideAuthor, messageText, _, _, _) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: peerData.messages, chatPeer: peerData.peer, accountPeerId: item.context.account.peerId, isPeerGroup: false) if message.flags.contains(.Incoming), !initialHideAuthor, let author = message.author, case .user = author { result += "\n\(item.presentationData.strings.VoiceOver_ChatList_MessageFrom(author.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)).string)" } @@ -1656,7 +1658,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { self.separatorNode = ASDisplayNode() self.separatorNode.isLayerBacked = true - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.isAccessibilityElement = true @@ -2452,7 +2454,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { let leftInset: CGFloat = params.leftInset + avatarLeftInset enum ContentData { - case chat(itemPeer: EngineRenderedPeer, threadInfo: ChatListItemContent.ThreadInfo?, peer: EnginePeer?, hideAuthor: Bool, messageText: String, spoilers: [NSRange]?, customEmojiRanges: [(NSRange, ChatTextInputTextCustomEmojiAttribute)]?) + case chat(itemPeer: EngineRenderedPeer, threadInfo: ChatListItemContent.ThreadInfo?, peer: EnginePeer?, hideAuthor: Bool, messageText: String, messageEntities: [MessageTextEntity], spoilers: [NSRange]?, customEmojiRanges: [(NSRange, ChatTextInputTextCustomEmojiAttribute)]?) case group(peers: [EngineChatList.GroupItem.Item]) } @@ -2461,7 +2463,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { var hideAuthor = false switch contentPeer { case let .chat(itemPeer): - var (peer, initialHideAuthor, messageText, spoilers, customEmojiRanges) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: messages, chatPeer: itemPeer, accountPeerId: item.context.account.peerId, enableMediaEmoji: !enableChatListPhotos, isPeerGroup: isPeerGroup) + var (peer, initialHideAuthor, messageText, messageEntities, spoilers, customEmojiRanges) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: messages, chatPeer: itemPeer, accountPeerId: item.context.account.peerId, enableMediaEmoji: !enableChatListPhotos, isPeerGroup: isPeerGroup) if case let .psa(_, maybePsaText) = promoInfo, let psaText = maybePsaText { initialHideAuthor = true @@ -2489,7 +2491,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { break } - contentData = .chat(itemPeer: itemPeer, threadInfo: threadInfo, peer: peer, hideAuthor: hideAuthor, messageText: messageText, spoilers: spoilers, customEmojiRanges: customEmojiRanges) + contentData = .chat(itemPeer: itemPeer, threadInfo: threadInfo, peer: peer, hideAuthor: hideAuthor, messageText: messageText, messageEntities: messageEntities, spoilers: spoilers, customEmojiRanges: customEmojiRanges) hideAuthor = initialHideAuthor case let .group(groupPeers): contentData = .group(peers: groupPeers) @@ -2508,7 +2510,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { forumTopicData = nil topForumTopicItems = [] - if case let .chat(itemPeer, _, _, _, _, _, _) = contentData { + if case let .chat(itemPeer, _, _, _, _, _, _, _) = contentData { if let messagePeer = itemPeer.chatMainPeer { switch messagePeer { case let .channel(channel): @@ -2556,7 +2558,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { var ignoreForwardedIcon = false switch contentData { - case let .chat(itemPeer, _, _, _, text, spoilers, customEmojiRanges): + case let .chat(itemPeer, _, _, _, text, entities, spoilers, customEmojiRanges): var isUser = false if case .user = itemPeer.chatMainPeer { isUser = true @@ -2639,7 +2641,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { } chatListText = (text, messageText) } - + if inlineAuthorPrefix == nil, let mediaDraftContentType { hasDraft = true authorAttributedString = NSAttributedString(string: item.presentationData.strings.DialogList_Draft, font: textFont, textColor: theme.messageDraftTextColor) @@ -2671,8 +2673,8 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { if let peerText = peerText { authorAttributedString = NSAttributedString(string: peerText, font: textFont, textColor: theme.authorNameColor) } - - var entities = (message._asMessage().textEntitiesAttribute?.entities ?? []).filter { entity in + + var entities = entities.filter { entity in switch entity.type { case .Spoiler, .CustomEmoji: return true @@ -2690,14 +2692,14 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { } else { regex = loginCodeRegex } - if let cached = currentCustomTextEntities, cached.matches(text: message.text) { + if let cached = currentCustomTextEntities, cached.matches(text: messageText) { customTextEntities = cached - } else if let matches = regex?.matches(in: message.text, options: [], range: NSMakeRange(0, (message.text as NSString).length)) { + } else if let matches = regex?.matches(in: messageText, options: [], range: NSMakeRange(0, (messageText as NSString).length)) { var entities: [MessageTextEntity] = [] if let first = matches.first { entities.append(MessageTextEntity(range: first.range.location ..< first.range.location + first.range.length, type: .Spoiler)) } - customTextEntities = CachedCustomTextEntities(text: message.text, textEntities: entities) + customTextEntities = CachedCustomTextEntities(text: messageText, textEntities: entities) } } @@ -2706,14 +2708,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { } let messageString: NSAttributedString - if !message.text.isEmpty && entities.count > 0 { - var messageText = message.text - var entities = entities - if !"".isEmpty, let translation = message.attributes.first(where: { $0 is TranslationMessageAttribute }) as? TranslationMessageAttribute, !translation.text.isEmpty { - messageText = translation.text - entities = translation.entities - } - + if !messageText.isEmpty && entities.count > 0 { messageString = foldLineBreaks(stringWithAppliedEntities(messageText, entities: entities, baseColor: theme.messageTextColor, linkColor: theme.messageTextColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: italicTextFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false, message: message._asMessage())) } else if spoilers != nil || customEmojiRanges != nil { let mutableString = NSMutableAttributedString(string: messageText, font: textFont, textColor: theme.messageTextColor) @@ -3100,7 +3095,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { } switch contentData { - case let .chat(itemPeer, threadInfo, _, _, _, _, _): + case let .chat(itemPeer, threadInfo, _, _, _, _, _, _): if case let .peer(peerData) = item.content, let customMessageListData = peerData.customMessageListData { if customMessageListData.commandPrefix != nil { titleAttributedString = nil @@ -3854,6 +3849,9 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { transition = .immediate } + transition.updateAlpha(node: strongSelf, alpha: item.hiddenOffset ? 0.0 : 1.0) + ComponentTransition(transition).setBlur(layer: strongSelf.layer, radius: item.hiddenOffset ? 8.0 : 0.0) + let contextContainerFrame = CGRect(origin: CGPoint(), size: CGSize(width: layout.contentSize.width, height: itemHeight)) // strongSelf.contextContainer.position = contextContainerFrame.center transition.updatePosition(node: strongSelf.contextContainer, position: contextContainerFrame.center) @@ -5031,7 +5029,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { if case let .groupReference(groupReferenceData) = item.content, groupReferenceData.hiddenByDefault { separatorInset = 0.0 } else if (!nextIsPinned && isPinned) || last { - separatorInset = 0.0 + separatorInset = 0.0 } else { separatorInset = editingOffset + leftInset + rawContentRect.origin.x } @@ -5057,8 +5055,8 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode { highlightedBackgroundColor = theme.itemHighlightedBackgroundColor } else if isPinned { if case let .groupReference(groupReferenceData) = item.content, groupReferenceData.hiddenByDefault { - backgroundColor = theme.itemBackgroundColor - highlightedBackgroundColor = theme.itemHighlightedBackgroundColor + backgroundColor = groupReferenceData.appearsPinned ? theme.pinnedItemBackgroundColor : theme.itemBackgroundColor + highlightedBackgroundColor = groupReferenceData.appearsPinned ? theme.pinnedItemHighlightedBackgroundColor : theme.itemHighlightedBackgroundColor } else { backgroundColor = theme.pinnedItemBackgroundColor highlightedBackgroundColor = theme.pinnedItemHighlightedBackgroundColor diff --git a/submodules/ChatListUI/Sources/Node/ChatListItemStrings.swift b/submodules/ChatListUI/Sources/Node/ChatListItemStrings.swift index ba82ca6f..a356607d 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItemStrings.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItemStrings.swift @@ -77,20 +77,21 @@ private func paidContentGroupType(paidContent: TelegramMediaPaidContent) -> Mess return currentType } -public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, contentSettings: ContentSettings, messages: [EngineMessage], chatPeer: EngineRenderedPeer, accountPeerId: EnginePeer.Id, enableMediaEmoji: Bool = true, isPeerGroup: Bool = false) -> (peer: EnginePeer?, hideAuthor: Bool, messageText: String, spoilers: [NSRange]?, customEmojiRanges: [(NSRange, ChatTextInputTextCustomEmojiAttribute)]?) { +public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, contentSettings: ContentSettings, messages: [EngineMessage], chatPeer: EngineRenderedPeer, accountPeerId: EnginePeer.Id, enableMediaEmoji: Bool = true, isPeerGroup: Bool = false) -> (peer: EnginePeer?, hideAuthor: Bool, messageText: String, messageEntities: [MessageTextEntity], spoilers: [NSRange]?, customEmojiRanges: [(NSRange, ChatTextInputTextCustomEmojiAttribute)]?) { let peer: EnginePeer? let message = messages.last if let restrictionReason = message?._asMessage().restrictionReason(platform: "ios", contentSettings: contentSettings) { - return (nil, false, restrictionReason, nil, nil) + return (nil, false, restrictionReason, [], nil, nil) } if let restrictionReason = chatPeer.chatMainPeer?.restrictionText(platform: "ios", contentSettings: contentSettings) { - return (nil, false, restrictionReason, nil, nil) + return (nil, false, restrictionReason, [], nil, nil) } var hideAuthor = false var messageText: String + var messageEntities: [MessageTextEntity] = [] var spoilers: [NSRange]? var customEmojiRanges: [(NSRange, ChatTextInputTextCustomEmojiAttribute)]? if let message = message { @@ -104,6 +105,7 @@ public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder: for message in messages { if !message.text.isEmpty { messageText = message.text + messageEntities = message._asMessage().textEntitiesAttribute?.entities ?? [] break } } @@ -469,5 +471,5 @@ public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder: } } - return (peer, hideAuthor, messageText, spoilers, customEmojiRanges) + return (peer, hideAuthor, messageText, messageEntities, spoilers, customEmojiRanges) } diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index ad864f6b..b7df981a 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -23,6 +23,7 @@ import ChatListHeaderComponent import UndoUI import NewSessionInfoScreen import PresentationDataUtils +import GlobalControlPanelsContext public enum ChatListNodeMode { case chatList(appendContacts: Bool) @@ -110,7 +111,6 @@ public final class ChatListNodeInteraction { let hideChatFolderUpdates: () -> Void let openStories: (ChatListNode.OpenStoriesSubject, ASDisplayNode?) -> Void let openStarsTopup: (Int64?) -> Void - let dismissNotice: (ChatListNotice) -> Void let editPeer: (ChatListItem) -> Void let openWebApp: (TelegramUser) -> Void let openPhotoSetup: () -> Void @@ -171,7 +171,6 @@ public final class ChatListNodeInteraction { hideChatFolderUpdates: @escaping () -> Void, openStories: @escaping (ChatListNode.OpenStoriesSubject, ASDisplayNode?) -> Void, openStarsTopup: @escaping (Int64?) -> Void, - dismissNotice: @escaping (ChatListNotice) -> Void, editPeer: @escaping (ChatListItem) -> Void, openWebApp: @escaping (TelegramUser) -> Void, openPhotoSetup: @escaping () -> Void, @@ -219,7 +218,6 @@ public final class ChatListNodeInteraction { self.hideChatFolderUpdates = hideChatFolderUpdates self.openStories = openStories self.openStarsTopup = openStarsTopup - self.dismissNotice = dismissNotice self.editPeer = editPeer self.openWebApp = openWebApp self.openPhotoSetup = openPhotoSetup @@ -698,6 +696,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL message: groupReferenceEntry.message, unreadCount: groupReferenceEntry.unreadCount, hiddenByDefault: groupReferenceEntry.hiddenByDefault, + appearsPinned: groupReferenceEntry.appearsPinned, storyState: groupReferenceEntry.storyState.flatMap { storyState in return ChatListItemContent.StoryState( stats: storyState.stats, @@ -751,47 +750,6 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSectionHeaderItem(theme: presentationData.theme, strings: presentationData.strings, hide: displayHide ? { hideChatListContacts(context: context) } : nil), directionHint: entry.directionHint) - case let .Notice(presentationData, notice): - return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListNoticeItem(context: context, theme: presentationData.theme, strings: presentationData.strings, notice: notice, action: { [weak nodeInteraction] action in - switch action { - case .activate: - switch notice { - case .clearStorage: - nodeInteraction?.openStorageManagement() - case .setupPassword: - nodeInteraction?.openPasswordSetup() - case .premiumUpgrade, .premiumAnnualDiscount, .premiumRestore: - nodeInteraction?.openPremiumIntro() - case .xmasPremiumGift: - nodeInteraction?.openPremiumGift([], nil) - case .premiumGrace: - nodeInteraction?.openPremiumManagement() - case .setupBirthday: - nodeInteraction?.openBirthdaySetup() - case let .birthdayPremiumGift(peers, birthdays): - nodeInteraction?.openPremiumGift(peers, birthdays) - case .reviewLogin: - break - case let .starsSubscriptionLowBalance(amount, _): - nodeInteraction?.openStarsTopup(amount.value) - case .setupPhoto: - nodeInteraction?.openPhotoSetup() - case .accountFreeze: - nodeInteraction?.openAccountFreezeInfo() - case let .link(_, url, _, _): - nodeInteraction?.openUrl(url) - } - case .hide: - nodeInteraction?.dismissNotice(notice) - case let .buttonChoice(isPositive): - switch notice { - case let .reviewLogin(newSessionReview, _): - nodeInteraction?.performActiveSessionAction(newSessionReview, isPositive) - default: - break - } - } - }), directionHint: entry.directionHint) } } } @@ -1048,6 +1006,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL message: groupReferenceEntry.message, unreadCount: groupReferenceEntry.unreadCount, hiddenByDefault: groupReferenceEntry.hiddenByDefault, + appearsPinned: groupReferenceEntry.appearsPinned, storyState: groupReferenceEntry.storyState.flatMap { storyState in return ChatListItemContent.StoryState( stats: storyState.stats, @@ -1101,47 +1060,6 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSectionHeaderItem(theme: presentationData.theme, strings: presentationData.strings, hide: displayHide ? { hideChatListContacts(context: context) } : nil), directionHint: entry.directionHint) - case let .Notice(presentationData, notice): - return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListNoticeItem(context: context, theme: presentationData.theme, strings: presentationData.strings, notice: notice, action: { [weak nodeInteraction] action in - switch action { - case .activate: - switch notice { - case .clearStorage: - nodeInteraction?.openStorageManagement() - case .setupPassword: - nodeInteraction?.openPasswordSetup() - case .premiumUpgrade, .premiumAnnualDiscount, .premiumRestore: - nodeInteraction?.openPremiumIntro() - case .xmasPremiumGift: - nodeInteraction?.openPremiumGift([], nil) - case .premiumGrace: - nodeInteraction?.openPremiumManagement() - case .setupBirthday: - nodeInteraction?.openBirthdaySetup() - case let .birthdayPremiumGift(peers, birthdays): - nodeInteraction?.openPremiumGift(peers, birthdays) - case .reviewLogin: - break - case let .starsSubscriptionLowBalance(amount, _): - nodeInteraction?.openStarsTopup(amount.value) - case .setupPhoto: - nodeInteraction?.openPhotoSetup() - case .accountFreeze: - nodeInteraction?.openAccountFreezeInfo() - case let .link(_, url, _, _): - nodeInteraction?.openUrl(url) - } - case .hide: - nodeInteraction?.dismissNotice(notice) - case let .buttonChoice(isPositive): - switch notice { - case let .reviewLogin(newSessionReview, _): - nodeInteraction?.performActiveSessionAction(newSessionReview, isPositive) - default: - break - } - } - }), directionHint: entry.directionHint) case .HeaderEntry: return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListEmptyHeaderItem(), directionHint: entry.directionHint) case let .AdditionalCategory(index: _, id, title, image, appearance, selected, presentationData): @@ -1282,7 +1200,7 @@ public final class ChatListNode: ListView { return [] } } - private var interaction: ChatListNodeInteraction? + public private(set) var interaction: ChatListNodeInteraction? private var dequeuedInitialTransitionOnLayout = false private var enqueuedTransition: (ChatListNodeListViewTransition, () -> Void)? @@ -1383,7 +1301,6 @@ public final class ChatListNode: ListView { private let autoSetReady: Bool public let isMainTab = ValuePromise(false, ignoreRepeated: true) - private let suggestedChatListNotice = Promise(nil) public var synchronousDrawingWhenNotAnimated: Bool = false @@ -1868,38 +1785,6 @@ public final class ChatListNode: ListView { return } self.openStarsTopup?(amount) - }, dismissNotice: { [weak self] notice in - guard let self else { - return - } - let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - switch notice { - case .xmasPremiumGift: - let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.xmasPremiumGift.id).startStandalone() - self.present?(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gift", scale: 0.058, colors: ["__allcolors__": UIColor.white], title: nil, text: presentationData.strings.ChatList_PremiumGiftInSettingsInfo, customUndoText: nil, timeout: 5.0), elevatedLayout: false, action: { _ in - return true - })) - case .setupBirthday: - let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupBirthday.id).startStandalone() - self.present?(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gift", scale: 0.058, colors: ["__allcolors__": UIColor.white], title: nil, text: presentationData.strings.ChatList_BirthdayInSettingsInfo, customUndoText: nil, timeout: 5.0), elevatedLayout: false, action: { _ in - return true - })) - case .birthdayPremiumGift: - let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.todayBirthdays.id).startStandalone() - self.present?(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gift", scale: 0.058, colors: ["__allcolors__": UIColor.white], title: nil, text: presentationData.strings.ChatList_PremiumGiftInSettingsInfo, customUndoText: nil, timeout: 5.0), elevatedLayout: false, action: { _ in - return true - })) - case .premiumGrace: - let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.gracePremium.id).startStandalone() - case .setupPhoto: - let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupPhoto.id).startStandalone() - case .starsSubscriptionLowBalance: - let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.starsSubscriptionLowBalance.id).startStandalone() - case let .link(id, _, _, _): - let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: id).startStandalone() - default: - break - } }, editPeer: { _ in }, openWebApp: { [weak self] user in guard let self else { @@ -1992,172 +1877,12 @@ public final class ChatListNode: ListView { } else { displayArchiveIntro = .single(false) } - - let starsSubscriptionsContextPromise = Promise(nil) self.updateIsMainTabDisposable = (self.isMainTab.get() - |> deliverOnMainQueue).startStrict(next: { [weak self] isMainTab in - guard let self else { - return + |> deliverOnMainQueue).startStrict(next: { isMainTab in + if isMainTab { + let _ = context.engine.privacy.cleanupSessionReviews().startStandalone() } - - guard case .chatList(groupId: .root) = location, isMainTab else { - self.suggestedChatListNotice.set(.single(nil)) - return - } - - let _ = context.engine.privacy.cleanupSessionReviews().startStandalone() - - let twoStepData: Signal = .single(nil) |> then(context.engine.auth.twoStepVerificationConfiguration() |> map(Optional.init)) - - let accountFreezeConfiguration = (context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration]) - |> map { view -> AppConfiguration in - let appConfiguration: AppConfiguration = view.values[PreferencesKeys.appConfiguration]?.get(AppConfiguration.self) ?? AppConfiguration.defaultValue - return appConfiguration - } - |> distinctUntilChanged - |> map { appConfiguration -> AccountFreezeConfiguration in - return AccountFreezeConfiguration.with(appConfiguration: appConfiguration) - }) - - let suggestedChatListNoticeSignal: Signal = combineLatest( - context.engine.notices.getServerProvidedSuggestions(), - context.engine.notices.getServerDismissedSuggestions(), - twoStepData, - newSessionReviews(postbox: context.account.postbox), - context.engine.data.subscribe( - TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId), - TelegramEngine.EngineData.Item.Peer.Birthday(id: context.account.peerId) - ), - context.account.stateManager.contactBirthdays, - starsSubscriptionsContextPromise.get(), - accountFreezeConfiguration - ) - |> mapToSignal { suggestions, dismissedSuggestions, configuration, newSessionReviews, data, birthdays, starsSubscriptionsContext, accountFreezeConfiguration -> Signal in - let (accountPeer, birthday) = data - - if let newSessionReview = newSessionReviews.first { - return .single(.reviewLogin(newSessionReview: newSessionReview, totalCount: newSessionReviews.count)) - } - if suggestions.contains(.setupPassword), let configuration { - var notSet = false - switch configuration { - case let .notSet(pendingEmail): - if pendingEmail == nil { - notSet = true - } - case .set: - break - } - if notSet { - return .single(.setupPassword) - } - } - - let today = Calendar(identifier: .gregorian).component(.day, from: Date()) - var todayBirthdayPeerIds: [EnginePeer.Id] = [] - for (peerId, birthday) in birthdays { - if birthday.day == today { - todayBirthdayPeerIds.append(peerId) - } - } - todayBirthdayPeerIds.sort { lhs, rhs in - return lhs < rhs - } - - if dismissedSuggestions.contains(ServerProvidedSuggestion.todayBirthdays.id) { - todayBirthdayPeerIds = [] - } - - if let _ = accountFreezeConfiguration.freezeUntilDate { - return .single(.accountFreeze) - } else if suggestions.contains(.starsSubscriptionLowBalance) { - if let starsSubscriptionsContext { - return starsSubscriptionsContext.state - |> map { state in - if state.balance > StarsAmount.zero && !state.subscriptions.isEmpty { - return .starsSubscriptionLowBalance( - amount: state.balance, - peers: state.subscriptions.map { $0.peer } - ) - } else { - return nil - } - } - } else { - starsSubscriptionsContextPromise.set(.single(context.engine.payments.peerStarsSubscriptionsContext(starsContext: nil, missingBalance: true))) - return .single(nil) - } - } else if suggestions.contains(.setupPhoto), let accountPeer, accountPeer.smallProfileImage == nil { - return .single(.setupPhoto(accountPeer)) - } else if suggestions.contains(.gracePremium) { - return .single(.premiumGrace) - } else if suggestions.contains(.xmasPremiumGift) { - return .single(.xmasPremiumGift) - } else if suggestions.contains(.annualPremium) || suggestions.contains(.upgradePremium) || suggestions.contains(.restorePremium), let inAppPurchaseManager = context.inAppPurchaseManager { - return inAppPurchaseManager.availableProducts - |> map { products -> ChatListNotice? in - if products.count > 1 { - let shortestOptionPrice: (Int64, NSDecimalNumber) - if let product = products.first(where: { $0.id.hasSuffix(".monthly") }) { - shortestOptionPrice = (Int64(Float(product.priceCurrencyAndAmount.amount)), product.priceValue) - } else { - shortestOptionPrice = (1, NSDecimalNumber(decimal: 1)) - } - for product in products { - if product.id.hasSuffix(".annual") { - let fraction = Float(product.priceCurrencyAndAmount.amount) / Float(12) / Float(shortestOptionPrice.0) - let discount = Int32(round((1.0 - fraction) * 20.0) * 5.0) - if discount > 0 { - if suggestions.contains(.restorePremium) { - return .premiumRestore(discount: discount) - } else if suggestions.contains(.annualPremium) { - return .premiumAnnualDiscount(discount: discount) - } else if suggestions.contains(.upgradePremium) { - return .premiumUpgrade(discount: discount) - } - } - break - } - } - return nil - } else { - if !GlobalExperimentalSettings.isAppStoreBuild { - if suggestions.contains(.restorePremium) { - return .premiumRestore(discount: 0) - } else if suggestions.contains(.annualPremium) { - return .premiumAnnualDiscount(discount: 0) - } else if suggestions.contains(.upgradePremium) { - return .premiumUpgrade(discount: 0) - } - } - return nil - } - } - } else if !todayBirthdayPeerIds.isEmpty { - return context.engine.data.get( - EngineDataMap(todayBirthdayPeerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:))) - ) - |> map { result -> ChatListNotice? in - var todayBirthdayPeers: [EnginePeer] = [] - for (peerId, _) in birthdays { - if let maybePeer = result[peerId], let peer = maybePeer { - todayBirthdayPeers.append(peer) - } - } - return .birthdayPremiumGift(peers: todayBirthdayPeers, birthdays: birthdays) - } - } else if suggestions.contains(.setupBirthday) && birthday == nil { - return .single(.setupBirthday) - } else if case let .link(id, url, title, subtitle) = suggestions.first(where: { if case .link = $0 { return true } else { return false} }) { - return .single(.link(id: id, url: url, title: title, subtitle: subtitle)) - } else { - return .single(nil) - } - } - |> distinctUntilChanged - - self.suggestedChatListNotice.set(suggestedChatListNoticeSignal) }).strict() let storageInfo: Signal @@ -2346,7 +2071,6 @@ public final class ChatListNode: ListView { hideArchivedFolderByDefault, displayArchiveIntro, storageInfo, - suggestedChatListNotice.get(), savedMessagesPeer, chatListViewUpdate, self.statePromise.get(), @@ -2354,23 +2078,14 @@ public final class ChatListNode: ListView { chatListFilters, accountIsPremium ) - |> mapToQueue { (hideArchivedFolderByDefault, displayArchiveIntro, storageInfo, suggestedChatListNotice, savedMessagesPeer, updateAndFilter, state, contacts, chatListFilters, accountIsPremium) -> Signal in + |> mapToQueue { (hideArchivedFolderByDefault, displayArchiveIntro, storageInfo, savedMessagesPeer, updateAndFilter, state, contacts, chatListFilters, accountIsPremium) -> Signal in let (update, filter) = updateAndFilter let previousHideArchivedFolderByDefaultValue = previousHideArchivedFolderByDefault.swap(hideArchivedFolderByDefault) - let notice: ChatListNotice? - if let suggestedChatListNotice { - notice = suggestedChatListNotice - } else if let storageInfo { - notice = .clearStorage(sizeFraction: storageInfo) - } else { - notice = nil - } - let innerIsMainTab = location == .chatList(groupId: .root) && chatListFilter == nil - let (rawEntries, isLoading) = chatListNodeEntriesForView(view: update.list, state: state, savedMessagesPeer: savedMessagesPeer, foundPeers: state.foundPeers, hideArchivedFolderByDefault: hideArchivedFolderByDefault, displayArchiveIntro: displayArchiveIntro, notice: notice, mode: mode, chatListLocation: location, contacts: contacts, accountPeerId: accountPeerId, isMainTab: innerIsMainTab) + let (rawEntries, isLoading) = chatListNodeEntriesForView(view: update.list, state: state, savedMessagesPeer: savedMessagesPeer, foundPeers: state.foundPeers, hideArchivedFolderByDefault: hideArchivedFolderByDefault, displayArchiveIntro: displayArchiveIntro, mode: mode, chatListLocation: location, contacts: contacts, accountPeerId: accountPeerId, isMainTab: innerIsMainTab) var isEmpty = true var entries = rawEntries.filter { entry in switch entry { @@ -2697,7 +2412,6 @@ public final class ChatListNode: ListView { var didIncludeRemovingPeerId = false var didIncludeHiddenByDefaultArchive = false var didIncludeHiddenThread = false - var didIncludeNotice = false if let previous = previousView { for entry in previous.filteredEntries { if case let .PeerEntry(peerEntry) = entry { @@ -2724,15 +2438,12 @@ public final class ChatListNode: ListView { } } else if case let .GroupReferenceEntry(groupReferenceEntry) = entry { didIncludeHiddenByDefaultArchive = groupReferenceEntry.hiddenByDefault - } else if case .Notice = entry { - didIncludeNotice = true } } } var doesIncludeRemovingPeerId = false var doesIncludeArchive = false var doesIncludeHiddenByDefaultArchive = false - var doesIncludeNotice = false var doesIncludeHiddenThread = false for entry in processedView.filteredEntries { @@ -2761,8 +2472,6 @@ public final class ChatListNode: ListView { } else if case let .GroupReferenceEntry(groupReferenceEntry) = entry { doesIncludeArchive = true doesIncludeHiddenByDefaultArchive = groupReferenceEntry.hiddenByDefault - } else if case .Notice = entry { - doesIncludeNotice = true } } if previousPinnedChats != updatedPinnedChats || previousPinnedThreads != updatedPinnedThreads { @@ -2789,9 +2498,6 @@ public final class ChatListNode: ListView { if didIncludeHiddenThread != doesIncludeHiddenThread { disableAnimations = false } - if didIncludeNotice != doesIncludeNotice { - disableAnimations = false - } } if let _ = previousHideArchivedFolderByDefaultValue, previousHideArchivedFolderByDefaultValue != hideArchivedFolderByDefault { @@ -3547,8 +3253,10 @@ public final class ChatListNode: ListView { if entryCount - 1 - i < 0 { continue } - if case .PeerEntry = transition.chatListView.filteredEntries[entryCount - 1 - i] { - } else { + switch transition.chatListView.filteredEntries[entryCount - 1 - i] { + case .PeerEntry, .GroupReferenceEntry: + break + default: continue } if case let .index(index) = transition.chatListView.filteredEntries[entryCount - 1 - i].sortIndex, case let .chatList(chatListIndex) = index, chatListIndex.pinningIndex != nil { @@ -3658,7 +3366,7 @@ public final class ChatListNode: ListView { } else { break loop } - case .ArchiveIntro, .EmptyIntro, .SectionHeader, .Notice, .HeaderEntry, .AdditionalCategory: + case .ArchiveIntro, .EmptyIntro, .SectionHeader, .HeaderEntry, .AdditionalCategory: break } } diff --git a/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift b/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift index dfc33618..cd42c2a4 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift @@ -79,23 +79,6 @@ public enum ChatListNodeEntryPromoInfo: Equatable { case psa(type: String, message: String?) } -public enum ChatListNotice: Equatable { - case clearStorage(sizeFraction: Double) - case setupPassword - case premiumUpgrade(discount: Int32) - case premiumAnnualDiscount(discount: Int32) - case premiumRestore(discount: Int32) - case xmasPremiumGift - case setupBirthday - case birthdayPremiumGift(peers: [EnginePeer], birthdays: [EnginePeer.Id: TelegramBirthday]) - case reviewLogin(newSessionReview: NewSessionReview, totalCount: Int) - case premiumGrace - case starsSubscriptionLowBalance(amount: StarsAmount, peers: [EnginePeer]) - case setupPhoto(EnginePeer) - case accountFreeze - case link(id: String, url: String, title: ServerSuggestionInfo.Item.Text, subtitle: ServerSuggestionInfo.Item.Text) -} - enum ChatListNodeEntry: Comparable, Identifiable { struct PeerEntryData: Equatable { var index: EngineChatList.Item.Index @@ -339,6 +322,7 @@ enum ChatListNodeEntry: Comparable, Identifiable { var unreadCount: Int var revealed: Bool var hiddenByDefault: Bool + var appearsPinned: Bool var storyState: ChatListNodeState.StoryState? init( @@ -351,6 +335,7 @@ enum ChatListNodeEntry: Comparable, Identifiable { unreadCount: Int, revealed: Bool, hiddenByDefault: Bool, + appearsPinned: Bool, storyState: ChatListNodeState.StoryState? ) { self.index = index @@ -362,6 +347,7 @@ enum ChatListNodeEntry: Comparable, Identifiable { self.unreadCount = unreadCount self.revealed = revealed self.hiddenByDefault = hiddenByDefault + self.appearsPinned = appearsPinned self.storyState = storyState } @@ -393,6 +379,9 @@ enum ChatListNodeEntry: Comparable, Identifiable { if lhs.hiddenByDefault != rhs.hiddenByDefault { return false } + if lhs.appearsPinned != rhs.appearsPinned { + return false + } if lhs.storyState != rhs.storyState { return false } @@ -409,7 +398,6 @@ enum ChatListNodeEntry: Comparable, Identifiable { case ArchiveIntro(presentationData: ChatListPresentationData) case EmptyIntro(presentationData: ChatListPresentationData) case SectionHeader(presentationData: ChatListPresentationData, displayHide: Bool) - case Notice(presentationData: ChatListPresentationData, notice: ChatListNotice) case AdditionalCategory(index: Int, id: Int, title: String, image: UIImage?, appearance: ChatListNodeAdditionalCategory.Appearance, selected: Bool, presentationData: ChatListPresentationData) var sortIndex: ChatListNodeEntrySortIndex { @@ -430,8 +418,6 @@ enum ChatListNodeEntry: Comparable, Identifiable { return .index(.chatList(EngineChatList.Item.Index.ChatList.absoluteUpperBound.successor)) case .SectionHeader: return .sectionHeader - case .Notice: - return .index(.chatList(EngineChatList.Item.Index.ChatList.absoluteUpperBound.successor.successor)) case let .AdditionalCategory(index, _, _, _, _, _, _): return .additionalCategory(index) } @@ -460,8 +446,6 @@ enum ChatListNodeEntry: Comparable, Identifiable { return .EmptyIntro case .SectionHeader: return .SectionHeader - case .Notice: - return .Notice case let .AdditionalCategory(_, id, _, _, _, _, _): return .additionalCategory(id) } @@ -534,18 +518,6 @@ enum ChatListNodeEntry: Comparable, Identifiable { } else { return false } - case let .Notice(lhsPresentationData, lhsInfo): - if case let .Notice(rhsPresentationData, rhsInfo) = rhs { - if lhsPresentationData !== rhsPresentationData { - return false - } - if lhsInfo != rhsInfo { - return false - } - return true - } else { - return false - } case let .AdditionalCategory(lhsIndex, lhsId, lhsTitle, lhsImage, lhsAppearance, lhsSelected, lhsPresentationData): if case let .AdditionalCategory(rhsIndex, rhsId, rhsTitle, rhsImage, rhsAppearance, rhsSelected, rhsPresentationData) = rhs { if lhsIndex != rhsIndex { @@ -595,7 +567,7 @@ struct ChatListContactPeer { } } -func chatListNodeEntriesForView(view: EngineChatList, state: ChatListNodeState, savedMessagesPeer: EnginePeer?, foundPeers: [(EnginePeer, EnginePeer?)], hideArchivedFolderByDefault: Bool, displayArchiveIntro: Bool, notice: ChatListNotice?, mode: ChatListNodeMode, chatListLocation: ChatListControllerLocation, contacts: [ChatListContactPeer], accountPeerId: EnginePeer.Id, isMainTab: Bool) -> (entries: [ChatListNodeEntry], loading: Bool) { +func chatListNodeEntriesForView(view: EngineChatList, state: ChatListNodeState, savedMessagesPeer: EnginePeer?, foundPeers: [(EnginePeer, EnginePeer?)], hideArchivedFolderByDefault: Bool, displayArchiveIntro: Bool, mode: ChatListNodeMode, chatListLocation: ChatListControllerLocation, contacts: [ChatListContactPeer], accountPeerId: EnginePeer.Id, isMainTab: Bool) -> (entries: [ChatListNodeEntry], loading: Bool) { var groupItems = view.groupItems if isMainTab && state.archiveStoryState != nil && groupItems.isEmpty { groupItems.append(EngineChatList.GroupItem( @@ -656,6 +628,8 @@ func chatListNodeEntriesForView(view: EngineChatList, state: ChatListNodeState, var hiddenGeneralThread: ChatListNodeEntry? + var hasPinned = false + loop: for entry in view.items { var peerId: EnginePeer.Id? var threadId: Int64? @@ -707,6 +681,17 @@ func chatListNodeEntriesForView(view: EngineChatList, state: ChatListNodeState, if let threadData = entry.threadData, let threadId { threadInfo = ChatListItemContent.ThreadInfo(id: threadId, info: threadData.info, isOwnedByMe: threadData.isOwnedByMe, isClosed: threadData.isClosed, isHidden: threadData.isHidden, threadPeer: nil) } + + switch entry.index { + case let .chatList(chatList): + if chatList.pinningIndex != nil { + hasPinned = true + } + case let .forum(pinnedIndex, _, _, _, _): + if case .index = pinnedIndex { + hasPinned = true + } + } let entry: ChatListNodeEntry = .PeerEntry(ChatListNodeEntry.PeerEntryData( index: offsetPinnedIndex(entry.index, offset: pinnedIndexOffset), @@ -796,6 +781,7 @@ func chatListNodeEntriesForView(view: EngineChatList, state: ChatListNodeState, ))) if foundPinningIndex != 0 { foundPinningIndex -= 1 + hasPinned = true } } } @@ -886,6 +872,7 @@ func chatListNodeEntriesForView(view: EngineChatList, state: ChatListNodeState, ))) if pinningIndex != 0 { pinningIndex -= 1 + hasPinned = true } } } @@ -908,10 +895,12 @@ func chatListNodeEntriesForView(view: EngineChatList, state: ChatListNodeState, unreadCount: groupReference.unreadCount, revealed: state.hiddenItemShouldBeTemporaryRevealed, hiddenByDefault: hideArchivedFolderByDefault, + appearsPinned: hasPinned, storyState: mappedStoryState ))) if pinningIndex != 0 { pinningIndex -= 1 + hasPinned = true } } @@ -927,10 +916,6 @@ func chatListNodeEntriesForView(view: EngineChatList, state: ChatListNodeState, result.append(.EmptyIntro(presentationData: state.presentationData)) } - if let notice { - result.append(.Notice(presentationData: state.presentationData, notice: notice)) - } - result.append(.HeaderEntry) } diff --git a/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift b/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift index 6b677831..b6134692 100644 --- a/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift +++ b/submodules/ChatPresentationInterfaceState/Sources/ChatPresentationInterfaceState.swift @@ -339,14 +339,6 @@ public struct ChatActiveGroupCallInfo: Equatable { } } -public struct ChatPresentationImportState: Equatable { - public var progress: Float - - public init(progress: Float) { - self.progress = progress - } -} - public struct ChatPresentationTranslationState: Equatable { public var isEnabled: Bool public var fromLang: String @@ -526,7 +518,6 @@ public final class ChatPresentationInterfaceState: Equatable { public let pendingUnpinnedAllMessages: Bool public let activeGroupCallInfo: ChatActiveGroupCallInfo? public let hasActiveGroupCall: Bool - public let importState: ChatPresentationImportState? public let reportReason: ReportReasonData? public let showCommands: Bool public let hasBotCommands: Bool @@ -568,7 +559,7 @@ public final class ChatPresentationInterfaceState: Equatable { public let persistentData: PersistentPeerData public let removePaidMessageFeeData: RemovePaidMessageFeeData? - public init(chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, limitsConfiguration: LimitsConfiguration, fontSize: PresentationFontSize, bubbleCorners: PresentationChatBubbleCorners, accountPeerId: PeerId, mode: ChatControllerPresentationMode, chatLocation: ChatLocation, subject: ChatControllerSubject?, peerNearbyData: ChatPeerNearbyData?, greetingData: ChatGreetingData?, pendingUnpinnedAllMessages: Bool, activeGroupCallInfo: ChatActiveGroupCallInfo?, hasActiveGroupCall: Bool, importState: ChatPresentationImportState?, threadData: ThreadData?, isGeneralThreadClosed: Bool?, replyMessage: Message?, accountPeerColor: AccountPeerColor?, businessIntro: TelegramBusinessIntro?) { + public init(chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, limitsConfiguration: LimitsConfiguration, fontSize: PresentationFontSize, bubbleCorners: PresentationChatBubbleCorners, accountPeerId: PeerId, mode: ChatControllerPresentationMode, chatLocation: ChatLocation, subject: ChatControllerSubject?, peerNearbyData: ChatPeerNearbyData?, greetingData: ChatGreetingData?, pendingUnpinnedAllMessages: Bool, activeGroupCallInfo: ChatActiveGroupCallInfo?, hasActiveGroupCall: Bool, threadData: ThreadData?, isGeneralThreadClosed: Bool?, replyMessage: Message?, accountPeerColor: AccountPeerColor?, businessIntro: TelegramBusinessIntro?) { self.interfaceState = ChatInterfaceState() self.inputTextPanelState = ChatTextInputPanelState() self.editMessageState = nil @@ -619,7 +610,6 @@ public final class ChatPresentationInterfaceState: Equatable { self.pendingUnpinnedAllMessages = pendingUnpinnedAllMessages self.activeGroupCallInfo = activeGroupCallInfo self.hasActiveGroupCall = hasActiveGroupCall - self.importState = importState self.reportReason = nil self.showCommands = false self.hasBotCommands = false @@ -664,7 +654,7 @@ public final class ChatPresentationInterfaceState: Equatable { self.removePaidMessageFeeData = nil } - public init(interfaceState: ChatInterfaceState, chatLocation: ChatLocation, renderedPeer: RenderedPeer?, isNotAccessible: Bool, explicitelyCanPinMessages: Bool, contactStatus: ChatContactStatus?, hasBots: Bool, isArchived: Bool, inputTextPanelState: ChatTextInputPanelState, editMessageState: ChatEditInterfaceMessageState?, inputQueryResults: [ChatPresentationInputQueryKind: ChatPresentationInputQueryResult], inputMode: ChatInputMode, titlePanelContexts: [ChatTitlePanelContext], keyboardButtonsMessage: Message?, pinnedMessageId: MessageId?, pinnedMessage: ChatPinnedMessage?, peerIsBlocked: Bool, peerIsMuted: Bool, peerDiscussionId: PeerId?, peerGeoLocation: PeerGeoLocation?, callsAvailable: Bool, callsPrivate: Bool, slowmodeState: ChatSlowmodeState?, chatHistoryState: ChatHistoryNodeHistoryState?, botStartPayload: String?, urlPreview: UrlPreview?, editingUrlPreview: UrlPreview?, search: ChatSearchData?, searchQuerySuggestionResult: ChatPresentationInputQueryResult?, historyFilter: HistoryFilter?, displayHistoryFilterAsList: Bool, presentationReady: Bool, chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, limitsConfiguration: LimitsConfiguration, fontSize: PresentationFontSize, bubbleCorners: PresentationChatBubbleCorners, accountPeerId: PeerId, mode: ChatControllerPresentationMode, hasScheduledMessages: Bool, autoremoveTimeout: Int32?, subject: ChatControllerSubject?, peerNearbyData: ChatPeerNearbyData?, greetingData: ChatGreetingData?, pendingUnpinnedAllMessages: Bool, activeGroupCallInfo: ChatActiveGroupCallInfo?, hasActiveGroupCall: Bool, importState: ChatPresentationImportState?, reportReason: ReportReasonData?, showCommands: Bool, hasBotCommands: Bool, showSendAsPeers: Bool, sendAsPeers: [SendAsPeer]?, botMenuButton: BotMenuButton, showWebView: Bool, currentSendAsPeerId: PeerId?, copyProtectionEnabled: Bool, hasAtLeast3Messages: Bool, hasPlentyOfMessages: Bool, isPremium: Bool, premiumGiftOptions: [CachedPremiumGiftOption], suggestPremiumGift: Bool, forceInputCommandsHidden: Bool, voiceMessagesAvailable: Bool, customEmojiAvailable: Bool, threadData: ThreadData?, forumTopicData: ThreadData?, isGeneralThreadClosed: Bool?, translationState: ChatPresentationTranslationState?, replyMessage: Message?, accountPeerColor: AccountPeerColor?, savedMessagesTopicPeer: EnginePeer?, hasSearchTags: Bool, isPremiumRequiredForMessaging: Bool, sendPaidMessageStars: StarsAmount?, acknowledgedPaidMessage: Bool, hasSavedChats: Bool, appliedBoosts: Int32?, boostsToUnrestrict: Int32?, businessIntro: TelegramBusinessIntro?, hasBirthdayToday: Bool, adMessage: Message?, peerVerification: PeerVerification?, starGiftsAvailable: Bool, alwaysShowGiftButton: Bool, disallowedGifts: TelegramDisallowedGifts?, persistentData: PersistentPeerData, removePaidMessageFeeData: RemovePaidMessageFeeData?) { + public init(interfaceState: ChatInterfaceState, chatLocation: ChatLocation, renderedPeer: RenderedPeer?, isNotAccessible: Bool, explicitelyCanPinMessages: Bool, contactStatus: ChatContactStatus?, hasBots: Bool, isArchived: Bool, inputTextPanelState: ChatTextInputPanelState, editMessageState: ChatEditInterfaceMessageState?, inputQueryResults: [ChatPresentationInputQueryKind: ChatPresentationInputQueryResult], inputMode: ChatInputMode, titlePanelContexts: [ChatTitlePanelContext], keyboardButtonsMessage: Message?, pinnedMessageId: MessageId?, pinnedMessage: ChatPinnedMessage?, peerIsBlocked: Bool, peerIsMuted: Bool, peerDiscussionId: PeerId?, peerGeoLocation: PeerGeoLocation?, callsAvailable: Bool, callsPrivate: Bool, slowmodeState: ChatSlowmodeState?, chatHistoryState: ChatHistoryNodeHistoryState?, botStartPayload: String?, urlPreview: UrlPreview?, editingUrlPreview: UrlPreview?, search: ChatSearchData?, searchQuerySuggestionResult: ChatPresentationInputQueryResult?, historyFilter: HistoryFilter?, displayHistoryFilterAsList: Bool, presentationReady: Bool, chatWallpaper: TelegramWallpaper, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, limitsConfiguration: LimitsConfiguration, fontSize: PresentationFontSize, bubbleCorners: PresentationChatBubbleCorners, accountPeerId: PeerId, mode: ChatControllerPresentationMode, hasScheduledMessages: Bool, autoremoveTimeout: Int32?, subject: ChatControllerSubject?, peerNearbyData: ChatPeerNearbyData?, greetingData: ChatGreetingData?, pendingUnpinnedAllMessages: Bool, activeGroupCallInfo: ChatActiveGroupCallInfo?, hasActiveGroupCall: Bool, reportReason: ReportReasonData?, showCommands: Bool, hasBotCommands: Bool, showSendAsPeers: Bool, sendAsPeers: [SendAsPeer]?, botMenuButton: BotMenuButton, showWebView: Bool, currentSendAsPeerId: PeerId?, copyProtectionEnabled: Bool, hasAtLeast3Messages: Bool, hasPlentyOfMessages: Bool, isPremium: Bool, premiumGiftOptions: [CachedPremiumGiftOption], suggestPremiumGift: Bool, forceInputCommandsHidden: Bool, voiceMessagesAvailable: Bool, customEmojiAvailable: Bool, threadData: ThreadData?, forumTopicData: ThreadData?, isGeneralThreadClosed: Bool?, translationState: ChatPresentationTranslationState?, replyMessage: Message?, accountPeerColor: AccountPeerColor?, savedMessagesTopicPeer: EnginePeer?, hasSearchTags: Bool, isPremiumRequiredForMessaging: Bool, sendPaidMessageStars: StarsAmount?, acknowledgedPaidMessage: Bool, hasSavedChats: Bool, appliedBoosts: Int32?, boostsToUnrestrict: Int32?, businessIntro: TelegramBusinessIntro?, hasBirthdayToday: Bool, adMessage: Message?, peerVerification: PeerVerification?, starGiftsAvailable: Bool, alwaysShowGiftButton: Bool, disallowedGifts: TelegramDisallowedGifts?, persistentData: PersistentPeerData, removePaidMessageFeeData: RemovePaidMessageFeeData?) { self.interfaceState = interfaceState self.chatLocation = chatLocation self.renderedPeer = renderedPeer @@ -715,7 +705,6 @@ public final class ChatPresentationInterfaceState: Equatable { self.pendingUnpinnedAllMessages = pendingUnpinnedAllMessages self.activeGroupCallInfo = activeGroupCallInfo self.hasActiveGroupCall = hasActiveGroupCall - self.importState = importState self.reportReason = reportReason self.showCommands = showCommands self.hasBotCommands = hasBotCommands @@ -918,9 +907,6 @@ public final class ChatPresentationInterfaceState: Equatable { if lhs.hasActiveGroupCall != rhs.hasActiveGroupCall { return false } - if lhs.importState != rhs.importState { - return false - } if lhs.reportReason != rhs.reportReason { return false } @@ -1045,35 +1031,35 @@ public final class ChatPresentationInterfaceState: Equatable { } public func updatedInterfaceState(_ f: (ChatInterfaceState) -> ChatInterfaceState) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: f(self.interfaceState), chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: f(self.interfaceState), chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedChatLocation(_ chatLocation: ChatLocation) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedPeer(_ f: (RenderedPeer?) -> RenderedPeer?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: f(self.renderedPeer), isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: f(self.renderedPeer), isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedIsNotAccessible(_ isNotAccessible: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedExplicitelyCanPinMessages(_ explicitelyCanPinMessages: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedContactStatus(_ contactStatus: ChatContactStatus?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedHasBots(_ hasBots: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedIsArchived(_ isArchived: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedInputQueryResult(queryKind: ChatPresentationInputQueryKind, _ f: (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?) -> ChatPresentationInterfaceState { @@ -1090,311 +1076,307 @@ public final class ChatPresentationInterfaceState: Equatable { inputQueryResults.removeValue(forKey: queryKind) } - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedInputTextPanelState(_ f: (ChatTextInputPanelState) -> ChatTextInputPanelState) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: f(self.inputTextPanelState), editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: f(self.inputTextPanelState), editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedEditMessageState(_ editMessageState: ChatEditInterfaceMessageState?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedInputMode(_ f: (ChatInputMode) -> ChatInputMode) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: f(self.inputMode), titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: f(self.inputMode), titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedTitlePanelContext(_ f: ([ChatTitlePanelContext]) -> [ChatTitlePanelContext]) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: f(self.titlePanelContexts), keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: f(self.titlePanelContexts), keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedKeyboardButtonsMessage(_ message: Message?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: message, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: message, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedPinnedMessage(_ pinnedMessage: ChatPinnedMessage?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedPeerIsBlocked(_ peerIsBlocked: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedPeerIsMuted(_ peerIsMuted: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedPeerDiscussionId(_ peerDiscussionId: PeerId?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedPeerGeoLocation(_ peerGeoLocation: PeerGeoLocation?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedCallsAvailable(_ callsAvailable: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedCallsPrivate(_ callsPrivate: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedSlowmodeState(_ slowmodeState: ChatSlowmodeState?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedBotStartPayload(_ botStartPayload: String?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedChatHistoryState(_ chatHistoryState: ChatHistoryNodeHistoryState?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedUrlPreview(_ urlPreview: UrlPreview?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedEditingUrlPreview(_ editingUrlPreview: UrlPreview?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedSearch(_ search: ChatSearchData?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedSearchQuerySuggestionResult(_ f: (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: f(self.searchQuerySuggestionResult), historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: f(self.searchQuerySuggestionResult), historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedHistoryFilter(_ historyFilter: HistoryFilter?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedDisplayHistoryFilterAsList(_ displayHistoryFilterAsList: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedMode(_ mode: ChatControllerPresentationMode) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedPresentationReady(_ presentationReady: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedTheme(_ theme: PresentationTheme) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedStrings(_ strings: PresentationStrings) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedDateTimeFormat(_ dateTimeFormat: PresentationDateTimeFormat) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedChatWallpaper(_ chatWallpaper: TelegramWallpaper) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedBubbleCorners(_ bubbleCorners: PresentationChatBubbleCorners) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedHasScheduledMessages(_ hasScheduledMessages: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedSubject(_ subject: ChatControllerSubject?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedAutoremoveTimeout(_ autoremoveTimeout: Int32?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedPendingUnpinnedAllMessages(_ pendingUnpinnedAllMessages: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedActiveGroupCallInfo(_ activeGroupCallInfo: ChatActiveGroupCallInfo?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedHasActiveGroupCall(_ hasActiveGroupCall: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) - } - - public func updatedImportState(_ importState: ChatPresentationImportState?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedReportReason(_ reportReason: ReportReasonData?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedShowCommands(_ showCommands: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedHasBotCommands(_ hasBotCommands: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedShowSendAsPeers(_ showSendAsPeers: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedSendAsPeers(_ sendAsPeers: [SendAsPeer]?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedCurrentSendAsPeerId(_ currentSendAsPeerId: PeerId?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedBotMenuButton(_ botMenuButton: BotMenuButton) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedShowWebView(_ showWebView: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedCopyProtectionEnabled(_ copyProtectionEnabled: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedHasAtLeast3Messages(_ hasAtLeast3Messages: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedHasPlentyOfMessages(_ hasPlentyOfMessages: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedIsPremium(_ isPremium: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedPremiumGiftOptions(_ premiumGiftOptions: [CachedPremiumGiftOption]) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedSuggestPremiumGift(_ suggestPremiumGift: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedForceInputCommandsHidden(_ forceInputCommandsHidden: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedVoiceMessagesAvailable(_ voiceMessagesAvailable: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedCustomEmojiAvailable(_ customEmojiAvailable: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedThreadData(_ threadData: ThreadData?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedForumTopicData(_ forumTopicData: ThreadData?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedIsGeneralThreadClosed(_ isGeneralThreadClosed: Bool?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedTranslationState(_ translationState: ChatPresentationTranslationState?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedReplyMessage(_ replyMessage: Message?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedAccountPeerColor(_ accountPeerColor: AccountPeerColor?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedSavedMessagesTopicPeer(_ savedMessagesTopicPeer: EnginePeer?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedHasSearchTags(_ hasSearchTags: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedIsPremiumRequiredForMessaging(_ isPremiumRequiredForMessaging: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedSendPaidMessageStars(_ sendPaidMessageStars: StarsAmount?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedAcknowledgedPaidMessage(_ acknowledgedPaidMessage: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedHasSavedChats(_ hasSavedChats: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedAppliedBoosts(_ appliedBoosts: Int32?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedBoostsToUnrestrict(_ boostsToUnrestrict: Int32?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedBusinessIntro(_ businessIntro: TelegramBusinessIntro?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedHasBirthdayToday(_ hasBirthdayToday: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedAdMessage(_ adMessage: Message?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedPeerVerification(_ peerVerification: PeerVerification?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedStarGiftsAvailable(_ starGiftsAvailable: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedAlwaysShowGiftButton(_ alwaysShowGiftButton: Bool) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedDisallowedGifts(_ disallowedGifts: TelegramDisallowedGifts?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedPersistentData(_ persistentData: PersistentPeerData) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: persistentData, removePaidMessageFeeData: self.removePaidMessageFeeData) } public func updatedRemovePaidMessageFeeData(_ removePaidMessageFeeData: RemovePaidMessageFeeData?) -> ChatPresentationInterfaceState { - return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, importState: self.importState, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: removePaidMessageFeeData) + return ChatPresentationInterfaceState(interfaceState: self.interfaceState, chatLocation: self.chatLocation, renderedPeer: self.renderedPeer, isNotAccessible: self.isNotAccessible, explicitelyCanPinMessages: self.explicitelyCanPinMessages, contactStatus: self.contactStatus, hasBots: self.hasBots, isArchived: self.isArchived, inputTextPanelState: self.inputTextPanelState, editMessageState: self.editMessageState, inputQueryResults: self.inputQueryResults, inputMode: self.inputMode, titlePanelContexts: self.titlePanelContexts, keyboardButtonsMessage: self.keyboardButtonsMessage, pinnedMessageId: self.pinnedMessageId, pinnedMessage: self.pinnedMessage, peerIsBlocked: self.peerIsBlocked, peerIsMuted: self.peerIsMuted, peerDiscussionId: self.peerDiscussionId, peerGeoLocation: self.peerGeoLocation, callsAvailable: self.callsAvailable, callsPrivate: self.callsPrivate, slowmodeState: self.slowmodeState, chatHistoryState: self.chatHistoryState, botStartPayload: self.botStartPayload, urlPreview: self.urlPreview, editingUrlPreview: self.editingUrlPreview, search: self.search, searchQuerySuggestionResult: self.searchQuerySuggestionResult, historyFilter: self.historyFilter, displayHistoryFilterAsList: self.displayHistoryFilterAsList, presentationReady: self.presentationReady, chatWallpaper: self.chatWallpaper, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, limitsConfiguration: self.limitsConfiguration, fontSize: self.fontSize, bubbleCorners: self.bubbleCorners, accountPeerId: self.accountPeerId, mode: self.mode, hasScheduledMessages: self.hasScheduledMessages, autoremoveTimeout: self.autoremoveTimeout, subject: self.subject, peerNearbyData: self.peerNearbyData, greetingData: self.greetingData, pendingUnpinnedAllMessages: self.pendingUnpinnedAllMessages, activeGroupCallInfo: self.activeGroupCallInfo, hasActiveGroupCall: self.hasActiveGroupCall, reportReason: self.reportReason, showCommands: self.showCommands, hasBotCommands: self.hasBotCommands, showSendAsPeers: self.showSendAsPeers, sendAsPeers: self.sendAsPeers, botMenuButton: self.botMenuButton, showWebView: self.showWebView, currentSendAsPeerId: self.currentSendAsPeerId, copyProtectionEnabled: self.copyProtectionEnabled, hasAtLeast3Messages: self.hasAtLeast3Messages, hasPlentyOfMessages: self.hasPlentyOfMessages, isPremium: self.isPremium, premiumGiftOptions: self.premiumGiftOptions, suggestPremiumGift: self.suggestPremiumGift, forceInputCommandsHidden: self.forceInputCommandsHidden, voiceMessagesAvailable: self.voiceMessagesAvailable, customEmojiAvailable: self.customEmojiAvailable, threadData: self.threadData, forumTopicData: self.forumTopicData, isGeneralThreadClosed: self.isGeneralThreadClosed, translationState: self.translationState, replyMessage: self.replyMessage, accountPeerColor: self.accountPeerColor, savedMessagesTopicPeer: self.savedMessagesTopicPeer, hasSearchTags: self.hasSearchTags, isPremiumRequiredForMessaging: self.isPremiumRequiredForMessaging, sendPaidMessageStars: self.sendPaidMessageStars, acknowledgedPaidMessage: self.acknowledgedPaidMessage, hasSavedChats: self.hasSavedChats, appliedBoosts: self.appliedBoosts, boostsToUnrestrict: self.boostsToUnrestrict, businessIntro: self.businessIntro, hasBirthdayToday: self.hasBirthdayToday, adMessage: self.adMessage, peerVerification: self.peerVerification, starGiftsAvailable: self.starGiftsAvailable, alwaysShowGiftButton: self.alwaysShowGiftButton, disallowedGifts: self.disallowedGifts, persistentData: self.persistentData, removePaidMessageFeeData: removePaidMessageFeeData) } } diff --git a/submodules/ChatTextLinkEditUI/BUILD b/submodules/ChatTextLinkEditUI/BUILD index e48e5676..39f5f783 100644 --- a/submodules/ChatTextLinkEditUI/BUILD +++ b/submodules/ChatTextLinkEditUI/BUILD @@ -10,14 +10,17 @@ swift_library( "-warnings-as-errors", ], deps = [ - "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", - "//submodules/AsyncDisplayKit:AsyncDisplayKit", - "//submodules/Display:Display", - "//submodules/Postbox:Postbox", - "//submodules/TelegramCore:TelegramCore", - "//submodules/AccountContext:AccountContext", - "//submodules/TelegramPresentationData:TelegramPresentationData", - "//submodules/UrlEscaping:UrlEscaping", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/Postbox", + "//submodules/TelegramCore", + "//submodules/AccountContext", + "//submodules/TelegramPresentationData", + "//submodules/UrlEscaping", + "//submodules/ComponentFlow", + "//submodules/TelegramUI/Components/AlertComponent", + "//submodules/TelegramUI/Components/AlertComponent/AlertMultilineInputFieldComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/ChatTextLinkEditUI/Sources/ChatTextLinkEditController.swift b/submodules/ChatTextLinkEditUI/Sources/ChatTextLinkEditController.swift index c802ca39..4229f746 100644 --- a/submodules/ChatTextLinkEditUI/Sources/ChatTextLinkEditController.swift +++ b/submodules/ChatTextLinkEditUI/Sources/ChatTextLinkEditController.swift @@ -7,458 +7,90 @@ import TelegramCore import TelegramPresentationData import AccountContext import UrlEscaping +import ComponentFlow +import AlertComponent +import AlertMultilineInputFieldComponent -private final class ChatTextLinkEditInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegate { - private var theme: PresentationTheme - private let backgroundNode: ASImageNode - fileprivate let textInputNode: EditableTextNode - private let placeholderNode: ASTextNode +public func chatTextLinkEditController( + context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, + text: String, + link: String?, + apply: @escaping (String?) -> Void +) -> ViewController { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let strings = presentationData.strings + + let inputState = AlertMultilineInputFieldComponent.ExternalState() - var updateHeight: (() -> Void)? - var complete: (() -> Void)? - var textChanged: ((String) -> Void)? + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: link != nil ? strings.TextFormat_EditLinkTitle : strings.TextFormat_AddLinkTitle) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(strings.TextFormat_AddLinkText(text).string)) + ) + )) - private let backgroundInsets = UIEdgeInsets(top: 8.0, left: 16.0, bottom: 15.0, right: 16.0) - private let inputInsets = UIEdgeInsets(top: 5.0, left: 12.0, bottom: 5.0, right: 12.0) - - var text: String { - get { - return self.textInputNode.attributedText?.string ?? "" - } - set { - self.textInputNode.attributedText = NSAttributedString(string: newValue, font: Font.regular(17.0), textColor: self.theme.actionSheet.inputTextColor) - self.placeholderNode.isHidden = !newValue.isEmpty - } - } - - var placeholder: String = "" { - didSet { - self.placeholderNode.attributedText = NSAttributedString(string: self.placeholder, font: Font.regular(17.0), textColor: self.theme.actionSheet.inputPlaceholderColor) - } - } - - init(theme: PresentationTheme, placeholder: String) { - self.theme = theme - - self.backgroundNode = ASImageNode() - self.backgroundNode.isLayerBacked = true - self.backgroundNode.displaysAsynchronously = false - self.backgroundNode.displayWithoutProcessing = true - self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 12.0, color: theme.actionSheet.inputHollowBackgroundColor, strokeColor: theme.actionSheet.inputBorderColor, strokeWidth: 1.0) - - self.textInputNode = EditableTextNode() - self.textInputNode.typingAttributes = [NSAttributedString.Key.font.rawValue: Font.regular(17.0), NSAttributedString.Key.foregroundColor.rawValue: theme.actionSheet.inputTextColor] - self.textInputNode.clipsToBounds = true - self.textInputNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0) - self.textInputNode.textContainerInset = UIEdgeInsets(top: self.inputInsets.top, left: 0.0, bottom: self.inputInsets.bottom, right: 0.0) - self.textInputNode.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance - self.textInputNode.keyboardType = .URL - self.textInputNode.autocapitalizationType = .none - self.textInputNode.returnKeyType = .done - self.textInputNode.autocorrectionType = .no - self.textInputNode.tintColor = theme.actionSheet.controlAccentColor - - self.placeholderNode = ASTextNode() - self.placeholderNode.isUserInteractionEnabled = false - self.placeholderNode.displaysAsynchronously = false - self.placeholderNode.attributedText = NSAttributedString(string: placeholder, font: Font.regular(17.0), textColor: self.theme.actionSheet.inputPlaceholderColor) - - super.init() - - self.textInputNode.delegate = self - - self.addSubnode(self.backgroundNode) - self.addSubnode(self.textInputNode) - self.addSubnode(self.placeholderNode) - } - - func updateTheme(_ theme: PresentationTheme) { - self.theme = theme - - self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 12.0, color: self.theme.actionSheet.inputHollowBackgroundColor, strokeColor: self.theme.actionSheet.inputBorderColor, strokeWidth: 1.0) - self.textInputNode.keyboardAppearance = self.theme.rootController.keyboardColor.keyboardAppearance - self.placeholderNode.attributedText = NSAttributedString(string: self.placeholderNode.attributedText?.string ?? "", font: Font.regular(17.0), textColor: self.theme.actionSheet.inputPlaceholderColor) - self.textInputNode.tintColor = self.theme.actionSheet.controlAccentColor - } - - func updateLayout(width: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat { - let backgroundInsets = self.backgroundInsets - let inputInsets = self.inputInsets - - let textFieldHeight = self.calculateTextFieldMetrics(width: width) - let panelHeight = textFieldHeight + backgroundInsets.top + backgroundInsets.bottom - - let backgroundFrame = CGRect(origin: CGPoint(x: backgroundInsets.left, y: backgroundInsets.top), size: CGSize(width: width - backgroundInsets.left - backgroundInsets.right, height: panelHeight - backgroundInsets.top - backgroundInsets.bottom)) - transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame) - - let placeholderSize = self.placeholderNode.measure(backgroundFrame.size) - transition.updateFrame(node: self.placeholderNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + inputInsets.left, y: backgroundFrame.minY + floor((backgroundFrame.size.height - placeholderSize.height) / 2.0)), size: placeholderSize)) - - transition.updateFrame(node: self.textInputNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + inputInsets.left, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.size.width - inputInsets.left - inputInsets.right, height: backgroundFrame.size.height))) - - return panelHeight - } - - func activateInput() { - self.textInputNode.becomeFirstResponder() - } - - func deactivateInput() { - self.textInputNode.resignFirstResponder() - } - - @objc func editableTextNodeDidUpdateText(_ editableTextNode: ASEditableTextNode) { - self.updateTextNodeText(animated: true) - self.textChanged?(editableTextNode.textView.text) - self.placeholderNode.isHidden = !(editableTextNode.textView.text ?? "").isEmpty - } - - func editableTextNode(_ editableTextNode: ASEditableTextNode, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { - if text == "\n" { - self.complete?() - return false - } - return true - } - - private func calculateTextFieldMetrics(width: CGFloat) -> CGFloat { - let backgroundInsets = self.backgroundInsets - let inputInsets = self.inputInsets - - let unboundTextFieldHeight = max(33.0, ceil(self.textInputNode.measure(CGSize(width: width - backgroundInsets.left - backgroundInsets.right - inputInsets.left - inputInsets.right, height: CGFloat.greatestFiniteMagnitude)).height)) - - return min(61.0, max(33.0, unboundTextFieldHeight)) - } - - private func updateTextNodeText(animated: Bool) { - let backgroundInsets = self.backgroundInsets - - let textFieldHeight = self.calculateTextFieldMetrics(width: self.bounds.size.width) - - let panelHeight = textFieldHeight + backgroundInsets.top + backgroundInsets.bottom - if !self.bounds.size.height.isEqual(to: panelHeight) { - self.updateHeight?() - } - } - - @objc func clearPressed() { - self.textInputNode.attributedText = nil - self.deactivateInput() - } -} - -private final class ChatTextLinkEditAlertContentNode: AlertContentNode { - private let strings: PresentationStrings - private let text: String - - private let titleNode: ASTextNode - private let textNode: ASTextNode - let inputFieldNode: ChatTextLinkEditInputFieldNode - - private let actionNodesSeparator: ASDisplayNode - private let actionNodes: [TextAlertContentActionNode] - private let actionVerticalSeparators: [ASDisplayNode] - - private let disposable = MetaDisposable() - - private var validLayout: CGSize? - - private let hapticFeedback = HapticFeedback() - - var complete: (() -> Void)? { - didSet { - self.inputFieldNode.complete = self.complete - } - } - - override var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled - } - - private var isEditing = false - private let allowEmpty: Bool - - init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction], text: String, link: String?, allowEmpty: Bool) { - self.strings = strings - self.text = text - self.isEditing = link != nil - self.allowEmpty = allowEmpty - - self.titleNode = ASTextNode() - self.titleNode.maximumNumberOfLines = 2 - self.textNode = ASTextNode() - self.textNode.maximumNumberOfLines = 2 - - self.inputFieldNode = ChatTextLinkEditInputFieldNode(theme: ptheme, placeholder: strings.TextFormat_AddLinkPlaceholder) - self.inputFieldNode.text = link ?? "" - - self.actionNodesSeparator = ASDisplayNode() - self.actionNodesSeparator.isLayerBacked = true - - self.actionNodes = actions.map { action -> TextAlertContentActionNode in - return TextAlertContentActionNode(theme: theme, action: action) - } - - var actionVerticalSeparators: [ASDisplayNode] = [] - if actions.count > 1 { - for _ in 0 ..< actions.count - 1 { - let separatorNode = ASDisplayNode() - separatorNode.isLayerBacked = true - actionVerticalSeparators.append(separatorNode) - } - } - self.actionVerticalSeparators = actionVerticalSeparators - - super.init() - - self.addSubnode(self.titleNode) - self.addSubnode(self.textNode) - - self.addSubnode(self.inputFieldNode) - - self.addSubnode(self.actionNodesSeparator) - - for actionNode in self.actionNodes { - self.addSubnode(actionNode) - } - self.actionNodes.last?.actionEnabled = !(link ?? "").isEmpty - if allowEmpty { - self.actionNodes.last?.actionEnabled = true - } - - for separatorNode in self.actionVerticalSeparators { - self.addSubnode(separatorNode) - } - - self.inputFieldNode.updateHeight = { [weak self] in - if let strongSelf = self { - if let _ = strongSelf.validLayout { - strongSelf.requestLayout?(.animated(duration: 0.15, curve: .spring)) - } - } - } - - self.inputFieldNode.textChanged = { [weak self] text in - if let strongSelf = self, let lastNode = strongSelf.actionNodes.last { - if strongSelf.allowEmpty { - lastNode.actionEnabled = true - } else { - lastNode.actionEnabled = !text.isEmpty - } - } - } - - self.updateTheme(theme) - - if (link ?? "").isEmpty { - Queue.mainQueue().after(0.1, { - let pasteboard = UIPasteboard.general - if pasteboard.hasURLs { - if let url = pasteboard.url?.absoluteString, !url.isEmpty { - self.inputFieldNode.text = url - if let lastNode = self.actionNodes.last { - lastNode.actionEnabled = true - } - self.inputFieldNode.textInputNode.textView.selectAll(nil) - } - } - }) - } - } - - deinit { - self.disposable.dispose() - } - - var link: String { - return self.inputFieldNode.text - } - - override func updateTheme(_ theme: AlertControllerTheme) { - self.titleNode.attributedText = NSAttributedString(string: self.isEditing ? self.strings.TextFormat_EditLinkTitle : self.strings.TextFormat_AddLinkTitle, font: Font.bold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center) - self.textNode.attributedText = NSAttributedString(string: self.strings.TextFormat_AddLinkText(self.text).string, font: Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center) - - self.actionNodesSeparator.backgroundColor = theme.separatorColor - for actionNode in self.actionNodes { - actionNode.updateTheme(theme) - } - for separatorNode in self.actionVerticalSeparators { - separatorNode.backgroundColor = theme.separatorColor - } - - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) - } - } - - override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - var size = size - size.width = min(size.width, 270.0) - let measureSize = CGSize(width: size.width - 16.0 * 2.0, height: CGFloat.greatestFiniteMagnitude) - - let hadValidLayout = self.validLayout != nil - - self.validLayout = size - - var origin: CGPoint = CGPoint(x: 0.0, y: 20.0) - let spacing: CGFloat = 5.0 - - let titleSize = self.titleNode.measure(measureSize) - transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize)) - origin.y += titleSize.height + 4.0 - - let textSize = self.textNode.measure(measureSize) - transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize)) - origin.y += textSize.height + 6.0 + spacing - - let actionButtonHeight: CGFloat = 44.0 - var minActionsWidth: CGFloat = 0.0 - let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) - let actionTitleInsets: CGFloat = 8.0 - - var effectiveActionLayout = TextAlertContentActionLayout.horizontal - for actionNode in self.actionNodes { - let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) - if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 { - effectiveActionLayout = .vertical - } - switch effectiveActionLayout { - case .horizontal: - minActionsWidth += actionTitleSize.width + actionTitleInsets - case .vertical: - minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) - } - } - - let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 9.0, right: 18.0) - - var contentWidth = max(titleSize.width, minActionsWidth) - contentWidth = max(contentWidth, 234.0) - - var actionsHeight: CGFloat = 0.0 - switch effectiveActionLayout { - case .horizontal: - actionsHeight = actionButtonHeight - case .vertical: - actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) - } - - let resultWidth = contentWidth + insets.left + insets.right - - let inputFieldWidth = resultWidth - let inputFieldHeight = self.inputFieldNode.updateLayout(width: inputFieldWidth, transition: transition) - let inputHeight = inputFieldHeight - transition.updateFrame(node: self.inputFieldNode, frame: CGRect(x: 0.0, y: origin.y, width: resultWidth, height: inputFieldHeight)) - transition.updateAlpha(node: self.inputFieldNode, alpha: inputHeight > 0.0 ? 1.0 : 0.0) - - let resultSize = CGSize(width: resultWidth, height: titleSize.height + textSize.height + spacing + inputHeight + actionsHeight + insets.top + insets.bottom) - - transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - - var actionOffset: CGFloat = 0.0 - let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) - var separatorIndex = -1 - var nodeIndex = 0 - for actionNode in self.actionNodes { - if separatorIndex >= 0 { - let separatorNode = self.actionVerticalSeparators[separatorIndex] - switch effectiveActionLayout { - case .horizontal: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) - case .vertical: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - } - } - separatorIndex += 1 - - let currentActionWidth: CGFloat - switch effectiveActionLayout { - case .horizontal: - if nodeIndex == self.actionNodes.count - 1 { - currentActionWidth = resultSize.width - actionOffset - } else { - currentActionWidth = actionWidth - } - case .vertical: - currentActionWidth = resultSize.width - } - - let actionNodeFrame: CGRect - switch effectiveActionLayout { - case .horizontal: - actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += currentActionWidth - case .vertical: - actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += actionButtonHeight - } - - transition.updateFrame(node: actionNode, frame: actionNodeFrame) - - nodeIndex += 1 - } - - if !hadValidLayout { - self.inputFieldNode.activateInput() - } - - return resultSize - } - - func animateError() { - self.inputFieldNode.layer.addShakeAnimation() - self.hapticFeedback.error() - } -} - -public func chatTextLinkEditController(sharedContext: SharedAccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, account: Account, text: String, link: String?, allowEmpty: Bool = false, apply: @escaping (String?) -> Void) -> AlertController { - let presentationData = updatedPresentationData?.initial ?? sharedContext.currentPresentationData.with { $0 } - - var dismissImpl: ((Bool) -> Void)? var applyImpl: (() -> Void)? + content.append(AnyComponentWithIdentity( + id: "input", + component: AnyComponent( + AlertMultilineInputFieldComponent( + context: context, + initialValue: link.flatMap { NSAttributedString(string: $0) }, + placeholder: strings.TextFormat_AddLinkPlaceholder, + returnKeyType: .done, + keyboardType: .URL, + autocapitalizationType: .none, + autocorrectionType: .no, + isInitiallyFocused: true, + externalState: inputState, + returnKeyAction: { + applyImpl?() + } + ) + ) + )) - let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - dismissImpl?(true) - apply(nil) - }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Done, action: { - applyImpl?() - })] - - let contentNode = ChatTextLinkEditAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: actions, text: text, link: link, allowEmpty: allowEmpty) - contentNode.complete = { - applyImpl?() + var effectiveUpdatedPresentationData: (PresentationData, Signal) + if let updatedPresentationData { + effectiveUpdatedPresentationData = updatedPresentationData + } else { + effectiveUpdatedPresentationData = (presentationData, context.sharedContext.presentationData) } - applyImpl = { [weak contentNode] in - guard let contentNode = contentNode else { - return - } - let updatedLink = explicitUrl(contentNode.link) + + var dismissImpl: (() -> Void)? + let alertController = AlertScreen( + configuration: AlertScreen.Configuration(allowInputInset: true), + content: content, + actions: [ + .init(title: strings.Common_Cancel), + .init(title: strings.Common_Done, type: .default, action: { + applyImpl?() + }, autoDismiss: false) + ], + updatedPresentationData: effectiveUpdatedPresentationData + ) + applyImpl = { + let updatedLink = explicitUrl(inputState.value.string) if !updatedLink.isEmpty && isValidUrl(updatedLink, validSchemes: ["http": true, "https": true, "tg": false, "ton": false, "tonsite": true]) { - dismissImpl?(true) + dismissImpl?() apply(updatedLink) - } else if allowEmpty && contentNode.link.isEmpty { - dismissImpl?(true) + } else if inputState.value.string.isEmpty { + dismissImpl?() apply("") } else { - contentNode.animateError() + inputState.animateError() } } - - let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode) - let presentationDataDisposable = (updatedPresentationData?.signal ?? sharedContext.presentationData).start(next: { [weak controller, weak contentNode] presentationData in - controller?.theme = AlertControllerTheme(presentationData: presentationData) - contentNode?.inputFieldNode.updateTheme(presentationData.theme) - }) - controller.dismissed = { _ in - presentationDataDisposable.dispose() + dismissImpl = { [weak alertController] in + alertController?.dismiss(completion: nil) } - dismissImpl = { [weak controller] animated in - contentNode.inputFieldNode.deactivateInput() - if animated { - controller?.dismissAnimated() - } else { - controller?.dismiss() - } - } - return controller + return alertController } diff --git a/submodules/ChatTitleActivityNode/Sources/ChatTitleActivityContentNode.swift b/submodules/ChatTitleActivityNode/Sources/ChatTitleActivityContentNode.swift index 1944d18b..11a1b756 100644 --- a/submodules/ChatTitleActivityNode/Sources/ChatTitleActivityContentNode.swift +++ b/submodules/ChatTitleActivityNode/Sources/ChatTitleActivityContentNode.swift @@ -110,7 +110,7 @@ public class ChatTitleActivityContentNode: ASDisplayNode { }) if case .slide = style { - self.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: 14.0), duration: transitionDuration, additive: true) + self.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: 4.0), duration: transitionDuration, additive: true) } } @@ -118,7 +118,7 @@ public class ChatTitleActivityContentNode: ASDisplayNode { self.layer.animateAlpha(from: 0.0, to: 1.0, duration: transitionDuration) if case .slide = style { - self.layer.animatePosition(from: CGPoint(x: 0.0, y: -14.0), to: CGPoint(), duration: transitionDuration, additive: true) + self.layer.animatePosition(from: CGPoint(x: 0.0, y: -4.0), to: CGPoint(), duration: transitionDuration, additive: true) } } diff --git a/submodules/ChatTitleActivityNode/Sources/ChatTitleActivityNode.swift b/submodules/ChatTitleActivityNode/Sources/ChatTitleActivityNode.swift index b9013f66..86efa6d0 100644 --- a/submodules/ChatTitleActivityNode/Sources/ChatTitleActivityNode.swift +++ b/submodules/ChatTitleActivityNode/Sources/ChatTitleActivityNode.swift @@ -130,6 +130,9 @@ public class ChatTitleActivityNode: ASDisplayNode { } public func updateLayout(_ constrainedSize: CGSize, offset: CGFloat = 0.0, alignment: NSTextAlignment) -> CGSize { - return CGSize(width: 0.0, height: self.contentNode?.updateLayout(constrainedSize, offset: offset, alignment: alignment).height ?? 0.0) + guard let contentSize = self.contentNode?.updateLayout(constrainedSize, offset: offset, alignment: alignment) else { + return CGSize() + } + return contentSize } } diff --git a/submodules/ComponentFlow/Source/Base/CombinedComponent.swift b/submodules/ComponentFlow/Source/Base/CombinedComponent.swift index 5f019bc5..3c818879 100644 --- a/submodules/ComponentFlow/Source/Base/CombinedComponent.swift +++ b/submodules/ComponentFlow/Source/Base/CombinedComponent.swift @@ -410,7 +410,7 @@ public final class CombinedComponentContext { public let component: ComponentType public let availableSize: CGSize public let transition: ComponentTransition - private let addImpl: (_ updatedComponent: _UpdatedChildComponent) -> Void + private let addImpl: (_ updatedComponent: _UpdatedChildComponent, _ container: UIView?) -> Void public var environment: Environment { return self.context.environment @@ -425,7 +425,7 @@ public final class CombinedComponentContext { component: ComponentType, availableSize: CGSize, transition: ComponentTransition, - add: @escaping (_ updatedComponent: _UpdatedChildComponent) -> Void + add: @escaping (_ updatedComponent: _UpdatedChildComponent, _ container: UIView?) -> Void ) { self.context = context self.view = view @@ -436,7 +436,11 @@ public final class CombinedComponentContext { } public func add(_ updatedComponent: _UpdatedChildComponent) { - self.addImpl(updatedComponent) + self.addImpl(updatedComponent, nil) + } + + public func addWithExternalContainer(_ updatedComponent: _UpdatedChildComponent, container: UIView) { + self.addImpl(updatedComponent, container) } } @@ -671,7 +675,7 @@ public extension CombinedComponent { component: self, availableSize: availableSize, transition: transition, - add: { updatedChild in + add: { updatedChild, optionalContainer in if !addedChildIds.insert(updatedChild.id).inserted { preconditionFailure("Child component can only be added once") } @@ -692,7 +696,11 @@ public extension CombinedComponent { context.childViewIndices.remove(at: previousView.index) context.childViewIndices.insert(updatedChild.id, at: index) previousView.index = index - view.insertSubview(previousView.view, at: index) + if let optionalContainer { + optionalContainer.addSubview(previousView.view) + } else { + view.insertSubview(previousView.view, at: index) + } } previousView.updateGestures(updatedChild.gestures) @@ -715,7 +723,11 @@ public extension CombinedComponent { childView.transition = updatedChild.transitionDisappear childView.transitionWithGuide = updatedChild.transitionDisappearWithGuide - view.insertSubview(updatedChild.view, at: index) + if let optionalContainer { + optionalContainer.addSubview(updatedChild.view) + } else { + view.insertSubview(updatedChild.view, at: index) + } updatedChild.view.layer.anchorPoint = updatedChild._anchorPoint ?? CGPoint(x: 0.5, y: 0.5) diff --git a/submodules/ComponentFlow/Source/Base/Transition.swift b/submodules/ComponentFlow/Source/Base/Transition.swift index 4dcd0401..4beba45c 100644 --- a/submodules/ComponentFlow/Source/Base/Transition.swift +++ b/submodules/ComponentFlow/Source/Base/Transition.swift @@ -489,7 +489,25 @@ public struct ComponentTransition { } public func setAlpha(view: UIView, alpha: CGFloat, delay: Double = 0.0, completion: ((Bool) -> Void)? = nil) { - self.setAlpha(layer: view.layer, alpha: alpha, delay: delay, completion: completion) + if view.alpha == alpha { + completion?(true) + return + } + switch self.animation { + case .none: + view.alpha = alpha + view.layer.removeAnimation(forKey: "opacity") + completion?(true) + case .curve: + let previousAlpha: Float + if view.layer.animation(forKey: "opacity") != nil { + previousAlpha = view.layer.presentation()?.opacity ?? Float(view.alpha) + } else { + previousAlpha = Float(view.alpha) + } + view.alpha = alpha + self.animateAlpha(layer: view.layer, from: CGFloat(previousAlpha), to: alpha, delay: delay, completion: completion) + } } public func setAlpha(layer: CALayer, alpha: CGFloat, delay: Double = 0.0, completion: ((Bool) -> Void)? = nil) { @@ -1335,6 +1353,41 @@ public struct ComponentTransition { completion: completion ) } + + public func setBlur(layer: CALayer, radius: CGFloat, completion: ((Bool) -> Void)? = nil) { + var currentRadius: CGFloat = 0.0 + if let currentFilters = layer.filters { + for filter in currentFilters { + if let filter = filter as? NSObject, filter.description.contains("gaussianBlur") { + currentRadius = filter.value(forKey: "inputRadius") as? CGFloat ?? 0.0 + } + } + } + + if currentRadius == radius { + completion?(true) + return + } + + if let blurFilter = CALayer.blur() { + blurFilter.setValue(radius as NSNumber, forKey: "inputRadius") + layer.filters = [blurFilter] + switch self.animation { + case .none: + completion?(true) + case let .curve(duration, curve): + layer.animate(from: currentRadius as NSNumber, to: radius as NSNumber, keyPath: "filters.gaussianBlur.inputRadius", duration: duration, delay: 0.0, curve: curve, removeOnCompletion: true, additive: false,completion: { [weak layer] flag in + if let layer { + if radius <= 0.0 { + layer.filters = nil + } + } + + completion?(flag) + }) + } + } + } public func animateBlur(layer: CALayer, fromRadius: CGFloat, toRadius: CGFloat, delay: Double = 0.0, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) { let duration: Double @@ -1359,4 +1412,23 @@ public struct ComponentTransition { }) } } + + public func animateMeshTransform(layer: CALayer, from fromValue: NSObject, to toValue: NSObject, delay: Double = 0.0, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) { + switch self.animation { + case .none: + completion?(true) + case let .curve(duration, curve): + layer.animate( + from: fromValue, + to: toValue, + keyPath: "meshTransform", + duration: duration, + delay: delay, + curve: curve, + removeOnCompletion: removeOnCompletion, + additive: false, + completion: completion + ) + } + } } diff --git a/submodules/ComponentFlow/Source/Components/Button.swift b/submodules/ComponentFlow/Source/Components/Button.swift index c93ef055..a40aeb0d 100644 --- a/submodules/ComponentFlow/Source/Components/Button.swift +++ b/submodules/ComponentFlow/Source/Components/Button.swift @@ -3,6 +3,7 @@ import UIKit public final class Button: Component { public let content: AnyComponent + public let contentInsets: UIEdgeInsets public let minSize: CGSize? public let hitTestEdgeInsets: UIEdgeInsets? public let tag: AnyObject? @@ -15,6 +16,7 @@ public final class Button: Component { convenience public init( content: AnyComponent, + contentInsets: UIEdgeInsets = UIEdgeInsets(), isEnabled: Bool = true, automaticHighlight: Bool = true, action: @escaping () -> Void, @@ -22,6 +24,7 @@ public final class Button: Component { ) { self.init( content: content, + contentInsets: contentInsets, minSize: nil, hitTestEdgeInsets: nil, tag: nil, @@ -35,6 +38,7 @@ public final class Button: Component { private init( content: AnyComponent, + contentInsets: UIEdgeInsets = UIEdgeInsets(), minSize: CGSize? = nil, hitTestEdgeInsets: UIEdgeInsets? = nil, tag: AnyObject? = nil, @@ -46,6 +50,7 @@ public final class Button: Component { highlightedAction: ActionSlot? ) { self.content = content + self.contentInsets = contentInsets self.minSize = minSize self.hitTestEdgeInsets = hitTestEdgeInsets self.tag = tag @@ -60,6 +65,7 @@ public final class Button: Component { public func minSize(_ minSize: CGSize?) -> Button { return Button( content: self.content, + contentInsets: self.contentInsets, minSize: minSize, hitTestEdgeInsets: self.hitTestEdgeInsets, tag: self.tag, @@ -75,6 +81,7 @@ public final class Button: Component { public func withHitTestEdgeInsets(_ hitTestEdgeInsets: UIEdgeInsets?) -> Button { return Button( content: self.content, + contentInsets: self.contentInsets, minSize: self.minSize, hitTestEdgeInsets: hitTestEdgeInsets, tag: self.tag, @@ -90,6 +97,7 @@ public final class Button: Component { public func withIsExclusive(_ isExclusive: Bool) -> Button { return Button( content: self.content, + contentInsets: self.contentInsets, minSize: self.minSize, hitTestEdgeInsets: self.hitTestEdgeInsets, tag: self.tag, @@ -106,6 +114,7 @@ public final class Button: Component { public func withHoldAction(_ holdAction: ((UIView) -> Void)?) -> Button { return Button( content: self.content, + contentInsets: self.contentInsets, minSize: self.minSize, hitTestEdgeInsets: self.hitTestEdgeInsets, tag: self.tag, @@ -121,6 +130,7 @@ public final class Button: Component { public func tagged(_ tag: AnyObject) -> Button { return Button( content: self.content, + contentInsets: self.contentInsets, minSize: self.minSize, hitTestEdgeInsets: self.hitTestEdgeInsets, tag: tag, @@ -137,6 +147,9 @@ public final class Button: Component { if lhs.content != rhs.content { return false } + if lhs.contentInsets != rhs.contentInsets { + return false + } if lhs.minSize != rhs.minSize { return false } @@ -318,6 +331,8 @@ public final class Button: Component { size.width = max(size.width, minSize.width) size.height = max(size.height, minSize.height) } + size.width += component.contentInsets.left + component.contentInsets.right + size.height += component.contentInsets.top + component.contentInsets.bottom self.component = component diff --git a/submodules/ComponentFlow/Source/Components/Image.swift b/submodules/ComponentFlow/Source/Components/Image.swift index 78475c94..f18aba2e 100644 --- a/submodules/ComponentFlow/Source/Components/Image.swift +++ b/submodules/ComponentFlow/Source/Components/Image.swift @@ -6,17 +6,20 @@ public final class Image: Component { public let tintColor: UIColor? public let size: CGSize? public let contentMode: UIImageView.ContentMode + public let cornerRadius: CGFloat public init( image: UIImage?, tintColor: UIColor? = nil, size: CGSize? = nil, - contentMode: UIImageView.ContentMode = .scaleToFill + contentMode: UIImageView.ContentMode = .scaleToFill, + cornerRadius: CGFloat = 0.0 ) { self.image = image self.tintColor = tintColor self.size = size self.contentMode = contentMode + self.cornerRadius = cornerRadius } public static func ==(lhs: Image, rhs: Image) -> Bool { @@ -32,6 +35,9 @@ public final class Image: Component { if lhs.contentMode != rhs.contentMode { return false } + if lhs.cornerRadius != rhs.cornerRadius { + return false + } return true } @@ -47,7 +53,9 @@ public final class Image: Component { func update(component: Image, availableSize: CGSize, environment: Environment, transition: ComponentTransition) -> CGSize { self.image = component.image self.contentMode = component.contentMode - + self.clipsToBounds = component.cornerRadius > 0.0 + + transition.setCornerRadius(layer: self.layer, cornerRadius: component.cornerRadius) transition.setTintColor(view: self, color: component.tintColor ?? .white) switch component.contentMode { diff --git a/submodules/Components/MultilineTextWithEntitiesComponent/Sources/MultilineTextWithEntitiesComponent.swift b/submodules/Components/MultilineTextWithEntitiesComponent/Sources/MultilineTextWithEntitiesComponent.swift index 7766272b..2519f2f6 100644 --- a/submodules/Components/MultilineTextWithEntitiesComponent/Sources/MultilineTextWithEntitiesComponent.swift +++ b/submodules/Components/MultilineTextWithEntitiesComponent/Sources/MultilineTextWithEntitiesComponent.swift @@ -43,6 +43,7 @@ public final class MultilineTextWithEntitiesComponent: Component { public let handleSpoilers: Bool public let manualVisibilityControl: Bool public let resetAnimationsOnVisibilityChange: Bool + public let enableLooping: Bool public let displaysAsynchronously: Bool public let maxWidth: CGFloat? public let highlightAction: (([NSAttributedString.Key: Any]) -> NSAttributedString.Key?)? @@ -71,6 +72,7 @@ public final class MultilineTextWithEntitiesComponent: Component { handleSpoilers: Bool = false, manualVisibilityControl: Bool = false, resetAnimationsOnVisibilityChange: Bool = false, + enableLooping: Bool = true, displaysAsynchronously: Bool = true, maxWidth: CGFloat? = nil, highlightAction: (([NSAttributedString.Key: Any]) -> NSAttributedString.Key?)? = nil, @@ -99,6 +101,7 @@ public final class MultilineTextWithEntitiesComponent: Component { self.handleSpoilers = handleSpoilers self.manualVisibilityControl = manualVisibilityControl self.resetAnimationsOnVisibilityChange = resetAnimationsOnVisibilityChange + self.enableLooping = enableLooping self.displaysAsynchronously = displaysAsynchronously self.maxWidth = maxWidth self.tapAction = tapAction @@ -142,6 +145,9 @@ public final class MultilineTextWithEntitiesComponent: Component { if lhs.resetAnimationsOnVisibilityChange != rhs.resetAnimationsOnVisibilityChange { return false } + if lhs.enableLooping != rhs.enableLooping { + return false + } if lhs.displaysAsynchronously != rhs.displaysAsynchronously { return false } @@ -250,6 +256,7 @@ public final class MultilineTextWithEntitiesComponent: Component { self.textNode.tapAttributeAction = component.tapAction self.textNode.longTapAttributeAction = component.longTapAction self.textNode.spoilerColor = component.spoilerColor + self.textNode.enableLooping = component.enableLooping self.textNode.resetEmojiToFirstFrameAutomatically = component.resetAnimationsOnVisibilityChange diff --git a/submodules/Components/PagerComponent/Sources/PagerComponent.swift b/submodules/Components/PagerComponent/Sources/PagerComponent.swift index 42f1c2b0..4b11ff0e 100644 --- a/submodules/Components/PagerComponent/Sources/PagerComponent.swift +++ b/submodules/Components/PagerComponent/Sources/PagerComponent.swift @@ -974,8 +974,9 @@ public final class PagerComponent: Component { public typealias EnvironmentType = (ChildEnvironmentType, SheetComponentEnvironment) @@ -342,7 +341,7 @@ public final class SheetComponent: C let contentOffset = (self.scrollView.contentOffset.y + self.scrollView.contentInset.top - self.scrollView.contentSize.height) * -1.0 let dismissalOffset = self.scrollView.contentSize.height + abs(contentView.frame.minY) let delta = dismissalOffset - contentOffset - var targetPosition = self.scrollView.center.y + delta + var targetPosition = self.scrollView.center.y + delta + 6.0 if self.isCentered { targetPosition = self.frame.height + self.scrollView.frame.height * 0.5 } @@ -351,7 +350,7 @@ public final class SheetComponent: C completion() }) } else { - var targetOffset: CGFloat = self.scrollView.contentSize.height + abs(contentView.frame.minY) + var targetOffset: CGFloat = self.scrollView.contentSize.height + abs(contentView.frame.minY) + 6.0 if self.isCentered { targetOffset = self.frame.height + self.scrollView.frame.height * 0.5 } @@ -468,7 +467,7 @@ public final class SheetComponent: C switch component.style { case .glass: let clipFrame = CGRect(origin: CGPoint(x: glassInset, y: -glassInset), size: CGSize(width: contentSize.width, height: contentSize.height)) - self.clipView.update(size: clipFrame.size, color: .clear, topCornerRadius: topCornerRadius, bottomCornerRadius: bottomCornerRadius, transition: transition) + self.clipView.update(size: clipFrame.size, color: .clear, topCornerRadius: topCornerRadius - 1.5, bottomCornerRadius: bottomCornerRadius, transition: transition) transition.setFrame(view: self.clipView, frame: clipFrame) transition.setFrame(view: contentView, frame: CGRect(origin: .zero, size: CGSize(width: contentSize.width, height: contentSize.height)), completion: nil) transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(x: glassInset, y: -glassInset), size: CGSize(width: contentSize.width, height: contentSize.height)), completion: nil) @@ -482,7 +481,7 @@ public final class SheetComponent: C transition.setFrame(view: effectView, frame: CGRect(origin: .zero, size: CGSize(width: contentSize.width, height: contentSize.height + 1000.0)), completion: nil) } } - self.backgroundView.update(size: contentSize, color: backgroundColor, topCornerRadius: topCornerRadius, bottomCornerRadius: bottomCornerRadius, transition: transition) + self.backgroundView.update(size: contentSize, color: backgroundColor, topCornerRadius: topCornerRadius + 1.5, bottomCornerRadius: bottomCornerRadius, transition: transition) } } transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(), size: availableSize), completion: nil) diff --git a/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift b/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift index 25d70a78..e017ad64 100644 --- a/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift +++ b/submodules/Components/ViewControllerComponent/Sources/ViewControllerComponent.swift @@ -280,12 +280,14 @@ open class ViewControllerComponentContainer: ViewController { case .none: navigationBarPresentationData = nil case .transparent: - navigationBarPresentationData = NavigationBarPresentationData(presentationData: presentationData, hideBackground: true, hideBadge: false, hideSeparator: true) + navigationBarPresentationData = NavigationBarPresentationData(presentationData: presentationData, hideBackground: true, hideBadge: false, hideSeparator: true, style: .glass) case .default: - navigationBarPresentationData = NavigationBarPresentationData(presentationData: presentationData) + navigationBarPresentationData = NavigationBarPresentationData(presentationData: presentationData, style: .glass) } super.init(navigationBarPresentationData: navigationBarPresentationData) + self._hasGlassStyle = true + self.setupPresentationData(effectiveUpdatedPresentationData, navigationBarAppearance: navigationBarAppearance, statusBarStyle: statusBarStyle, presentationMode: presentationMode) } @@ -308,9 +310,9 @@ open class ViewControllerComponentContainer: ViewController { case .none: navigationBarPresentationData = nil case .transparent: - navigationBarPresentationData = NavigationBarPresentationData(presentationData: presentationData, hideBackground: true, hideBadge: false, hideSeparator: true) + navigationBarPresentationData = NavigationBarPresentationData(presentationData: presentationData, hideBackground: true, hideBadge: false, hideSeparator: true, style: .glass) case .default: - navigationBarPresentationData = NavigationBarPresentationData(presentationData: presentationData) + navigationBarPresentationData = NavigationBarPresentationData(presentationData: presentationData, style: .glass) } super.init(navigationBarPresentationData: navigationBarPresentationData) @@ -356,12 +358,12 @@ open class ViewControllerComponentContainer: ViewController { case .none: navigationBarPresentationData = nil case .transparent: - navigationBarPresentationData = NavigationBarPresentationData(presentationData: presentationData, hideBackground: true, hideBadge: false, hideSeparator: true) + navigationBarPresentationData = NavigationBarPresentationData(presentationData: presentationData, hideBackground: true, hideBadge: false, hideSeparator: true, style: .glass) case .default: - navigationBarPresentationData = NavigationBarPresentationData(presentationData: presentationData) + navigationBarPresentationData = NavigationBarPresentationData(presentationData: presentationData, style: .glass) } if let navigationBarPresentationData { - strongSelf.navigationBar?.updatePresentationData(navigationBarPresentationData) + strongSelf.navigationBar?.updatePresentationData(navigationBarPresentationData, transition: .immediate) } if let layout = strongSelf.validLayout { diff --git a/submodules/ComposePollUI/BUILD b/submodules/ComposePollUI/BUILD index 04f0e88a..db6a0ef3 100644 --- a/submodules/ComposePollUI/BUILD +++ b/submodules/ComposePollUI/BUILD @@ -42,7 +42,6 @@ swift_library( "//submodules/ChatPresentationInterfaceState", "//submodules/TelegramUI/Components/EmojiSuggestionsComponent", "//submodules/TelegramUI/Components/ListComposePollOptionComponent", - "//submodules/TelegramUI/Components/EdgeEffect", "//submodules/TelegramUI/Components/GlassBarButtonComponent", ], visibility = [ diff --git a/submodules/ComposePollUI/Sources/ComposePollScreen.swift b/submodules/ComposePollUI/Sources/ComposePollScreen.swift index c00d4021..32b9cdde 100644 --- a/submodules/ComposePollUI/Sources/ComposePollScreen.swift +++ b/submodules/ComposePollUI/Sources/ComposePollScreen.swift @@ -27,7 +27,6 @@ import EmojiSuggestionsComponent import TextFormat import TextFieldComponent import ListComposePollOptionComponent -import EdgeEffect import GlassBarButtonComponent public final class ComposedPoll { @@ -76,6 +75,7 @@ final class ComposePollScreenComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment let context: AccountContext + let overNavigationContainer: UIView let peer: EnginePeer let isQuiz: Bool? let initialData: ComposePollScreen.InitialData @@ -83,12 +83,14 @@ final class ComposePollScreenComponent: Component { init( context: AccountContext, + overNavigationContainer: UIView, peer: EnginePeer, isQuiz: Bool?, initialData: ComposePollScreen.InitialData, completion: @escaping (ComposedPoll) -> Void ) { self.context = context + self.overNavigationContainer = overNavigationContainer self.peer = peer self.isQuiz = isQuiz self.initialData = initialData @@ -112,7 +114,6 @@ final class ComposePollScreenComponent: Component { final class View: UIView, UIScrollViewDelegate { private let scrollView: UIScrollView - private let edgeEffectView: EdgeEffectView private var reactionInput: ComponentView? private let pollTextSection = ComponentView() @@ -184,8 +185,6 @@ final class ComposePollScreenComponent: Component { self.scrollView.contentInsetAdjustmentBehavior = .never self.scrollView.alwaysBounceVertical = true - self.edgeEffectView = EdgeEffectView() - self.pollOptionsSectionContainer = ListSectionContentView(frame: CGRect()) super.init(frame: frame) @@ -193,8 +192,6 @@ final class ComposePollScreenComponent: Component { self.scrollView.delegate = self self.addSubview(self.scrollView) - self.addSubview(self.edgeEffectView) - let reorderRecognizer = ReorderGestureRecognizer( shouldBegin: { [weak self] point in guard let self, let (id, item) = self.item(at: point) else { @@ -550,7 +547,6 @@ final class ComposePollScreenComponent: Component { pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, - importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, @@ -1624,11 +1620,6 @@ final class ComposePollScreenComponent: Component { self.scrollView.verticalScrollIndicatorInsets = scrollInsets } - let edgeEffectHeight: CGFloat = 80.0 - let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: edgeEffectHeight)) - transition.setFrame(view: self.edgeEffectView, frame: edgeEffectFrame) - self.edgeEffectView.update(content: theme.list.blocksBackgroundColor, blur: true, alpha: 1.0, rect: edgeEffectFrame, edge: .top, edgeSize: edgeEffectFrame.height, transition: transition) - let title = self.isQuiz ? environment.strings.CreatePoll_QuizTitle : environment.strings.CreatePoll_Title let titleSize = self.title.update( transition: .immediate, @@ -1649,23 +1640,23 @@ final class ComposePollScreenComponent: Component { let titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - titleSize.width) / 2.0), y: floorToScreenPixels((environment.navigationHeight - titleSize.height) / 2.0) + 3.0), size: titleSize) if let titleView = self.title.view { if titleView.superview == nil { - self.addSubview(titleView) + component.overNavigationContainer.addSubview(titleView) } transition.setFrame(view: titleView, frame: titleFrame) } - let barButtonSize = CGSize(width: 40.0, height: 40.0) + let barButtonSize = CGSize(width: 44.0, height: 44.0) let cancelButtonSize = self.cancelButton.update( transition: transition, component: AnyComponent(GlassBarButtonComponent( size: barButtonSize, - backgroundColor: environment.theme.rootController.navigationBar.glassBarButtonBackgroundColor, + backgroundColor: nil, isDark: environment.theme.overallDarkAppearance, - state: .generic, + state: .glass, component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: environment.theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: environment.theme.chat.inputPanel.panelControlColor ) )), action: { [weak self] _ in @@ -1681,7 +1672,7 @@ final class ComposePollScreenComponent: Component { let cancelButtonFrame = CGRect(origin: CGPoint(x: environment.safeInsets.left + 16.0, y: 16.0), size: cancelButtonSize) if let cancelButtonView = self.cancelButton.view { if cancelButtonView.superview == nil { - self.addSubview(cancelButtonView) + component.overNavigationContainer.addSubview(cancelButtonView) } transition.setFrame(view: cancelButtonView, frame: cancelButtonFrame) } @@ -1717,7 +1708,7 @@ final class ComposePollScreenComponent: Component { let doneButtonFrame = CGRect(origin: CGPoint(x: availableSize.width - environment.safeInsets.right - 16.0 - doneButtonSize.width, y: 16.0), size: doneButtonSize) if let doneButtonView = self.doneButton.view { if doneButtonView.superview == nil { - self.addSubview(doneButtonView) + component.overNavigationContainer.addSubview(doneButtonView) } transition.setFrame(view: doneButtonView, frame: doneButtonFrame) } @@ -1798,6 +1789,8 @@ public class ComposePollScreen: ViewControllerComponentContainer, AttachmentCont fileprivate let completion: (ComposedPoll) -> Void private var isDismissed: Bool = false + private let overNavigationContainer: UIView + fileprivate private(set) var sendButtonItem: UIBarButtonItem? public var isMinimized: Bool = false @@ -1842,13 +1835,16 @@ public class ComposePollScreen: ViewControllerComponentContainer, AttachmentCont self.context = context self.completion = completion + self.overNavigationContainer = SparseContainerView() + super.init(context: context, component: ComposePollScreenComponent( context: context, + overNavigationContainer: self.overNavigationContainer, peer: peer, isQuiz: isQuiz, initialData: initialData, completion: completion - ), navigationBarAppearance: .transparent, theme: .default) + ), navigationBarAppearance: .default, theme: .default) self._hasGlassStyle = true @@ -1883,6 +1879,10 @@ public class ComposePollScreen: ViewControllerComponentContainer, AttachmentCont return componentView.attemptNavigation(complete: complete) } + + if let navigationBar = self.navigationBar { + navigationBar.customOverBackgroundContentView.insertSubview(self.overNavigationContainer, at: 0) + } } required public init(coder aDecoder: NSCoder) { diff --git a/submodules/ContactListUI/BUILD b/submodules/ContactListUI/BUILD index 99168662..67211ef0 100644 --- a/submodules/ContactListUI/BUILD +++ b/submodules/ContactListUI/BUILD @@ -22,7 +22,6 @@ swift_library( "//submodules/ChatListSearchItemHeader:ChatListSearchItemHeader", "//submodules/ItemListPeerItem:ItemListPeerItem", "//submodules/ContactsPeerItem:ContactsPeerItem", - "//submodules/ChatListSearchItemNode:ChatListSearchItemNode", "//submodules/TelegramPermissionsUI:TelegramPermissionsUI", "//submodules/TelegramNotices:TelegramNotices", "//submodules/AlertUI:AlertUI", @@ -48,6 +47,7 @@ swift_library( "//submodules/ContextUI", "//submodules/TelegramUI/Components/EdgeEffect", "//submodules/TelegramUI/Components/SearchInputPanelComponent", + "//submodules/SearchBarNode", ], visibility = [ "//visibility:public", diff --git a/submodules/ContactListUI/Sources/ContactAddItem.swift b/submodules/ContactListUI/Sources/ContactAddItem.swift index 22d6856c..550001c4 100644 --- a/submodules/ContactListUI/Sources/ContactAddItem.swift +++ b/submodules/ContactListUI/Sources/ContactAddItem.swift @@ -129,7 +129,7 @@ class ContactsAddItemNode: ListViewItemNode { self.iconNode = ASImageNode() self.titleNode = TextNode() - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.backgroundNode) self.addSubnode(self.separatorNode) diff --git a/submodules/ContactListUI/Sources/ContactListActionItem.swift b/submodules/ContactListUI/Sources/ContactListActionItem.swift index b6dcf22b..3b25b455 100644 --- a/submodules/ContactListUI/Sources/ContactListActionItem.swift +++ b/submodules/ContactListUI/Sources/ContactListActionItem.swift @@ -181,7 +181,7 @@ class ContactListActionItemNode: ListViewItemNode { self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.iconNode) self.addSubnode(self.titleNode) diff --git a/submodules/ContactListUI/Sources/ContactListNameIndexHeader.swift b/submodules/ContactListUI/Sources/ContactListNameIndexHeader.swift index 9fd3a49d..a693ff97 100644 --- a/submodules/ContactListUI/Sources/ContactListNameIndexHeader.swift +++ b/submodules/ContactListUI/Sources/ContactListNameIndexHeader.swift @@ -11,6 +11,7 @@ final class ContactListNameIndexHeader: Equatable, ListViewItemHeader { let letter: unichar let stickDirection: ListViewItemHeaderStickDirection = .top public let stickOverInsets: Bool = true + public let isSticky: Bool = false let height: CGFloat = 29.0 diff --git a/submodules/ContactListUI/Sources/ContactListNode.swift b/submodules/ContactListUI/Sources/ContactListNode.swift index 9dc1ed23..ed179a82 100644 --- a/submodules/ContactListUI/Sources/ContactListNode.swift +++ b/submodules/ContactListUI/Sources/ContactListNode.swift @@ -16,7 +16,6 @@ import AccountContext import TelegramPermissions import TelegramNotices import ContactsPeerItem -import ChatListSearchItemNode import ChatListSearchItemHeader import SearchUI import TelegramPermissionsUI @@ -96,7 +95,6 @@ private enum ContactListNodeEntry: Comparable, Identifiable { var hasUnseenCloseFriends: Bool } - case search(PresentationTheme, PresentationStrings) case sort(PresentationTheme, PresentationStrings, ContactsSortOrder) case permissionInfo(PresentationTheme, String, String, Bool) case permissionEnable(PresentationTheme, String) @@ -107,8 +105,6 @@ private enum ContactListNodeEntry: Comparable, Identifiable { var stableId: ContactListNodeEntryId { switch self { - case .search: - return .search case .sort: return .sort case .permissionInfo: @@ -133,10 +129,6 @@ private enum ContactListNodeEntry: Comparable, Identifiable { func item(context: AccountContext, presentationData: PresentationData, interaction: ContactListNodeInteraction, isSearch: Bool, listStyle: ItemListStyle) -> ListViewItem { switch self { - case let .search(theme, strings): - return ChatListSearchItem(theme: theme, placeholder: strings.Contacts_SearchLabel, activate: { - interaction.activateSearch() - }) case let .sort(_, strings, sortOrder): var text = strings.Contacts_SortedByName if case .presence = sortOrder { @@ -270,12 +262,6 @@ private enum ContactListNodeEntry: Comparable, Identifiable { static func ==(lhs: ContactListNodeEntry, rhs: ContactListNodeEntry) -> Bool { switch lhs { - case let .search(lhsTheme, lhsStrings): - if case let .search(rhsTheme, rhsStrings) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings { - return true - } else { - return false - } case let .sort(lhsTheme, lhsStrings, lhsSortOrder): if case let .sort(rhsTheme, rhsStrings, rhsSortOrder) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsSortOrder == rhsSortOrder { return true @@ -376,39 +362,35 @@ private enum ContactListNodeEntry: Comparable, Identifiable { static func <(lhs: ContactListNodeEntry, rhs: ContactListNodeEntry) -> Bool { switch lhs { - case .search: - return true case .sort: switch rhs { - case .search: - return false default: return true } case .permissionInfo: switch rhs { - case .search, .sort: + case .sort: return false default: return true } case .permissionEnable: switch rhs { - case .search, .sort, .permissionInfo: + case .sort, .permissionInfo: return false default: return true } case .permissionLimited: switch rhs { - case .search, .sort, .permissionInfo, .permissionEnable: + case .sort, .permissionInfo, .permissionEnable: return false default: return true } case let .option(lhsIndex, _, _, _, _): switch rhs { - case .search, .sort, .permissionInfo, .permissionEnable, .permissionLimited: + case .sort, .permissionInfo, .permissionEnable, .permissionLimited: return false case let .option(rhsIndex, _, _, _, _): return lhsIndex < rhsIndex @@ -417,14 +399,14 @@ private enum ContactListNodeEntry: Comparable, Identifiable { } case let .header(lhsIndex, _): switch rhs { - case .search, .sort, .permissionInfo, .permissionEnable, .permissionLimited, .option: + case .sort, .permissionInfo, .permissionEnable, .permissionLimited, .option: return false case let .header(rhsIndex, _), let .peer(rhsIndex, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): return lhsIndex < rhsIndex } case let .peer(lhsIndex, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): switch rhs { - case .search, .sort, .permissionInfo, .permissionEnable, .permissionLimited, .option: + case .sort, .permissionInfo, .permissionEnable, .permissionLimited, .option: return false case let .peer(rhsIndex, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _), let .header(rhsIndex, _): // if (lhsStoryData == nil) != (rhsStoryData == nil) { @@ -901,9 +883,6 @@ private func preparedContactListNodeTransition(context: AccountContext, presenta switch entry { case .sort: shouldFixScroll = true - case .search: - //indexSections.apend(CollectionIndexNode.searchIndex) - break case let .peer(_, _, _, header, _, _, _, _, _, _, _, _, _, _, _, _): if let header = header as? ContactListNameIndexHeader { if !existingSections.contains(header.letter) { @@ -1255,7 +1234,6 @@ public final class ContactListNode: ASDisplayNode { self.presentationData = presentationData self.listNode = ListView() - self.listNode.dynamicBounceEnabled = false self.listNode.accessibilityPageScrolledString = { row, count in return presentationData.strings.VoiceOver_ScrollStatus(row, count).string } @@ -1419,11 +1397,6 @@ public final class ContactListNode: ASDisplayNode { var peerIndex = 0 loop: for entry in entries { switch entry { - case .search: - if section == CollectionIndexNode.searchIndex { - strongSelf.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.PreferSynchronousDrawing, .PreferSynchronousResourceLoading], scrollToItem: ListViewScrollToItem(index: index, position: .top(-navigationBarSearchContentHeight), animated: false, curve: .Default(duration: nil), directionHint: .Down), additionalScrollDistance: 0.0, updateSizeAndInsets: updateSizeAndInsets, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) - break loop - } case let .peer(_, _, _, header, _, _, _, _, _, _, _, _, _, _, _, _): if let header = header as? ContactListNameIndexHeader { if let scalar = UnicodeScalar(header.letter) { @@ -2124,8 +2097,6 @@ public final class ContactListNode: ASDisplayNode { strongSelf.authorizationNode.isHidden = authorizationPreviousHidden strongSelf.addSubnode(strongSelf.authorizationNode) - strongSelf.listNode.dynamicBounceEnabled = false - strongSelf.listNode.forEachAccessoryItemNode({ accessoryItemNode in if let accessoryItemNode = accessoryItemNode as? ContactsSectionHeaderAccessoryItemNode { accessoryItemNode.updateTheme(theme: presentationData.theme) diff --git a/submodules/ContactListUI/Sources/ContactsController.swift b/submodules/ContactListUI/Sources/ContactsController.swift index 425ca2b7..293c10db 100644 --- a/submodules/ContactListUI/Sources/ContactsController.swift +++ b/submodules/ContactListUI/Sources/ContactsController.swift @@ -24,6 +24,7 @@ import ChatListHeaderComponent import TelegramIntents import UndoUI import ShareController +import SearchBarNode private final class HeaderContextReferenceContentSource: ContextReferenceContentSource { private let controller: ViewController @@ -141,7 +142,6 @@ public class ContactsController: ViewController { self.sortButton = SortHeaderButton(presentationData: self.presentationData) - //super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData)) super.init(navigationBarPresentationData: nil) self.tabBarItemContextActionType = .always @@ -226,6 +226,8 @@ public class ContactsController: ViewController { } self.sortButton.addTarget(self, action: #selector(self.sortPressed), forControlEvents: .touchUpInside) + + self.updateTabBarSearchState(ViewController.TabBarSearchState(isActive: false), transition: .immediate) } required public init(coder aDecoder: NSCoder) { @@ -242,7 +244,6 @@ public class ContactsController: ViewController { private func updateThemeAndStrings() { self.sortButton.update(theme: self.presentationData.theme, strings: self.presentationData.strings) self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) self.title = self.presentationData.strings.Contacts_Title self.tabBarItem.title = self.presentationData.strings.Contacts_Title @@ -280,8 +281,6 @@ public class ContactsController: ViewController { |> take(1) |> map { _ -> Bool in true }) - self.contactsNode.navigationBar = self.navigationBar - let openPeer: (ContactListPeer, Bool) -> Void = { [weak self] peer, fromSearch in if let strongSelf = self { switch peer { @@ -343,7 +342,7 @@ public class ContactsController: ViewController { } self.contactsNode.contactListNode.activateSearch = { [weak self] in - self?.activateSearch() + self?.activateSearch(isFromTabBar: false) } self.contactsNode.contactListNode.openPeer = { [weak self] peer, _, _, _ in @@ -580,18 +579,26 @@ public class ContactsController: ViewController { self.sortButton.contextAction?(self.sortButton.containerNode, nil) } - private func activateSearch() { - if let searchContentNode = self.searchContentNode() { - self.contactsNode.activateSearch(placeholderNode: searchContentNode.placeholderNode) + private func activateSearch(isFromTabBar: Bool) { + let placeholderNode = isFromTabBar ? nil : self.searchContentNode()?.placeholderNode + self.contactsNode.activateSearch(placeholderNode: placeholderNode) + if placeholderNode != nil { + (self.parent as? TabBarController)?.updateIsTabBarHidden(true, transition: .animated(duration: 0.5, curve: .spring)) + } else { + self.updateTabBarSearchState(ViewController.TabBarSearchState(isActive: true), transition: .animated(duration: 0.5, curve: .spring)) + if let searchBarNode = self.currentTabBarSearchNode?() as? SearchBarNode { + self.contactsNode.searchDisplayController?.setSearchBar(searchBarNode) + searchBarNode.activate() + } } self.requestLayout(transition: .animated(duration: 0.5, curve: .spring)) } private func deactivateSearch(animated: Bool) { - if let searchContentNode = self.searchContentNode() { - self.contactsNode.deactivateSearch(placeholderNode: searchContentNode.placeholderNode, animated: animated) - self.requestLayout(transition: .animated(duration: 0.5, curve: .spring)) - } + self.contactsNode.deactivateSearch(placeholderNode: self.searchContentNode()?.placeholderNode, animated: animated) + self.updateTabBarSearchState(ViewController.TabBarSearchState(isActive: false), transition: .animated(duration: 0.5, curve: .spring)) + (self.parent as? TabBarController)?.updateIsTabBarHidden(false, transition: .animated(duration: 0.5, curve: .spring)) + self.requestLayout(transition: .animated(duration: 0.5, curve: .spring)) } func presentSortMenu(sourceView: UIView, gesture: ContextGesture?) { @@ -796,6 +803,14 @@ public class ContactsController: ViewController { let controller = ContextController(presentationData: self.presentationData, source: .reference(ContactsTabBarContextReferenceContentSource(controller: self, sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture) self.context.sharedContext.mainWindow?.presentInGlobalOverlay(controller) } + + override public func tabBarActivateSearch() { + self.activateSearch(isFromTabBar: true) + } + + override public func tabBarDeactivateSearch() { + self.deactivateSearch(animated: true) + } } private final class ContactsTabBarContextReferenceContentSource: ContextReferenceContentSource { diff --git a/submodules/ContactListUI/Sources/ContactsControllerNode.swift b/submodules/ContactListUI/Sources/ContactsControllerNode.swift index 2718f81f..cb9b316c 100644 --- a/submodules/ContactListUI/Sources/ContactsControllerNode.swift +++ b/submodules/ContactListUI/Sources/ContactsControllerNode.swift @@ -53,12 +53,11 @@ final class ContactsControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { private let context: AccountContext private(set) var searchDisplayController: SearchDisplayController? - private var isSearchDisplayControllerActive: Bool = false + private var isSearchDisplayControllerActive: ChatListNavigationBar.ActiveSearch? private var storiesUnlocked: Bool = false private var containerLayout: (ContainerViewLayout, CGFloat)? - var navigationBar: NavigationBar? let navigationBarView = ComponentView() var requestDeactivateSearch: (() -> Void)? @@ -350,7 +349,6 @@ final class ContactsControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { chatListTitle: NetworkStatusTitle(text: title, activity: false, hasProxy: false, connectsViaProxy: false, isPasscodeSet: false, isManuallyLocked: false, peerStatus: nil), leftButton: leftButton, rightButtons: rightButtons, - backTitle: nil, backPressed: nil ) @@ -362,14 +360,15 @@ final class ContactsControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { strings: self.presentationData.strings, statusBarHeight: layout.statusBarHeight ?? 0.0, sideInset: layout.safeInsets.left, - isSearchActive: self.isSearchDisplayControllerActive, - isSearchEnabled: true, + search: ChatListNavigationBar.Search(isEnabled: true), + activeSearch: self.isSearchDisplayControllerActive, primaryContent: primaryContent, secondaryContent: nil, secondaryTransition: 0.0, storySubscriptions: nil, storiesIncludeHidden: true, uploadProgress: [:], + headerPanels: nil, tabsNode: tabsNode, tabsNodeIsSearch: tabsNodeIsSearch, accessoryPanelContainer: nil, @@ -416,7 +415,7 @@ final class ContactsControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { private func updateNavigationScrolling(transition: ContainedViewLayoutTransition) { var offset = self.getEffectiveNavigationScrollingOffset() - if self.isSearchDisplayControllerActive { + if self.isSearchDisplayControllerActive != nil { offset = 0.0 } @@ -478,15 +477,15 @@ final class ContactsControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { } } - func activateSearch(placeholderNode: SearchBarPlaceholderNode) { + func activateSearch(placeholderNode: SearchBarPlaceholderNode?) { guard let (containerLayout, navigationBarHeight) = self.containerLayout, self.searchDisplayController == nil else { return } - self.isSearchDisplayControllerActive = true + self.isSearchDisplayControllerActive = ChatListNavigationBar.ActiveSearch(isExternal: placeholderNode == nil) self.storiesUnlocked = false - self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, mode: .list, contentNode: ContactsSearchContainerNode(context: self.context, onlyWriteable: false, categories: [.cloudContacts, .global, .deviceContacts], addContact: { [weak self] phoneNumber in + self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, mode: .navigation, contentNode: ContactsSearchContainerNode(context: self.context, glass: true, externalSearchBar: true, onlyWriteable: false, categories: [.cloudContacts, .global, .deviceContacts], addContact: { [weak self] phoneNumber in if let requestAddContact = self?.requestAddContact { requestAddContact(phoneNumber) } @@ -504,14 +503,14 @@ final class ContactsControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { if let requestDeactivateSearch = self?.requestDeactivateSearch { requestDeactivateSearch() } - }) + }, fieldStyle: placeholderNode?.fieldStyle ?? .modern, searchBarIsExternal: placeholderNode == nil) self.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: navigationBarHeight, transition: .immediate) self.searchDisplayController?.activate(insertSubnode: { [weak self] subnode, isSearchBar in if let strongSelf = self { if isSearchBar { if let navigationBarComponentView = strongSelf.navigationBarView.view as? ChatListNavigationBar.View { - navigationBarComponentView.addSubnode(subnode) + navigationBarComponentView.searchContentNode?.addSubnode(subnode) } } else { strongSelf.insertSubnode(subnode, aboveSubnode: strongSelf.contactListNode) @@ -520,16 +519,11 @@ final class ContactsControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { }, placeholder: placeholderNode) } - func deactivateSearch(placeholderNode: SearchBarPlaceholderNode, animated: Bool) { - self.isSearchDisplayControllerActive = false + func deactivateSearch(placeholderNode: SearchBarPlaceholderNode?, animated: Bool) { + self.isSearchDisplayControllerActive = nil if let searchDisplayController = self.searchDisplayController { - let previousFrame = placeholderNode.frame - placeholderNode.frame = previousFrame.offsetBy(dx: 0.0, dy: 54.0) - searchDisplayController.deactivate(placeholder: placeholderNode, animated: animated) self.searchDisplayController = nil - - placeholderNode.frame = previousFrame } } } diff --git a/submodules/ContactListUI/Sources/ContactsSearchContainerNode.swift b/submodules/ContactListUI/Sources/ContactsSearchContainerNode.swift index 67f534a4..c033bca5 100644 --- a/submodules/ContactListUI/Sources/ContactsSearchContainerNode.swift +++ b/submodules/ContactListUI/Sources/ContactsSearchContainerNode.swift @@ -232,6 +232,7 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo private let context: AccountContext private let glass: Bool + private let externalSearchBar: Bool private let isPeerEnabled: (ContactListPeer) -> Bool private let addContact: ((String) -> Void)? private let openPeer: (ContactListPeer, ContactsSearchContainerNode.OpenPeerAction) -> Void @@ -265,6 +266,7 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo public init( context: AccountContext, glass: Bool = false, + externalSearchBar: Bool = false, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, onlyWriteable: Bool, categories: ContactsSearchCategories, @@ -278,6 +280,7 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo ) { self.context = context self.glass = glass + self.externalSearchBar = externalSearchBar self.isPeerEnabled = isPeerEnabled self.addContact = addContact self.openPeer = openPeer @@ -290,7 +293,7 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo self.themeAndStringsPromise = Promise((self.presentationData.theme, self.presentationData.strings)) self.dimNode = ASDisplayNode() - self.dimNode.backgroundColor = glass ? .clear : UIColor.black.withAlphaComponent(0.5) + self.dimNode.backgroundColor = .clear self.backgroundNode = ASDisplayNode() self.backgroundNode.backgroundColor = self.presentationData.theme.list.plainBackgroundColor @@ -686,12 +689,26 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo self.containerViewLayout = (layout, navigationBarHeight) let topInset = navigationBarHeight - transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset), size: CGSize(width: layout.size.width, height: layout.size.height - topInset))) + transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height))) self.backgroundNode.frame = CGRect(origin: .zero, size: CGSize(width: layout.size.width, height: navigationBarHeight)) - self.listNode.frame = CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight), size: CGSize(width: layout.size.width, height: layout.size.height - topInset)) - self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: layout.size, insets: UIEdgeInsets(top: 0.0, left: layout.safeInsets.left, bottom: layout.intrinsicInsets.bottom, right: layout.safeInsets.right), duration: 0.0, curve: .Default(duration: nil)), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) + self.listNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height)) + let listDuration: Double + let listCurve: ListViewAnimationCurve + switch transition { + case .immediate: + listDuration = 0.0 + listCurve = .Default(duration: nil) + case let .animated(duration, curve): + listDuration = duration + if case .spring = curve { + listCurve = .Spring(duration: duration) + } else { + listCurve = .Default(duration: nil) + } + } + self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: layout.size, insets: UIEdgeInsets(top: topInset, left: layout.safeInsets.left, bottom: layout.intrinsicInsets.bottom, right: layout.safeInsets.right), duration: listDuration, curve: listCurve), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) let size = layout.size let sideInset = layout.safeInsets.left @@ -714,7 +731,7 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo textTransition.updateFrame(node: self.emptyResultsTextNode, frame: CGRect(origin: CGPoint(x: sideInset + padding + (size.width - sideInset * 2.0 - padding * 2.0 - emptyTextSize.width) / 2.0, y: emptyAnimationY + emptyAnimationHeight + emptyAnimationSpacing + emptyTitleSize.height + emptyTextSpacing), size: emptyTextSize)) self.emptyResultsAnimationNode.updateLayout(size: self.emptyResultsAnimationSize) - if self.glass { + if self.glass && !self.externalSearchBar { let searchInputSize = self.searchInput.update( transition: .immediate, component: AnyComponent( @@ -794,14 +811,14 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate) } - let containerTransition = ContainedViewLayoutTransition.animated(duration: 0.3, curve: .easeInOut) - containerTransition.updateAlpha(node: strongSelf.listNode, alpha: isSearching ? 1.0 : 0.0) - containerTransition.updateAlpha(node: strongSelf.backgroundNode, alpha: isSearching ? 1.0 : 0.0) + //let containerTransition = ContainedViewLayoutTransition.animated(duration: 0.3, curve: .easeInOut) + ContainedViewLayoutTransition.immediate.updateAlpha(node: strongSelf.listNode, alpha: isSearching ? 1.0 : 0.0) + ContainedViewLayoutTransition.immediate.updateAlpha(node: strongSelf.backgroundNode, alpha: isSearching ? 1.0 : 0.0) strongSelf.dimNode.isHidden = isSearching - containerTransition.updateAlpha(node: strongSelf.emptyResultsAnimationNode, alpha: emptyResults ? 1.0 : 0.0) - containerTransition.updateAlpha(node: strongSelf.emptyResultsTitleNode, alpha: emptyResults ? 1.0 : 0.0) - containerTransition.updateAlpha(node: strongSelf.emptyResultsTextNode, alpha: emptyResults ? 1.0 : 0.0) + ContainedViewLayoutTransition.immediate.updateAlpha(node: strongSelf.emptyResultsAnimationNode, alpha: emptyResults ? 1.0 : 0.0) + ContainedViewLayoutTransition.immediate.updateAlpha(node: strongSelf.emptyResultsTitleNode, alpha: emptyResults ? 1.0 : 0.0) + ContainedViewLayoutTransition.immediate.updateAlpha(node: strongSelf.emptyResultsTextNode, alpha: emptyResults ? 1.0 : 0.0) strongSelf.emptyResultsAnimationNode.visibility = emptyResults }) } diff --git a/submodules/ContactListUI/Sources/InviteContactsController.swift b/submodules/ContactListUI/Sources/InviteContactsController.swift index 00c58c2d..5d42a4a0 100644 --- a/submodules/ContactListUI/Sources/InviteContactsController.swift +++ b/submodules/ContactListUI/Sources/InviteContactsController.swift @@ -36,8 +36,9 @@ public class InviteContactsController: ViewController, MFMessageComposeViewContr self.presentationData = context.sharedContext.currentPresentationData.with { $0 } - super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData)) + super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData, style: .glass)) + self._hasGlassStyle = true self.navigationPresentation = .modal self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style @@ -86,7 +87,7 @@ public class InviteContactsController: ViewController, MFMessageComposeViewContr private func updateThemeAndStrings() { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData, style: .glass), transition: .immediate) self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search) self.title = self.presentationData.strings.Contacts_InviteFriends self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) diff --git a/submodules/ContactListUI/Sources/InviteContactsControllerNode.swift b/submodules/ContactListUI/Sources/InviteContactsControllerNode.swift index 57124b8f..c75a190a 100644 --- a/submodules/ContactListUI/Sources/InviteContactsControllerNode.swift +++ b/submodules/ContactListUI/Sources/InviteContactsControllerNode.swift @@ -518,7 +518,7 @@ final class InviteContactsControllerNode: ASDisplayNode { if let requestDeactivateSearch = self?.requestDeactivateSearch { requestDeactivateSearch() } - }) + }, fieldStyle: placeholderNode.fieldStyle) self.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: navigationBarHeight, transition: .immediate) self.searchDisplayController?.activate(insertSubnode: { [weak self, weak placeholderNode] subnode, isSearchBar in diff --git a/submodules/ContactListUI/Sources/LimitedPermissionItem.swift b/submodules/ContactListUI/Sources/LimitedPermissionItem.swift index c9923da2..0af140e6 100644 --- a/submodules/ContactListUI/Sources/LimitedPermissionItem.swift +++ b/submodules/ContactListUI/Sources/LimitedPermissionItem.swift @@ -103,7 +103,7 @@ public class LimitedPermissionItemNode: ListViewItemNode { self.actionButtonTitleNode = TextNode() self.actionButtonTitleNode.isUserInteractionEnabled = false - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.textNode) self.addSubnode(self.activateArea) diff --git a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift index 3e446f63..6b9dded5 100644 --- a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift +++ b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift @@ -199,6 +199,7 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader { let searchQuery: String? let isAd: Bool let alwaysShowLastSeparator: Bool + let hideBackground: Bool let action: ((ContactsPeerItemPeer) -> Void)? let disabledAction: ((ContactsPeerItemPeer) -> Void)? let setPeerIdWithRevealedOptions: ((EnginePeer.Id?, EnginePeer.Id?) -> Void)? @@ -247,6 +248,7 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader { searchQuery: String? = nil, isAd: Bool = false, alwaysShowLastSeparator: Bool = false, + hideBackground: Bool = false, action: ((ContactsPeerItemPeer) -> Void)?, disabledAction: ((ContactsPeerItemPeer) -> Void)? = nil, setPeerIdWithRevealedOptions: ((EnginePeer.Id?, EnginePeer.Id?) -> Void)? = nil, @@ -285,6 +287,7 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader { self.searchQuery = searchQuery self.isAd = isAd self.alwaysShowLastSeparator = alwaysShowLastSeparator + self.hideBackground = hideBackground self.action = action self.disabledAction = disabledAction self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions @@ -584,7 +587,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { self.titleNode = TextNode() self.statusNode = TextNodeWithEntities() - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.isAccessibilityElement = true @@ -1325,9 +1328,13 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { case .plain: strongSelf.topSeparatorNode.backgroundColor = item.presentationData.theme.list.itemPlainSeparatorColor strongSelf.separatorNode.backgroundColor = item.presentationData.theme.list.itemPlainSeparatorColor - strongSelf.backgroundNode.backgroundColor = item.presentationData.theme.list.plainBackgroundColor + if !item.hideBackground { + strongSelf.backgroundNode.backgroundColor = item.presentationData.theme.list.plainBackgroundColor + } case .blocks: - strongSelf.topSeparatorNode.backgroundColor = item.presentationData.theme.list.itemBlocksSeparatorColor + if !item.hideBackground { + strongSelf.topSeparatorNode.backgroundColor = item.presentationData.theme.list.itemBlocksSeparatorColor + } strongSelf.separatorNode.backgroundColor = item.presentationData.theme.list.itemBlocksSeparatorColor strongSelf.backgroundNode.backgroundColor = item.presentationData.theme.list.itemBlocksBackgroundColor } @@ -1857,7 +1864,8 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners, glass: item.systemStyle == .glass) : nil - let topHighlightInset: CGFloat = (first || !nodeLayout.insets.top.isZero) ? 0.0 : separatorHeight + var topHighlightInset: CGFloat = (first || !nodeLayout.insets.top.isZero) ? 0.0 : separatorHeight + topHighlightInset -= nodeLayout.insets.top strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: nodeLayout.contentSize.width, height: nodeLayout.contentSize.height)) strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -nodeLayout.insets.top - topHighlightInset), size: CGSize(width: nodeLayout.size.width, height: nodeLayout.size.height + topHighlightInset)) diff --git a/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift b/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift index dbf82367..2bb3470f 100644 --- a/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift +++ b/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift @@ -346,6 +346,7 @@ public final class ContextControllerActionsListActionItemNode: HighlightTracking } let titleColor: UIColor + let linkColor = presentationData.theme.list.itemAccentColor switch self.item.textColor { case .primary: titleColor = presentationData.theme.contextMenu.primaryColor @@ -365,6 +366,8 @@ public final class ContextControllerActionsListActionItemNode: HighlightTracking return ChatTextInputStateTextAttribute(type: .bold, range: entity.range) } else if case .Italic = entity.type { return ChatTextInputStateTextAttribute(type: .italic, range: entity.range) + } else if case .Url = entity.type { + return ChatTextInputStateTextAttribute(type: .textUrl(""), range: entity.range) } return nil }) @@ -375,9 +378,12 @@ public final class ContextControllerActionsListActionItemNode: HighlightTracking ], range: NSRange(location: 0, length: result.length)) for attribute in inputStateText.attributes { if case .bold = attribute.type { - result.addAttribute(NSAttributedString.Key.font, value: Font.semibold(presentationData.listsFontSize.baseDisplaySize), range: NSRange(location: attribute.range.lowerBound, length: attribute.range.count)) + result.addAttribute(NSAttributedString.Key.font, value: titleBoldFont, range: NSRange(location: attribute.range.lowerBound, length: attribute.range.count)) } else if case .italic = attribute.type { result.addAttribute(NSAttributedString.Key.font, value: Font.semibold(15.0), range: NSRange(location: attribute.range.lowerBound, length: attribute.range.count)) + } else if case .textUrl = attribute.type { + result.addAttribute(NSAttributedString.Key.foregroundColor, value: linkColor, range: NSRange(location: attribute.range.lowerBound, length: attribute.range.count)) + result.addAttribute(NSAttributedString.Key.font, value: titleBoldFont, range: NSRange(location: attribute.range.lowerBound, length: attribute.range.count)) } } attributedText = result diff --git a/submodules/CountrySelectionUI/Sources/AuthorizationSequenceCountrySelectionController.swift b/submodules/CountrySelectionUI/Sources/AuthorizationSequenceCountrySelectionController.swift index f2dfd41c..3b3cdc1f 100644 --- a/submodules/CountrySelectionUI/Sources/AuthorizationSequenceCountrySelectionController.swift +++ b/submodules/CountrySelectionUI/Sources/AuthorizationSequenceCountrySelectionController.swift @@ -142,7 +142,7 @@ private final class AuthorizationSequenceCountrySelectionNavigationContentNode: self.cancel = cancel - self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: theme), strings: strings, fieldStyle: .modern) + self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: theme), presentationTheme: theme, strings: strings, fieldStyle: .modern) let placeholderText = strings.Common_Search let searchBarFont = Font.regular(17.0) @@ -169,10 +169,12 @@ private final class AuthorizationSequenceCountrySelectionNavigationContentNode: return 54.0 } - override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { + override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { let searchBarFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - self.nominalHeight), size: CGSize(width: size.width, height: 54.0)) self.searchBar.frame = searchBarFrame self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition) + + return size } func activate() { @@ -332,7 +334,7 @@ public final class AuthorizationSequenceCountrySelectionController: ViewControll self.displayCodes = displayCodes self.glass = glass - super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: theme, hideBackground: glass, hideSeparator: glass), strings: NavigationBarStrings(presentationStrings: strings))) + super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: theme, hideBackground: glass, hideSeparator: glass, style: glass ? .glass : .legacy), strings: NavigationBarStrings(presentationStrings: strings))) self._hasGlassStyle = glass @@ -392,13 +394,13 @@ public final class AuthorizationSequenceCountrySelectionController: ViewControll id: "close", component: AnyComponent(GlassBarButtonComponent( size: barButtonSize, - backgroundColor: self.theme.rootController.navigationBar.glassBarButtonBackgroundColor, + backgroundColor: nil, isDark: self.theme.overallDarkAppearance, - state: .generic, + state: .glass, component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: self.theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: self.theme.chat.inputPanel.panelControlColor ) )), action: { [weak self] _ in @@ -413,13 +415,13 @@ public final class AuthorizationSequenceCountrySelectionController: ViewControll id: "search", component: AnyComponent(GlassBarButtonComponent( size: barButtonSize, - backgroundColor: self.theme.rootController.navigationBar.glassBarButtonBackgroundColor, + backgroundColor: nil, isDark: self.theme.overallDarkAppearance, - state: .generic, + state: .glass, component: AnyComponentWithIdentity(id: "search", component: AnyComponent( BundleIconComponent( name: "Navigation/Search", - tintColor: self.theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: self.theme.chat.inputPanel.panelControlColor ) )), action: { [weak self] _ in @@ -441,15 +443,17 @@ public final class AuthorizationSequenceCountrySelectionController: ViewControll self.closeButtonNode = closeButtonNode self.navigationItem.leftBarButtonItem = UIBarButtonItem(customDisplayNode: closeButtonNode) } - - let searchButtonNode: BarComponentHostNode - if let current = self.searchButtonNode { - searchButtonNode = current - searchButtonNode.component = searchComponent - } else { - searchButtonNode = BarComponentHostNode(component: searchComponent, size: barButtonSize) - self.searchButtonNode = searchButtonNode - self.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: searchButtonNode) + + if !self.glass { + let searchButtonNode: BarComponentHostNode + if let current = self.searchButtonNode { + searchButtonNode = current + searchButtonNode.component = searchComponent + } else { + searchButtonNode = BarComponentHostNode(component: searchComponent, size: barButtonSize) + self.searchButtonNode = searchButtonNode + self.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: searchButtonNode) + } } } diff --git a/submodules/Display/Source/ImmediateTextNode.swift b/submodules/Display/Source/ImmediateTextNode.swift index 8851e6c2..61a6d4e2 100644 --- a/submodules/Display/Source/ImmediateTextNode.swift +++ b/submodules/Display/Source/ImmediateTextNode.swift @@ -13,7 +13,7 @@ public struct ImmediateTextNodeLayoutInfo { } } -public class ImmediateTextNode: TextNode { +open class ImmediateTextNode: TextNode { public var attributedText: NSAttributedString? public var textAlignment: NSTextAlignment = .natural public var verticalAlignment: TextVerticalAlignment = .top @@ -60,7 +60,7 @@ public class ImmediateTextNode: TextNode { public var trailingLineWidth: CGFloat? - var constrainedSize: CGSize? + public var constrainedSize: CGSize? public var highlightAttributeAction: (([NSAttributedString.Key: Any]) -> NSAttributedString.Key?)? { didSet { @@ -94,7 +94,7 @@ public class ImmediateTextNode: TextNode { return node } - public func updateLayout(_ constrainedSize: CGSize) -> CGSize { + open func updateLayout(_ constrainedSize: CGSize) -> CGSize { self.constrainedSize = constrainedSize let makeLayout = TextNode.asyncLayout(self) diff --git a/submodules/Display/Source/KeyShortcutsController.swift b/submodules/Display/Source/KeyShortcutsController.swift index c8156afb..e8afab89 100644 --- a/submodules/Display/Source/KeyShortcutsController.swift +++ b/submodules/Display/Source/KeyShortcutsController.swift @@ -9,11 +9,7 @@ public class KeyShortcutsController: UIResponder { private var viewControllerEnumerator: (@escaping (ContainableController) -> Bool) -> Void public static var isAvailable: Bool { - if #available(iOSApplicationExtension 8.0, iOS 8.0, *), UIDevice.current.userInterfaceIdiom == .pad { - return true - } else { - return false - } + return true } public init(enumerator: @escaping (@escaping (ContainableController) -> Bool) -> Void) { diff --git a/submodules/Display/Source/LinkHighlightingNode.swift b/submodules/Display/Source/LinkHighlightingNode.swift index 22e128e0..99fa88c0 100644 --- a/submodules/Display/Source/LinkHighlightingNode.swift +++ b/submodules/Display/Source/LinkHighlightingNode.swift @@ -30,6 +30,232 @@ private func drawFullCorner(context: CGContext, color: UIColor, at point: CGPoin } } +private func drawRectsImageContent(size: CGSize, context: CGContext, color: UIColor, rects: [CGRect], inset: CGFloat, outerRadius: CGFloat, innerRadius: CGFloat, stroke: Bool, strokeWidth: CGFloat, useModernPathCalculation: Bool, topLeft: CGPoint) { + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(color.cgColor) + + context.setBlendMode(.copy) + + if useModernPathCalculation { + if rects.count == 1 { + let path = UIBezierPath(roundedRect: rects[0].offsetBy(dx: -topLeft.x, dy: -topLeft.y), cornerRadius: outerRadius).cgPath + context.addPath(path) + + if stroke { + context.setStrokeColor(color.cgColor) + context.setLineWidth(strokeWidth) + context.strokePath() + } else { + context.fillPath() + } + return + } + + var combinedRects: [[CGRect]] = [] + var currentRects: [CGRect] = [] + for rect in rects { + if rect.width.isZero { + if !currentRects.isEmpty { + combinedRects.append(currentRects) + } + currentRects.removeAll() + } else { + currentRects.append(rect) + } + } + if !currentRects.isEmpty { + combinedRects.append(currentRects) + } + + for rects in combinedRects { + var rects = rects.map { $0.insetBy(dx: -inset, dy: -inset).offsetBy(dx: -topLeft.x, dy: -topLeft.y) } + + let minRadius: CGFloat = 2.0 + + for _ in 0 ..< rects.count * rects.count { + var hadChanges = false + for i in 0 ..< rects.count - 1 { + if rects[i].maxY > rects[i + 1].minY { + let midY = floor((rects[i].maxY + rects[i + 1].minY) * 0.5) + rects[i].size.height = midY - rects[i].minY + rects[i + 1].origin.y = midY + rects[i + 1].size.height = rects[i + 1].maxY - midY + hadChanges = true + } + if rects[i].maxY >= rects[i + 1].minY && rects[i].insetBy(dx: 0.0, dy: 1.0).intersects(rects[i + 1]) { + if abs(rects[i].minX - rects[i + 1].minX) < minRadius { + let commonMinX = min(rects[i].origin.x, rects[i + 1].origin.x) + if rects[i].origin.x != commonMinX { + rects[i].origin.x = commonMinX + hadChanges = true + } + if rects[i + 1].origin.x != commonMinX { + rects[i + 1].origin.x = commonMinX + hadChanges = true + } + } + if abs(rects[i].maxX - rects[i + 1].maxX) < minRadius { + let commonMaxX = max(rects[i].maxX, rects[i + 1].maxX) + if rects[i].maxX != commonMaxX { + rects[i].size.width = commonMaxX - rects[i].minX + hadChanges = true + } + if rects[i + 1].maxX != commonMaxX { + rects[i + 1].size.width = commonMaxX - rects[i + 1].minX + hadChanges = true + } + } + } + } + if !hadChanges { + break + } + } + + context.move(to: CGPoint(x: rects[0].midX, y: rects[0].minY)) + context.addLine(to: CGPoint(x: rects[0].maxX - outerRadius, y: rects[0].minY)) + context.addArc(tangent1End: rects[0].topRight, tangent2End: CGPoint(x: rects[0].maxX, y: rects[0].minY + outerRadius), radius: outerRadius) + context.addLine(to: CGPoint(x: rects[0].maxX, y: rects[0].midY)) + + for i in 0 ..< rects.count - 1 { + let rect = rects[i] + let next = rects[i + 1] + + if rect.maxX == next.maxX { + context.addLine(to: CGPoint(x: next.maxX, y: next.midY)) + } else { + let nextRadius = min(outerRadius, floor(abs(rect.maxX - next.maxX) * 0.5)) + context.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY - nextRadius)) + if next.maxX > rect.maxX { + context.addArc(tangent1End: CGPoint(x: rect.maxX, y: rect.maxY), tangent2End: CGPoint(x: rect.maxX + nextRadius, y: rect.maxY), radius: nextRadius) + context.addLine(to: CGPoint(x: next.maxX - nextRadius, y: next.minY)) + } else { + context.addArc(tangent1End: CGPoint(x: rect.maxX, y: rect.maxY), tangent2End: CGPoint(x: rect.maxX - nextRadius, y: rect.maxY), radius: nextRadius) + context.addLine(to: CGPoint(x: next.maxX + nextRadius, y: next.minY)) + } + context.addArc(tangent1End: next.topRight, tangent2End: CGPoint(x: next.maxX, y: next.minY + nextRadius), radius: nextRadius) + context.addLine(to: CGPoint(x: next.maxX, y: next.midY)) + } + } + + let last = rects[rects.count - 1] + context.addLine(to: CGPoint(x: last.maxX, y: last.maxY - outerRadius)) + context.addArc(tangent1End: last.bottomRight, tangent2End: CGPoint(x: last.maxX - outerRadius, y: last.maxY), radius: outerRadius) + context.addLine(to: CGPoint(x: last.minX + outerRadius, y: last.maxY)) + context.addArc(tangent1End: last.bottomLeft, tangent2End: CGPoint(x: last.minX, y: last.maxY - outerRadius), radius: outerRadius) + + for i in (1 ..< rects.count).reversed() { + let rect = rects[i] + let prev = rects[i - 1] + + if rect.minX == prev.minX { + context.addLine(to: CGPoint(x: prev.minX, y: prev.midY)) + } else { + let prevRadius = min(outerRadius, floor(abs(rect.minX - prev.minX) * 0.5)) + context.addLine(to: CGPoint(x: rect.minX, y: rect.minY + prevRadius)) + if rect.minX < prev.minX { + context.addArc(tangent1End: CGPoint(x: rect.minX, y: rect.minY), tangent2End: CGPoint(x: rect.minX + prevRadius, y: rect.minY), radius: prevRadius) + context.addLine(to: CGPoint(x: prev.minX - prevRadius, y: prev.maxY)) + } else { + context.addArc(tangent1End: CGPoint(x: rect.minX, y: rect.minY), tangent2End: CGPoint(x: rect.minX - prevRadius, y: rect.minY), radius: prevRadius) + context.addLine(to: CGPoint(x: prev.minX + prevRadius, y: prev.maxY)) + } + context.addArc(tangent1End: prev.bottomLeft, tangent2End: CGPoint(x: prev.minX, y: prev.maxY - prevRadius), radius: prevRadius) + context.addLine(to: CGPoint(x: prev.minX, y: prev.midY)) + } + } + + context.addLine(to: CGPoint(x: rects[0].minX, y: rects[0].minY + outerRadius)) + context.addArc(tangent1End: rects[0].topLeft, tangent2End: CGPoint(x: rects[0].minX + outerRadius, y: rects[0].minY), radius: outerRadius) + context.addLine(to: CGPoint(x: rects[0].midX, y: rects[0].minY)) + + if stroke { + context.setStrokeColor(color.cgColor) + context.setLineWidth(strokeWidth) + context.strokePath() + } else { + context.fillPath() + } + } + return + } + + for i in 0 ..< rects.count { + let rect = rects[i].insetBy(dx: -inset, dy: -inset) + context.fill(rect.offsetBy(dx: -topLeft.x, dy: -topLeft.y)) + } + + for i in 0 ..< rects.count { + let rect = rects[i].insetBy(dx: -inset, dy: -inset).offsetBy(dx: -topLeft.x, dy: -topLeft.y) + + var previous: CGRect? + if i != 0 { + previous = rects[i - 1].insetBy(dx: -inset, dy: -inset).offsetBy(dx: -topLeft.x, dy: -topLeft.y) + } + + var next: CGRect? + if i != rects.count - 1 { + next = rects[i + 1].insetBy(dx: -inset, dy: -inset).offsetBy(dx: -topLeft.x, dy: -topLeft.y) + } + + if let previous = previous { + if previous.contains(rect.topLeft) { + if abs(rect.topLeft.x - previous.minX) >= innerRadius { + var radius = innerRadius + if let next = next { + radius = min(radius, floor((next.minY - previous.maxY) / 2.0)) + } + drawConnectingCorner(context: context, color: color, at: CGPoint(x: rect.topLeft.x, y: previous.maxY), type: .topLeft, radius: radius) + } + } else { + drawFullCorner(context: context, color: color, at: rect.topLeft, type: .topLeft, radius: outerRadius) + } + if previous.contains(rect.topRight.offsetBy(dx: -1.0, dy: 0.0)) { + if abs(rect.topRight.x - previous.maxX) >= innerRadius { + var radius = innerRadius + if let next = next { + radius = min(radius, floor((next.minY - previous.maxY) / 2.0)) + } + drawConnectingCorner(context: context, color: color, at: CGPoint(x: rect.topRight.x, y: previous.maxY), type: .topRight, radius: radius) + } + } else { + drawFullCorner(context: context, color: color, at: rect.topRight, type: .topRight, radius: outerRadius) + } + } else { + drawFullCorner(context: context, color: color, at: rect.topLeft, type: .topLeft, radius: outerRadius) + drawFullCorner(context: context, color: color, at: rect.topRight, type: .topRight, radius: outerRadius) + } + + if let next = next { + if next.contains(rect.bottomLeft) { + if abs(rect.bottomRight.x - next.maxX) >= innerRadius { + var radius = innerRadius + if let previous = previous { + radius = min(radius, floor((next.minY - previous.maxY) / 2.0)) + } + drawConnectingCorner(context: context, color: color, at: CGPoint(x: rect.bottomLeft.x, y: next.minY), type: .bottomLeft, radius: radius) + } + } else { + drawFullCorner(context: context, color: color, at: rect.bottomLeft, type: .bottomLeft, radius: outerRadius) + } + if next.contains(rect.bottomRight.offsetBy(dx: -1.0, dy: 0.0)) { + if abs(rect.bottomRight.x - next.maxX) >= innerRadius { + var radius = innerRadius + if let previous = previous { + radius = min(radius, floor((next.minY - previous.maxY) / 2.0)) + } + drawConnectingCorner(context: context, color: color, at: CGPoint(x: rect.bottomRight.x, y: next.minY), type: .bottomRight, radius: radius) + } + } else { + drawFullCorner(context: context, color: color, at: rect.bottomRight, type: .bottomRight, radius: outerRadius) + } + } else { + drawFullCorner(context: context, color: color, at: rect.bottomLeft, type: .bottomLeft, radius: outerRadius) + drawFullCorner(context: context, color: color, at: rect.bottomRight, type: .bottomRight, radius: outerRadius) + } + } +} + private func drawConnectingCorner(context: CGContext, color: UIColor, at point: CGPoint, type: CornerType, radius: CGFloat) { context.setFillColor(color.cgColor) switch type { @@ -76,230 +302,9 @@ public func generateRectsImage(color: UIColor, rects: [CGRect], inset: CGFloat, bottomRight.x += drawingInset * 2.0 bottomRight.y += drawingInset * 2.0 + let capturedTopLeft = topLeft return (topLeft, generateImage(CGSize(width: bottomRight.x - topLeft.x, height: bottomRight.y - topLeft.y), rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - context.setFillColor(color.cgColor) - - context.setBlendMode(.copy) - - if useModernPathCalculation { - if rects.count == 1 { - let path = UIBezierPath(roundedRect: rects[0].offsetBy(dx: -topLeft.x, dy: -topLeft.y), cornerRadius: outerRadius).cgPath - context.addPath(path) - - if stroke { - context.setStrokeColor(color.cgColor) - context.setLineWidth(strokeWidth) - context.strokePath() - } else { - context.fillPath() - } - return - } - - var combinedRects: [[CGRect]] = [] - var currentRects: [CGRect] = [] - for rect in rects { - if rect.width.isZero { - if !currentRects.isEmpty { - combinedRects.append(currentRects) - } - currentRects.removeAll() - } else { - currentRects.append(rect) - } - } - if !currentRects.isEmpty { - combinedRects.append(currentRects) - } - - for rects in combinedRects { - var rects = rects.map { $0.insetBy(dx: -inset, dy: -inset).offsetBy(dx: -topLeft.x, dy: -topLeft.y) } - - let minRadius: CGFloat = 2.0 - - for _ in 0 ..< rects.count * rects.count { - var hadChanges = false - for i in 0 ..< rects.count - 1 { - if rects[i].maxY > rects[i + 1].minY { - let midY = floor((rects[i].maxY + rects[i + 1].minY) * 0.5) - rects[i].size.height = midY - rects[i].minY - rects[i + 1].origin.y = midY - rects[i + 1].size.height = rects[i + 1].maxY - midY - hadChanges = true - } - if rects[i].maxY >= rects[i + 1].minY && rects[i].insetBy(dx: 0.0, dy: 1.0).intersects(rects[i + 1]) { - if abs(rects[i].minX - rects[i + 1].minX) < minRadius { - let commonMinX = min(rects[i].origin.x, rects[i + 1].origin.x) - if rects[i].origin.x != commonMinX { - rects[i].origin.x = commonMinX - hadChanges = true - } - if rects[i + 1].origin.x != commonMinX { - rects[i + 1].origin.x = commonMinX - hadChanges = true - } - } - if abs(rects[i].maxX - rects[i + 1].maxX) < minRadius { - let commonMaxX = max(rects[i].maxX, rects[i + 1].maxX) - if rects[i].maxX != commonMaxX { - rects[i].size.width = commonMaxX - rects[i].minX - hadChanges = true - } - if rects[i + 1].maxX != commonMaxX { - rects[i + 1].size.width = commonMaxX - rects[i + 1].minX - hadChanges = true - } - } - } - } - if !hadChanges { - break - } - } - - context.move(to: CGPoint(x: rects[0].midX, y: rects[0].minY)) - context.addLine(to: CGPoint(x: rects[0].maxX - outerRadius, y: rects[0].minY)) - context.addArc(tangent1End: rects[0].topRight, tangent2End: CGPoint(x: rects[0].maxX, y: rects[0].minY + outerRadius), radius: outerRadius) - context.addLine(to: CGPoint(x: rects[0].maxX, y: rects[0].midY)) - - for i in 0 ..< rects.count - 1 { - let rect = rects[i] - let next = rects[i + 1] - - if rect.maxX == next.maxX { - context.addLine(to: CGPoint(x: next.maxX, y: next.midY)) - } else { - let nextRadius = min(outerRadius, floor(abs(rect.maxX - next.maxX) * 0.5)) - context.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY - nextRadius)) - if next.maxX > rect.maxX { - context.addArc(tangent1End: CGPoint(x: rect.maxX, y: rect.maxY), tangent2End: CGPoint(x: rect.maxX + nextRadius, y: rect.maxY), radius: nextRadius) - context.addLine(to: CGPoint(x: next.maxX - nextRadius, y: next.minY)) - } else { - context.addArc(tangent1End: CGPoint(x: rect.maxX, y: rect.maxY), tangent2End: CGPoint(x: rect.maxX - nextRadius, y: rect.maxY), radius: nextRadius) - context.addLine(to: CGPoint(x: next.maxX + nextRadius, y: next.minY)) - } - context.addArc(tangent1End: next.topRight, tangent2End: CGPoint(x: next.maxX, y: next.minY + nextRadius), radius: nextRadius) - context.addLine(to: CGPoint(x: next.maxX, y: next.midY)) - } - } - - let last = rects[rects.count - 1] - context.addLine(to: CGPoint(x: last.maxX, y: last.maxY - outerRadius)) - context.addArc(tangent1End: last.bottomRight, tangent2End: CGPoint(x: last.maxX - outerRadius, y: last.maxY), radius: outerRadius) - context.addLine(to: CGPoint(x: last.minX + outerRadius, y: last.maxY)) - context.addArc(tangent1End: last.bottomLeft, tangent2End: CGPoint(x: last.minX, y: last.maxY - outerRadius), radius: outerRadius) - - for i in (1 ..< rects.count).reversed() { - let rect = rects[i] - let prev = rects[i - 1] - - if rect.minX == prev.minX { - context.addLine(to: CGPoint(x: prev.minX, y: prev.midY)) - } else { - let prevRadius = min(outerRadius, floor(abs(rect.minX - prev.minX) * 0.5)) - context.addLine(to: CGPoint(x: rect.minX, y: rect.minY + prevRadius)) - if rect.minX < prev.minX { - context.addArc(tangent1End: CGPoint(x: rect.minX, y: rect.minY), tangent2End: CGPoint(x: rect.minX + prevRadius, y: rect.minY), radius: prevRadius) - context.addLine(to: CGPoint(x: prev.minX - prevRadius, y: prev.maxY)) - } else { - context.addArc(tangent1End: CGPoint(x: rect.minX, y: rect.minY), tangent2End: CGPoint(x: rect.minX - prevRadius, y: rect.minY), radius: prevRadius) - context.addLine(to: CGPoint(x: prev.minX + prevRadius, y: prev.maxY)) - } - context.addArc(tangent1End: prev.bottomLeft, tangent2End: CGPoint(x: prev.minX, y: prev.maxY - prevRadius), radius: prevRadius) - context.addLine(to: CGPoint(x: prev.minX, y: prev.midY)) - } - } - - context.addLine(to: CGPoint(x: rects[0].minX, y: rects[0].minY + outerRadius)) - context.addArc(tangent1End: rects[0].topLeft, tangent2End: CGPoint(x: rects[0].minX + outerRadius, y: rects[0].minY), radius: outerRadius) - context.addLine(to: CGPoint(x: rects[0].midX, y: rects[0].minY)) - - if stroke { - context.setStrokeColor(color.cgColor) - context.setLineWidth(strokeWidth) - context.strokePath() - } else { - context.fillPath() - } - } - return - } - - for i in 0 ..< rects.count { - let rect = rects[i].insetBy(dx: -inset, dy: -inset) - context.fill(rect.offsetBy(dx: -topLeft.x, dy: -topLeft.y)) - } - - for i in 0 ..< rects.count { - let rect = rects[i].insetBy(dx: -inset, dy: -inset).offsetBy(dx: -topLeft.x, dy: -topLeft.y) - - var previous: CGRect? - if i != 0 { - previous = rects[i - 1].insetBy(dx: -inset, dy: -inset).offsetBy(dx: -topLeft.x, dy: -topLeft.y) - } - - var next: CGRect? - if i != rects.count - 1 { - next = rects[i + 1].insetBy(dx: -inset, dy: -inset).offsetBy(dx: -topLeft.x, dy: -topLeft.y) - } - - if let previous = previous { - if previous.contains(rect.topLeft) { - if abs(rect.topLeft.x - previous.minX) >= innerRadius { - var radius = innerRadius - if let next = next { - radius = min(radius, floor((next.minY - previous.maxY) / 2.0)) - } - drawConnectingCorner(context: context, color: color, at: CGPoint(x: rect.topLeft.x, y: previous.maxY), type: .topLeft, radius: radius) - } - } else { - drawFullCorner(context: context, color: color, at: rect.topLeft, type: .topLeft, radius: outerRadius) - } - if previous.contains(rect.topRight.offsetBy(dx: -1.0, dy: 0.0)) { - if abs(rect.topRight.x - previous.maxX) >= innerRadius { - var radius = innerRadius - if let next = next { - radius = min(radius, floor((next.minY - previous.maxY) / 2.0)) - } - drawConnectingCorner(context: context, color: color, at: CGPoint(x: rect.topRight.x, y: previous.maxY), type: .topRight, radius: radius) - } - } else { - drawFullCorner(context: context, color: color, at: rect.topRight, type: .topRight, radius: outerRadius) - } - } else { - drawFullCorner(context: context, color: color, at: rect.topLeft, type: .topLeft, radius: outerRadius) - drawFullCorner(context: context, color: color, at: rect.topRight, type: .topRight, radius: outerRadius) - } - - if let next = next { - if next.contains(rect.bottomLeft) { - if abs(rect.bottomRight.x - next.maxX) >= innerRadius { - var radius = innerRadius - if let previous = previous { - radius = min(radius, floor((next.minY - previous.maxY) / 2.0)) - } - drawConnectingCorner(context: context, color: color, at: CGPoint(x: rect.bottomLeft.x, y: next.minY), type: .bottomLeft, radius: radius) - } - } else { - drawFullCorner(context: context, color: color, at: rect.bottomLeft, type: .bottomLeft, radius: outerRadius) - } - if next.contains(rect.bottomRight.offsetBy(dx: -1.0, dy: 0.0)) { - if abs(rect.bottomRight.x - next.maxX) >= innerRadius { - var radius = innerRadius - if let previous = previous { - radius = min(radius, floor((next.minY - previous.maxY) / 2.0)) - } - drawConnectingCorner(context: context, color: color, at: CGPoint(x: rect.bottomRight.x, y: next.minY), type: .bottomRight, radius: radius) - } - } else { - drawFullCorner(context: context, color: color, at: rect.bottomRight, type: .bottomRight, radius: outerRadius) - } - } else { - drawFullCorner(context: context, color: color, at: rect.bottomLeft, type: .bottomLeft, radius: outerRadius) - drawFullCorner(context: context, color: color, at: rect.bottomRight, type: .bottomRight, radius: outerRadius) - } - } + drawRectsImageContent(size: size, context: context, color: color, rects: rects, inset: inset, outerRadius: outerRadius, innerRadius: innerRadius, stroke: stroke, strokeWidth: strokeWidth, useModernPathCalculation: useModernPathCalculation, topLeft: capturedTopLeft) })) } diff --git a/submodules/Display/Source/ListView.swift b/submodules/Display/Source/ListView.swift index bf3f593a..a1ab459d 100644 --- a/submodules/Display/Source/ListView.swift +++ b/submodules/Display/Source/ListView.swift @@ -210,7 +210,6 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel private final var displayLink: CADisplayLink! private final var needsAnimations = false - public final var dynamicBounceEnabled = true public final var rotated = false public final var experimentalSnapScrollToItem = false public final var useMainQueueTransactions = false @@ -265,6 +264,9 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel public final var addContentOffset: ((CGFloat, ListViewItemNode?) -> Void)? public final var shouldStopScrolling: ((CGFloat) -> Bool)? public final var onContentsUpdated: ((ContainedViewLayoutTransition) -> Void)? + + public private(set) final var edgeEffectExtension: CGFloat = 0.0 + public final var onEdgeEffectExtensionUpdated: ((ContainedViewLayoutTransition) -> Void)? public final var updateScrollingIndicator: ((ScrollingIndicatorState?, ContainedViewLayoutTransition) -> Void)? @@ -1044,41 +1046,10 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel self.enqueueUpdateVisibleItems(synchronous: false) } - var useScrollDynamics = false - - let anchor: CGFloat - if self.isTracking { - anchor = self.touchesPosition.y - } else if deltaY < 0.0 { - anchor = self.visibleSize.height - } else { - anchor = 0.0 - } - self.didScrollWithOffset?(deltaY, .immediate, nil, self.isTrackingOrDecelerating) for itemNode in self.itemNodes { itemNode.updateFrame(itemNode.frame.offsetBy(dx: 0.0, dy: -deltaY), within: self.visibleSize) - - if self.dynamicBounceEnabled && itemNode.wantsScrollDynamics { - useScrollDynamics = true - - var distance: CGFloat - let itemFrame = itemNode.apparentFrame - if anchor < itemFrame.origin.y { - distance = abs(itemFrame.origin.y - anchor) - } else if anchor > itemFrame.origin.y + itemFrame.size.height { - distance = abs(anchor - (itemFrame.origin.y + itemFrame.size.height)) - } else { - distance = 0.0 - } - - let factor: CGFloat = max(0.08, abs(distance) / self.visibleSize.height) - - let resistance: CGFloat = testSpringFreeResistance - - itemNode.addScrollingOffset(deltaY * factor * resistance) - } } if !self.snapToBounds(snapTopItem: false, stackFromBottom: self.stackFromBottom, insetDeltaOffsetFix: 0.0).offset.isZero { @@ -1088,38 +1059,10 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel self.updateItemHeaders(leftInset: self.insets.left, rightInset: self.insets.right, synchronousLoad: false) - for (_, headerNode) in self.itemHeaderNodes { - if self.dynamicBounceEnabled && headerNode.wantsScrollDynamics { - useScrollDynamics = true - - var distance: CGFloat - let itemFrame = headerNode.frame - if anchor < itemFrame.origin.y { - distance = abs(itemFrame.origin.y - anchor) - } else if anchor > itemFrame.origin.y + itemFrame.size.height { - distance = abs(anchor - (itemFrame.origin.y + itemFrame.size.height)) - } else { - distance = 0.0 - } - - let factor: CGFloat = max(0.08, abs(distance) / self.visibleSize.height) - - let resistance: CGFloat = testSpringFreeResistance - - headerNode.addScrollingOffset(deltaY * factor * resistance) - } - } - - if useScrollDynamics { - self.setNeedsAnimations() - } - self.updateVisibleContentOffset() self.updateVisibleItemRange() self.updateItemNodesVisibilities(onlyPositive: false) self.onContentsUpdated?(.immediate) - - //CATransaction.commit() } private func calculateAdditionalTopInverseInset() -> CGFloat { @@ -3914,6 +3857,8 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel let flashing = self.headerItemsAreFlashing() + var maxEdgeEffectExtension: CGFloat = 0.0 + func addHeader(id: VisibleHeaderNodeId, upperBound: CGFloat, upperIndex: Int, upperBoundEdge: CGFloat, lowerBound: CGFloat, lowerIndex: Int, item: ListViewItemHeader, hasValidNodes: Bool) { let itemHeaderHeight: CGFloat = item.height @@ -3928,7 +3873,11 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel switch item.stickDirection { case .top: naturalY = lowerBound - headerFrame = CGRect(origin: CGPoint(x: 0.0, y: min(max(upperDisplayBound, upperBound), lowerBound - itemHeaderHeight)), size: CGSize(width: self.visibleSize.width, height: itemHeaderHeight)) + if item.isSticky { + headerFrame = CGRect(origin: CGPoint(x: 0.0, y: min(max(upperDisplayBound, upperBound), lowerBound - itemHeaderHeight)), size: CGSize(width: self.visibleSize.width, height: itemHeaderHeight)) + } else { + headerFrame = CGRect(origin: CGPoint(x: 0.0, y: min(upperBound, lowerBound - itemHeaderHeight)), size: CGSize(width: self.visibleSize.width, height: itemHeaderHeight)) + } stickLocationDistance = headerFrame.minY - upperBound stickLocationDistanceFactor = max(0.0, min(1.0, stickLocationDistance / itemHeaderHeight)) case .topEdge: @@ -4096,6 +4045,11 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel } headerNode.updateStickDistanceFactor(stickLocationDistanceFactor, distance: stickLocationDistance, transition: .immediate) } + + if headerNode.contributesToEdgeEffect && stickLocationDistance > 0.0 { + maxEdgeEffectExtension = max(maxEdgeEffectExtension, upperDisplayBound + headerFrame.height + 8.0) + } + headerNode.offsetByHeaderNodeId = offsetByHeaderNodeId headerNode.naturalOriginY = naturalY var maxIntersectionHeight: (CGFloat, Int)? @@ -4228,6 +4182,11 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel } } } + + if self.edgeEffectExtension != maxEdgeEffectExtension { + self.edgeEffectExtension = maxEdgeEffectExtension + self.onEdgeEffectExtensionUpdated?(transition.0) + } } private func updateItemNodesVisibilities(onlyPositive: Bool) { diff --git a/submodules/Display/Source/ListViewItemHeader.swift b/submodules/Display/Source/ListViewItemHeader.swift index 98a43c73..92bb2f08 100644 --- a/submodules/Display/Source/ListViewItemHeader.swift +++ b/submodules/Display/Source/ListViewItemHeader.swift @@ -12,6 +12,7 @@ public protocol ListViewItemHeader: AnyObject { var id: ListViewItemNode.HeaderId { get } var stackingId: ListViewItemNode.HeaderId? { get } var stickDirection: ListViewItemHeaderStickDirection { get } + var isSticky: Bool { get } var height: CGFloat { get } var stickOverInsets: Bool { get } @@ -21,14 +22,19 @@ public protocol ListViewItemHeader: AnyObject { func updateNode(_ node: ListViewItemHeaderNode, previous: ListViewItemHeader?, next: ListViewItemHeader?) } +public extension ListViewItemHeader { + var isSticky: Bool { + return true + } +} + open class ListViewItemHeaderNode: ASDisplayNode { - private final var spring: ListViewItemSpring? - let wantsScrollDynamics: Bool let isRotated: Bool final private(set) var internalStickLocationDistanceFactor: CGFloat = 0.0 final var internalStickLocationDistance: CGFloat = 0.0 private var isFlashingOnScrolling = false weak var attachedToItemNode: ListViewItemNode? + public var contributesToEdgeEffect: Bool = false var offsetByHeaderNodeId: ListViewItemNode.HeaderId? var naturalOriginY: CGFloat? @@ -53,12 +59,8 @@ open class ListViewItemHeaderNode: ASDisplayNode { return self.alpha } - public init(layerBacked: Bool = false, dynamicBounce: Bool = false, isRotated: Bool = false, seeThrough: Bool = false) { - self.wantsScrollDynamics = dynamicBounce + public init(layerBacked: Bool = false, isRotated: Bool = false, seeThrough: Bool = false) { self.isRotated = isRotated - if dynamicBounce { - self.spring = ListViewItemSpring(stiffness: -280.0, damping: -24.0, mass: 0.85) - } super.init() @@ -69,54 +71,10 @@ open class ListViewItemHeaderNode: ASDisplayNode { } final func addScrollingOffset(_ scrollingOffset: CGFloat) { - if self.spring != nil && internalStickLocationDistanceFactor.isZero { - let bounds = self.bounds - self.bounds = CGRect(origin: CGPoint(x: 0.0, y: bounds.origin.y + scrollingOffset), size: bounds.size) - } } public func animate(_ timestamp: Double) -> Bool { - var continueAnimations = false - - if let _ = self.spring { - let bounds = self.bounds - var offset = bounds.origin.y - let currentOffset = offset - - let frictionConstant: CGFloat = testSpringFriction - let springConstant: CGFloat = testSpringConstant - let time: CGFloat = 1.0 / 60.0 - - // friction force = velocity * friction constant - let frictionForce = self.spring!.velocity * frictionConstant - // spring force = (target point - current position) * spring constant - let springForce = -currentOffset * springConstant - // force = spring force - friction force - let force = springForce - frictionForce - - // velocity = current velocity + force * time / mass - self.spring!.velocity = self.spring!.velocity + force * time - // position = current position + velocity * time - offset = currentOffset + self.spring!.velocity * time - - offset = offset.isNaN ? 0.0 : offset - - let epsilon: CGFloat = 0.1 - if abs(offset) < epsilon && abs(self.spring!.velocity) < epsilon { - offset = 0.0 - self.spring!.velocity = 0.0 - } else { - continueAnimations = true - } - - if abs(offset) > 250.0 { - offset = offset < 0.0 ? -250.0 : 250.0 - } - - self.bounds = CGRect(origin: CGPoint(x: 0.0, y: offset), size: bounds.size) - } - - return continueAnimations + return false } open func animateRemoved(duration: Double) { diff --git a/submodules/Display/Source/ListViewItemNode.swift b/submodules/Display/Source/ListViewItemNode.swift index 42c6a995..360e0791 100644 --- a/submodules/Display/Source/ListViewItemNode.swift +++ b/submodules/Display/Source/ListViewItemNode.swift @@ -129,7 +129,6 @@ open class ListViewItemNode: ASDisplayNode, AccessibilityFocusableNode { return nil } - private final var spring: ListViewItemSpring? private final var animations: [(String, ListViewAnimation)] = [] private final var pendingControlledTransitions: [ControlledTransition] = [] private final var controlledTransitions: [ControlledTransitionContext] = [] @@ -142,8 +141,6 @@ open class ListViewItemNode: ASDisplayNode, AccessibilityFocusableNode { open func attachedHeaderNodesUpdated() { } - final let wantsScrollDynamics: Bool - open var preferredAnimationCurve: (CGFloat) -> CGFloat { return listViewAnimationCurveSystem } @@ -236,12 +233,7 @@ open class ListViewItemNode: ASDisplayNode, AccessibilityFocusableNode { return .complete() } - public init(layerBacked: Bool, dynamicBounce: Bool = true, rotated: Bool = false, seeThrough: Bool = false) { - if dynamicBounce { - self.spring = ListViewItemSpring(stiffness: -280.0, damping: -24.0, mass: 0.85) - } - self.wantsScrollDynamics = dynamicBounce - + public init(layerBacked: Bool, rotated: Bool = false, seeThrough: Bool = false) { self.rotated = rotated super.init() @@ -338,56 +330,14 @@ open class ListViewItemNode: ASDisplayNode, AccessibilityFocusableNode { } final func addScrollingOffset(_ scrollingOffset: CGFloat) { - if self.spring != nil { - self.contentOffset += scrollingOffset - } } func initializeDynamicsFromSibling(_ itemView: ListViewItemNode, additionalOffset: CGFloat) { - if let itemViewSpring = itemView.spring { - self.contentOffset = itemView.contentOffset + additionalOffset - self.spring?.velocity = itemViewSpring.velocity - } } public func animate(timestamp: Double, invertOffsetDirection: inout Bool) -> Bool { var continueAnimations = false - if let _ = self.spring { - var offset = self.contentOffset - - let frictionConstant: CGFloat = testSpringFriction - let springConstant: CGFloat = testSpringConstant - let time: CGFloat = 1.0 / 60.0 - - // friction force = velocity * friction constant - let frictionForce = self.spring!.velocity * frictionConstant - // spring force = (target point - current position) * spring constant - let springForce = -self.contentOffset * springConstant - // force = spring force - friction force - let force = springForce - frictionForce - - // velocity = current velocity + force * time / mass - self.spring!.velocity = self.spring!.velocity + force * time - // position = current position + velocity * time - offset = self.contentOffset + self.spring!.velocity * time - - offset = offset.isNaN ? 0.0 : offset - - let epsilon: CGFloat = 0.1 - if abs(offset) < epsilon && abs(self.spring!.velocity) < epsilon { - offset = 0.0 - self.spring!.velocity = 0.0 - } else { - continueAnimations = true - } - - if abs(offset) > 250.0 { - offset = offset < 0.0 ? -250.0 : 250.0 - } - self.contentOffset = offset - } - var i = 0 var animationCount = self.animations.count while i < animationCount { diff --git a/submodules/Display/Source/Navigation/NavigationModalFrame.swift b/submodules/Display/Source/Navigation/NavigationModalFrame.swift index 2120a419..91c05d66 100644 --- a/submodules/Display/Source/Navigation/NavigationModalFrame.swift +++ b/submodules/Display/Source/Navigation/NavigationModalFrame.swift @@ -109,7 +109,7 @@ public final class NavigationModalFrame: ASDisplayNode { let contentScale = (layout.size.width - sideInset * 2.0) / layout.size.width let bottomInset: CGFloat = layout.size.height - contentScale * layout.size.height - topInset - let cornerRadius: CGFloat = 28.0 + let cornerRadius: CGFloat = 38.0 let initialCornerRadius: CGFloat if !layout.safeInsets.top.isZero { initialCornerRadius = layout.deviceMetrics.screenCornerRadius diff --git a/submodules/Display/Source/NavigationBackgroundView.swift b/submodules/Display/Source/NavigationBackgroundView.swift new file mode 100644 index 00000000..2fff1fe1 --- /dev/null +++ b/submodules/Display/Source/NavigationBackgroundView.swift @@ -0,0 +1,332 @@ +import Foundation +import UIKit +import AsyncDisplayKit + +private var sharedIsReduceTransparencyEnabled = UIAccessibility.isReduceTransparencyEnabled + +public final class NavigationBackgroundNode: ASDisplayNode { + private var _color: UIColor + + public var color: UIColor { + return self._color + } + + private var enableBlur: Bool + private var enableSaturation: Bool + private var customBlurRadius: CGFloat? + + public var effectView: UIVisualEffectView? + private let backgroundNode: ASDisplayNode + + public var backgroundView: UIView { + return self.backgroundNode.view + } + + private var validLayout: (CGSize, CGFloat)? + + public var backgroundCornerRadius: CGFloat { + if let (_, cornerRadius) = self.validLayout { + return cornerRadius + } else { + return 0.0 + } + } + + public init(color: UIColor, enableBlur: Bool = true, enableSaturation: Bool = true, customBlurRadius: CGFloat? = nil) { + self._color = .clear + self.enableBlur = enableBlur + self.enableSaturation = enableSaturation + self.customBlurRadius = customBlurRadius + + self.backgroundNode = ASDisplayNode() + + super.init() + + self.addSubnode(self.backgroundNode) + + self.updateColor(color: color, transition: .immediate) + } + + + public override func didLoad() { + super.didLoad() + + if self.scheduledUpdate { + self.scheduledUpdate = false + self.updateBackgroundBlur(forceKeepBlur: false) + } + } + + private var scheduledUpdate = false + + private func updateBackgroundBlur(forceKeepBlur: Bool) { + guard self.isNodeLoaded else { + self.scheduledUpdate = true + return + } + if self.enableBlur && !sharedIsReduceTransparencyEnabled && ((self._color.alpha > .ulpOfOne && self._color.alpha < 0.95) || forceKeepBlur) { + if self.effectView == nil { + let effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light)) + + for subview in effectView.subviews { + if subview.description.contains("VisualEffectSubview") { + subview.isHidden = true + } + } + + if let sublayer = effectView.layer.sublayers?[0], let filters = sublayer.filters { + sublayer.backgroundColor = nil + sublayer.isOpaque = false + var allowedKeys: [String] = [ + "gaussianBlur" + ] + if self.enableSaturation { + allowedKeys.append("colorSaturate") + } + sublayer.filters = filters.filter { filter in + guard let filter = filter as? NSObject else { + return true + } + let filterName = String(describing: filter) + if !allowedKeys.contains(filterName) { + return false + } + if let customBlurRadius = self.customBlurRadius, filterName == "gaussianBlur" { + filter.setValue(customBlurRadius as NSNumber, forKey: "inputRadius") + } + return true + } + } + + if let (size, cornerRadius) = self.validLayout { + effectView.frame = CGRect(origin: CGPoint(), size: size) + ContainedViewLayoutTransition.immediate.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius) + effectView.clipsToBounds = !cornerRadius.isZero + } + self.effectView = effectView + self.view.insertSubview(effectView, at: 0) + } + } else if let effectView = self.effectView { + self.effectView = nil + effectView.removeFromSuperview() + } + } + + public func updateColor(color: UIColor, enableBlur: Bool? = nil, enableSaturation: Bool? = nil, forceKeepBlur: Bool = false, transition: ContainedViewLayoutTransition) { + let effectiveEnableBlur = enableBlur ?? self.enableBlur + let effectiveEnableSaturation = enableSaturation ?? self.enableSaturation + + if self._color.isEqual(color) && self.enableBlur == effectiveEnableBlur && self.enableSaturation == effectiveEnableSaturation { + return + } + self._color = color + self.enableBlur = effectiveEnableBlur + self.enableSaturation = effectiveEnableSaturation + + if sharedIsReduceTransparencyEnabled { + transition.updateBackgroundColor(node: self.backgroundNode, color: self._color.withAlphaComponent(1.0)) + } else { + transition.updateBackgroundColor(node: self.backgroundNode, color: self._color) + } + + self.updateBackgroundBlur(forceKeepBlur: forceKeepBlur) + } + + public func update(size: CGSize, cornerRadius: CGFloat = 0.0, transition: ContainedViewLayoutTransition, beginWithCurrentState: Bool = true) { + self.validLayout = (size, cornerRadius) + + let contentFrame = CGRect(origin: CGPoint(), size: size) + transition.updateFrame(node: self.backgroundNode, frame: contentFrame, beginWithCurrentState: true) + if let effectView = self.effectView, effectView.frame != contentFrame { + transition.updateFrame(layer: effectView.layer, frame: contentFrame, beginWithCurrentState: true) + if let sublayers = effectView.layer.sublayers { + for sublayer in sublayers { + transition.updateFrame(layer: sublayer, frame: contentFrame, beginWithCurrentState: true) + } + } + } + + transition.updateCornerRadius(node: self.backgroundNode, cornerRadius: cornerRadius) + if let effectView = self.effectView { + transition.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius) + effectView.clipsToBounds = !cornerRadius.isZero + } + } + + public func update(size: CGSize, cornerRadius: CGFloat = 0.0, animator: ControlledTransitionAnimator) { + self.validLayout = (size, cornerRadius) + + let contentFrame = CGRect(origin: CGPoint(), size: size) + animator.updateFrame(layer: self.backgroundNode.layer, frame: contentFrame, completion: nil) + if let effectView = self.effectView, effectView.frame != contentFrame { + animator.updateFrame(layer: effectView.layer, frame: contentFrame, completion: nil) + if let sublayers = effectView.layer.sublayers { + for sublayer in sublayers { + animator.updateFrame(layer: sublayer, frame: contentFrame, completion: nil) + } + } + } + + animator.updateCornerRadius(layer: self.backgroundNode.layer, cornerRadius: cornerRadius, completion: nil) + if let effectView = self.effectView { + animator.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius, completion: nil) + effectView.clipsToBounds = !cornerRadius.isZero + } + } +} + +open class BlurredBackgroundView: UIView { + private var _color: UIColor? + + private var enableBlur: Bool + private var customBlurRadius: CGFloat? + + public private(set) var effectView: UIVisualEffectView? + private let backgroundView: UIView + + private var validLayout: (CGSize, CGFloat)? + + public var backgroundCornerRadius: CGFloat { + if let (_, cornerRadius) = self.validLayout { + return cornerRadius + } else { + return 0.0 + } + } + + public init(color: UIColor?, enableBlur: Bool = true, customBlurRadius: CGFloat? = nil) { + self._color = nil + self.enableBlur = enableBlur + self.customBlurRadius = customBlurRadius + + self.backgroundView = UIView() + + super.init(frame: CGRect()) + + self.addSubview(self.backgroundView) + + if let color = color { + self.updateColor(color: color, transition: .immediate) + } + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func updateBackgroundBlur(forceKeepBlur: Bool) { + if let color = self._color, self.enableBlur && !sharedIsReduceTransparencyEnabled && ((color.alpha > .ulpOfOne && color.alpha < 0.95) || forceKeepBlur) { + if self.effectView == nil { + let effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light)) + + for subview in effectView.subviews { + if subview.description.contains("VisualEffectSubview") { + subview.isHidden = true + } + } + + if let sublayer = effectView.layer.sublayers?[0], let filters = sublayer.filters { + sublayer.backgroundColor = nil + sublayer.isOpaque = false + //sublayer.setValue(true as NSNumber, forKey: "allowsInPlaceFiltering") + let allowedKeys: [String] = [ + "colorSaturate", + "gaussianBlur" + ] + sublayer.filters = filters.filter { filter in + guard let filter = filter as? NSObject else { + return true + } + let filterName = String(describing: filter) + if !allowedKeys.contains(filterName) { + return false + } + if let customBlurRadius = self.customBlurRadius, filterName == "gaussianBlur" { + filter.setValue(customBlurRadius as NSNumber, forKey: "inputRadius") + } + return true + } + } + + if let (size, cornerRadius) = self.validLayout { + effectView.frame = CGRect(origin: CGPoint(), size: size) + ContainedViewLayoutTransition.immediate.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius) + effectView.clipsToBounds = !cornerRadius.isZero + } + self.effectView = effectView + self.insertSubview(effectView, at: 0) + } + } else if let effectView = self.effectView { + self.effectView = nil + effectView.removeFromSuperview() + } + } + + public func updateColor(color: UIColor, enableBlur: Bool? = nil, forceKeepBlur: Bool = false, transition: ContainedViewLayoutTransition) { + let effectiveEnableBlur = enableBlur ?? self.enableBlur + + if self._color == color && self.enableBlur == effectiveEnableBlur { + return + } + self._color = color + self.enableBlur = effectiveEnableBlur + + if sharedIsReduceTransparencyEnabled { + transition.updateBackgroundColor(layer: self.backgroundView.layer, color: color.withAlphaComponent(1.0)) + } else { + transition.updateBackgroundColor(layer: self.backgroundView.layer, color: color) + } + + self.updateBackgroundBlur(forceKeepBlur: forceKeepBlur) + } + + public func update(size: CGSize, cornerRadius: CGFloat = 0.0, maskedCorners: CACornerMask = [.layerMaxXMaxYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMinXMinYCorner], transition: ContainedViewLayoutTransition) { + self.validLayout = (size, cornerRadius) + + let contentFrame = CGRect(origin: CGPoint(), size: size) + transition.updateFrame(view: self.backgroundView, frame: contentFrame, beginWithCurrentState: true) + if let effectView = self.effectView, effectView.frame != contentFrame { + transition.updateFrame(layer: effectView.layer, frame: contentFrame, beginWithCurrentState: true) + if let sublayers = effectView.layer.sublayers { + for sublayer in sublayers { + transition.updateFrame(layer: sublayer, frame: contentFrame, beginWithCurrentState: true) + } + } + } + + if #available(iOS 11.0, *) { + self.backgroundView.layer.maskedCorners = maskedCorners + } + + transition.updateCornerRadius(layer: self.backgroundView.layer, cornerRadius: cornerRadius) + if let effectView = self.effectView { + transition.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius) + effectView.clipsToBounds = !cornerRadius.isZero + + if #available(iOS 11.0, *) { + effectView.layer.maskedCorners = maskedCorners + } + } + } + + public func update(size: CGSize, cornerRadius: CGFloat = 0.0, animator: ControlledTransitionAnimator) { + self.validLayout = (size, cornerRadius) + + let contentFrame = CGRect(origin: CGPoint(), size: size) + animator.updateFrame(layer: self.backgroundView.layer, frame: contentFrame, completion: nil) + if let effectView = self.effectView, effectView.frame != contentFrame { + animator.updateFrame(layer: effectView.layer, frame: contentFrame, completion: nil) + if let sublayers = effectView.layer.sublayers { + for sublayer in sublayers { + animator.updateFrame(layer: sublayer, frame: contentFrame, completion: nil) + } + } + } + + animator.updateCornerRadius(layer: self.backgroundView.layer, cornerRadius: cornerRadius, completion: nil) + if let effectView = self.effectView { + animator.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius, completion: nil) + effectView.clipsToBounds = !cornerRadius.isZero + } + } +} diff --git a/submodules/Display/Source/NavigationBar.swift b/submodules/Display/Source/NavigationBar.swift index 03f4f718..67ad9cb6 100644 --- a/submodules/Display/Source/NavigationBar.swift +++ b/submodules/Display/Source/NavigationBar.swift @@ -4,50 +4,6 @@ import SwiftSignalKit private var backArrowImageCache: [Int32: UIImage] = [:] -open class SparseNode: ASDisplayNode { - override open func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - if self.alpha.isZero { - return nil - } - for view in self.view.subviews.reversed() { - if let result = view.hitTest(self.view.convert(point, to: view), with: event), result.isUserInteractionEnabled { - return result - } - } - - if !self.bounds.inset(by: self.hitTestSlop).contains(point) { - return nil - } - - let result = super.hitTest(point, with: event) - if result != self.view { - return result - } else { - return nil - } - } -} - -open class SparseContainerView: UIView { - override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - if self.alpha.isZero { - return nil - } - for view in self.subviews.reversed() { - if let result = view.hitTest(self.convert(point, to: view), with: event), result.isUserInteractionEnabled { - return result - } - } - - let result = super.hitTest(point, with: event) - if result != self { - return result - } else { - return nil - } - } -} - public final class NavigationBarTheme { public static func generateBackArrowImage(color: UIColor) -> UIImage? { return generateImage(CGSize(width: 13.0, height: 22.0), rotatedContext: { size, context in @@ -60,6 +16,7 @@ public final class NavigationBarTheme { }) } + public let overallDarkAppearance: Bool public let buttonColor: UIColor public let disabledButtonColor: UIColor public let primaryTextColor: UIColor @@ -70,8 +27,11 @@ public final class NavigationBarTheme { public let badgeBackgroundColor: UIColor public let badgeStrokeColor: UIColor public let badgeTextColor: UIColor + public let edgeEffectColor: UIColor? + public let style: NavigationBar.Style - public init(buttonColor: UIColor, disabledButtonColor: UIColor, primaryTextColor: UIColor, backgroundColor: UIColor, opaqueBackgroundColor: UIColor? = nil, enableBackgroundBlur: Bool, separatorColor: UIColor, badgeBackgroundColor: UIColor, badgeStrokeColor: UIColor, badgeTextColor: UIColor) { + public init(overallDarkAppearance: Bool, buttonColor: UIColor, disabledButtonColor: UIColor, primaryTextColor: UIColor, backgroundColor: UIColor, opaqueBackgroundColor: UIColor? = nil, enableBackgroundBlur: Bool, separatorColor: UIColor, badgeBackgroundColor: UIColor, badgeStrokeColor: UIColor, badgeTextColor: UIColor, edgeEffectColor: UIColor? = nil, style: NavigationBar.Style = .legacy) { + self.overallDarkAppearance = overallDarkAppearance self.buttonColor = buttonColor self.disabledButtonColor = disabledButtonColor self.primaryTextColor = primaryTextColor @@ -82,14 +42,16 @@ public final class NavigationBarTheme { self.badgeBackgroundColor = badgeBackgroundColor self.badgeStrokeColor = badgeStrokeColor self.badgeTextColor = badgeTextColor + self.edgeEffectColor = edgeEffectColor + self.style = style } public func withUpdatedBackgroundColor(_ color: UIColor) -> NavigationBarTheme { - return NavigationBarTheme(buttonColor: self.buttonColor, disabledButtonColor: self.disabledButtonColor, primaryTextColor: self.primaryTextColor, backgroundColor: color, opaqueBackgroundColor: self.opaqueBackgroundColor, enableBackgroundBlur: false, separatorColor: self.separatorColor, badgeBackgroundColor: self.badgeBackgroundColor, badgeStrokeColor: self.badgeStrokeColor, badgeTextColor: self.badgeTextColor) + return NavigationBarTheme(overallDarkAppearance: self.overallDarkAppearance, buttonColor: self.buttonColor, disabledButtonColor: self.disabledButtonColor, primaryTextColor: self.primaryTextColor, backgroundColor: color, opaqueBackgroundColor: self.opaqueBackgroundColor, enableBackgroundBlur: false, separatorColor: self.separatorColor, badgeBackgroundColor: self.badgeBackgroundColor, badgeStrokeColor: self.badgeStrokeColor, badgeTextColor: self.badgeTextColor, edgeEffectColor: self.edgeEffectColor, style: self.style) } public func withUpdatedSeparatorColor(_ color: UIColor) -> NavigationBarTheme { - return NavigationBarTheme(buttonColor: self.buttonColor, disabledButtonColor: self.disabledButtonColor, primaryTextColor: self.primaryTextColor, backgroundColor: self.backgroundColor, opaqueBackgroundColor: self.opaqueBackgroundColor, enableBackgroundBlur: self.enableBackgroundBlur, separatorColor: color, badgeBackgroundColor: self.badgeBackgroundColor, badgeStrokeColor: self.badgeStrokeColor, badgeTextColor: self.badgeTextColor) + return NavigationBarTheme(overallDarkAppearance: self.overallDarkAppearance, buttonColor: self.buttonColor, disabledButtonColor: self.disabledButtonColor, primaryTextColor: self.primaryTextColor, backgroundColor: self.backgroundColor, opaqueBackgroundColor: self.opaqueBackgroundColor, enableBackgroundBlur: self.enableBackgroundBlur, separatorColor: color, badgeBackgroundColor: self.badgeBackgroundColor, badgeStrokeColor: self.badgeStrokeColor, badgeTextColor: self.badgeTextColor, edgeEffectColor: self.edgeEffectColor, style: self.style) } } @@ -135,1700 +97,101 @@ public enum NavigationPreviousAction: Equatable { } } -private var sharedIsReduceTransparencyEnabled = UIAccessibility.isReduceTransparencyEnabled - -public final class NavigationBackgroundNode: ASDisplayNode { - private var _color: UIColor - - public var color: UIColor { - return self._color - } - - private var enableBlur: Bool - private var enableSaturation: Bool - private var customBlurRadius: CGFloat? - - public var effectView: UIVisualEffectView? - private let backgroundNode: ASDisplayNode - - public var backgroundView: UIView { - return self.backgroundNode.view - } - - private var validLayout: (CGSize, CGFloat)? - - public var backgroundCornerRadius: CGFloat { - if let (_, cornerRadius) = self.validLayout { - return cornerRadius - } else { - return 0.0 - } - } - - public init(color: UIColor, enableBlur: Bool = true, enableSaturation: Bool = true, customBlurRadius: CGFloat? = nil) { - self._color = .clear - self.enableBlur = enableBlur - self.enableSaturation = enableSaturation - self.customBlurRadius = customBlurRadius - - self.backgroundNode = ASDisplayNode() - - super.init() - - self.addSubnode(self.backgroundNode) - - self.updateColor(color: color, transition: .immediate) - } - - - public override func didLoad() { - super.didLoad() - - if self.scheduledUpdate { - self.scheduledUpdate = false - self.updateBackgroundBlur(forceKeepBlur: false) - } - } - - private var scheduledUpdate = false - - private func updateBackgroundBlur(forceKeepBlur: Bool) { - guard self.isNodeLoaded else { - self.scheduledUpdate = true - return - } - if self.enableBlur && !sharedIsReduceTransparencyEnabled && ((self._color.alpha > .ulpOfOne && self._color.alpha < 0.95) || forceKeepBlur) { - if self.effectView == nil { - let effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light)) - - for subview in effectView.subviews { - if subview.description.contains("VisualEffectSubview") { - subview.isHidden = true - } - } - - if let sublayer = effectView.layer.sublayers?[0], let filters = sublayer.filters { - sublayer.backgroundColor = nil - sublayer.isOpaque = false - var allowedKeys: [String] = [ - "gaussianBlur" - ] - if self.enableSaturation { - allowedKeys.append("colorSaturate") - } - sublayer.filters = filters.filter { filter in - guard let filter = filter as? NSObject else { - return true - } - let filterName = String(describing: filter) - if !allowedKeys.contains(filterName) { - return false - } - if let customBlurRadius = self.customBlurRadius, filterName == "gaussianBlur" { - filter.setValue(customBlurRadius as NSNumber, forKey: "inputRadius") - } - return true - } - } - - if let (size, cornerRadius) = self.validLayout { - effectView.frame = CGRect(origin: CGPoint(), size: size) - ContainedViewLayoutTransition.immediate.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius) - effectView.clipsToBounds = !cornerRadius.isZero - } - self.effectView = effectView - self.view.insertSubview(effectView, at: 0) - } - } else if let effectView = self.effectView { - self.effectView = nil - effectView.removeFromSuperview() - } - } - - public func updateColor(color: UIColor, enableBlur: Bool? = nil, enableSaturation: Bool? = nil, forceKeepBlur: Bool = false, transition: ContainedViewLayoutTransition) { - let effectiveEnableBlur = enableBlur ?? self.enableBlur - let effectiveEnableSaturation = enableSaturation ?? self.enableSaturation - - if self._color.isEqual(color) && self.enableBlur == effectiveEnableBlur && self.enableSaturation == effectiveEnableSaturation { - return - } - self._color = color - self.enableBlur = effectiveEnableBlur - self.enableSaturation = effectiveEnableSaturation - - if sharedIsReduceTransparencyEnabled { - transition.updateBackgroundColor(node: self.backgroundNode, color: self._color.withAlphaComponent(1.0)) - } else { - transition.updateBackgroundColor(node: self.backgroundNode, color: self._color) - } - - self.updateBackgroundBlur(forceKeepBlur: forceKeepBlur) - } - - public func update(size: CGSize, cornerRadius: CGFloat = 0.0, transition: ContainedViewLayoutTransition, beginWithCurrentState: Bool = true) { - self.validLayout = (size, cornerRadius) - - let contentFrame = CGRect(origin: CGPoint(), size: size) - transition.updateFrame(node: self.backgroundNode, frame: contentFrame, beginWithCurrentState: true) - if let effectView = self.effectView, effectView.frame != contentFrame { - transition.updateFrame(layer: effectView.layer, frame: contentFrame, beginWithCurrentState: true) - if let sublayers = effectView.layer.sublayers { - for sublayer in sublayers { - transition.updateFrame(layer: sublayer, frame: contentFrame, beginWithCurrentState: true) - } - } - } - - transition.updateCornerRadius(node: self.backgroundNode, cornerRadius: cornerRadius) - if let effectView = self.effectView { - transition.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius) - effectView.clipsToBounds = !cornerRadius.isZero - } - } - - public func update(size: CGSize, cornerRadius: CGFloat = 0.0, animator: ControlledTransitionAnimator) { - self.validLayout = (size, cornerRadius) - - let contentFrame = CGRect(origin: CGPoint(), size: size) - animator.updateFrame(layer: self.backgroundNode.layer, frame: contentFrame, completion: nil) - if let effectView = self.effectView, effectView.frame != contentFrame { - animator.updateFrame(layer: effectView.layer, frame: contentFrame, completion: nil) - if let sublayers = effectView.layer.sublayers { - for sublayer in sublayers { - animator.updateFrame(layer: sublayer, frame: contentFrame, completion: nil) - } - } - } - - animator.updateCornerRadius(layer: self.backgroundNode.layer, cornerRadius: cornerRadius, completion: nil) - if let effectView = self.effectView { - animator.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius, completion: nil) - effectView.clipsToBounds = !cornerRadius.isZero - } - } +public enum NavigationBarStyle { + case legacy + case glass } -open class BlurredBackgroundView: UIView { - private var _color: UIColor? - - private var enableBlur: Bool - private var customBlurRadius: CGFloat? - - public private(set) var effectView: UIVisualEffectView? - private let backgroundView: UIView - - private var validLayout: (CGSize, CGFloat)? +public func navigationBarBackArrowImage(color: UIColor) -> UIImage? { + var red: CGFloat = 0.0 + var green: CGFloat = 0.0 + var blue: CGFloat = 0.0 + var alpha: CGFloat = 0.0 + color.getRed(&red, green: &green, blue: &blue, alpha: &alpha) - public var backgroundCornerRadius: CGFloat { - if let (_, cornerRadius) = self.validLayout { - return cornerRadius - } else { - return 0.0 - } - } - - public init(color: UIColor?, enableBlur: Bool = true, customBlurRadius: CGFloat? = nil) { - self._color = nil - self.enableBlur = enableBlur - self.customBlurRadius = customBlurRadius - - self.backgroundView = UIView() - - super.init(frame: CGRect()) - - self.addSubview(self.backgroundView) - - if let color = color { - self.updateColor(color: color, transition: .immediate) - } - } - - required public init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func updateBackgroundBlur(forceKeepBlur: Bool) { - if let color = self._color, self.enableBlur && !sharedIsReduceTransparencyEnabled && ((color.alpha > .ulpOfOne && color.alpha < 0.95) || forceKeepBlur) { - if self.effectView == nil { - let effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light)) - - for subview in effectView.subviews { - if subview.description.contains("VisualEffectSubview") { - subview.isHidden = true - } - } - - if let sublayer = effectView.layer.sublayers?[0], let filters = sublayer.filters { - sublayer.backgroundColor = nil - sublayer.isOpaque = false - //sublayer.setValue(true as NSNumber, forKey: "allowsInPlaceFiltering") - let allowedKeys: [String] = [ - "colorSaturate", - "gaussianBlur" - ] - sublayer.filters = filters.filter { filter in - guard let filter = filter as? NSObject else { - return true - } - let filterName = String(describing: filter) - if !allowedKeys.contains(filterName) { - return false - } - if let customBlurRadius = self.customBlurRadius, filterName == "gaussianBlur" { - filter.setValue(customBlurRadius as NSNumber, forKey: "inputRadius") - } - return true - } - } - - if let (size, cornerRadius) = self.validLayout { - effectView.frame = CGRect(origin: CGPoint(), size: size) - ContainedViewLayoutTransition.immediate.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius) - effectView.clipsToBounds = !cornerRadius.isZero - } - self.effectView = effectView - self.insertSubview(effectView, at: 0) - } - } else if let effectView = self.effectView { - self.effectView = nil - effectView.removeFromSuperview() - } - } - - public func updateColor(color: UIColor, enableBlur: Bool? = nil, forceKeepBlur: Bool = false, transition: ContainedViewLayoutTransition) { - let effectiveEnableBlur = enableBlur ?? self.enableBlur - - if self._color == color && self.enableBlur == effectiveEnableBlur { - return - } - self._color = color - self.enableBlur = effectiveEnableBlur - - if sharedIsReduceTransparencyEnabled { - transition.updateBackgroundColor(layer: self.backgroundView.layer, color: color.withAlphaComponent(1.0)) - } else { - transition.updateBackgroundColor(layer: self.backgroundView.layer, color: color) - } - - self.updateBackgroundBlur(forceKeepBlur: forceKeepBlur) - } - - public func update(size: CGSize, cornerRadius: CGFloat = 0.0, maskedCorners: CACornerMask = [.layerMaxXMaxYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMinXMinYCorner], transition: ContainedViewLayoutTransition) { - self.validLayout = (size, cornerRadius) - - let contentFrame = CGRect(origin: CGPoint(), size: size) - transition.updateFrame(view: self.backgroundView, frame: contentFrame, beginWithCurrentState: true) - if let effectView = self.effectView, effectView.frame != contentFrame { - transition.updateFrame(layer: effectView.layer, frame: contentFrame, beginWithCurrentState: true) - if let sublayers = effectView.layer.sublayers { - for sublayer in sublayers { - transition.updateFrame(layer: sublayer, frame: contentFrame, beginWithCurrentState: true) - } - } - } - - if #available(iOS 11.0, *) { - self.backgroundView.layer.maskedCorners = maskedCorners - } - - transition.updateCornerRadius(layer: self.backgroundView.layer, cornerRadius: cornerRadius) - if let effectView = self.effectView { - transition.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius) - effectView.clipsToBounds = !cornerRadius.isZero - - if #available(iOS 11.0, *) { - effectView.layer.maskedCorners = maskedCorners - } - } - } - - public func update(size: CGSize, cornerRadius: CGFloat = 0.0, animator: ControlledTransitionAnimator) { - self.validLayout = (size, cornerRadius) - - let contentFrame = CGRect(origin: CGPoint(), size: size) - animator.updateFrame(layer: self.backgroundView.layer, frame: contentFrame, completion: nil) - if let effectView = self.effectView, effectView.frame != contentFrame { - animator.updateFrame(layer: effectView.layer, frame: contentFrame, completion: nil) - if let sublayers = effectView.layer.sublayers { - for sublayer in sublayers { - animator.updateFrame(layer: sublayer, frame: contentFrame, completion: nil) - } - } - } - - animator.updateCornerRadius(layer: self.backgroundView.layer, cornerRadius: cornerRadius, completion: nil) - if let effectView = self.effectView { - animator.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius, completion: nil) - effectView.clipsToBounds = !cornerRadius.isZero - } - } -} - -public protocol NavigationBarHeaderView: UIView { -} - -private final class NavigationBackgroundCutoutView: UIView { - private let topLayer: SimpleLayer - private let rightLayer: SimpleLayer - - weak var targetNode: ASDisplayNode? - - override init(frame: CGRect) { - self.topLayer = SimpleLayer() - self.topLayer.backgroundColor = UIColor.white.cgColor - - self.rightLayer = SimpleLayer() - self.rightLayer.backgroundColor = UIColor.white.cgColor - - super.init(frame: frame) - - self.layer.addSublayer(self.topLayer) - self.layer.addSublayer(self.rightLayer) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func update(size: CGSize, cutout: CGSize?, transition: ContainedViewLayoutTransition) { - let cutout = cutout ?? CGSize() - - let topFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: cutout.height)) - let rightFrame = CGRect(origin: CGPoint(x: cutout.width, y: 0.0), size: CGSize(width: max(0.0, size.width - cutout.width), height: size.height)) - - if self.topLayer.frame != topFrame || self.rightLayer.frame != rightFrame { - if cutout.width != 0.0 && cutout.height != 0.0 { - self.targetNode?.view.mask = self - //self.targetNode?.view.addSubview(self) - } - - transition.updateFrame(layer: self.topLayer, frame: topFrame) - transition.updateFrame(layer: self.rightLayer, frame: rightFrame, completion: { [weak self] completed in - guard let self, completed else { - return - } - - if !(cutout.width != 0.0 && cutout.height != 0.0) { - self.targetNode?.view.mask = nil - //self.removeFromSuperview() - } - }) - } - } -} - -open class NavigationBar: ASDisplayNode { - public static var defaultSecondaryContentHeight: CGFloat { - return 38.0 - } - - public static func backArrowImage(color: UIColor) -> UIImage? { - var red: CGFloat = 0.0 - var green: CGFloat = 0.0 - var blue: CGFloat = 0.0 - var alpha: CGFloat = 0.0 - color.getRed(&red, green: &green, blue: &blue, alpha: &alpha) - - let key = (Int32(alpha * 255.0) << 24) | (Int32(red * 255.0) << 16) | (Int32(green * 255.0) << 8) | Int32(blue * 255.0) - if let image = backArrowImageCache[key] { + let key = (Int32(alpha * 255.0) << 24) | (Int32(red * 255.0) << 16) | (Int32(green * 255.0) << 8) | Int32(blue * 255.0) + if let image = backArrowImageCache[key] { + return image + } else { + if let image = NavigationBarTheme.generateBackArrowImage(color: color) { + backArrowImageCache[key] = image return image - } else { - if let image = NavigationBarTheme.generateBackArrowImage(color: color) { - backArrowImageCache[key] = image - return image - } else { - return nil - } - } - } - - public static let thinBackArrowImage = generateTintedImage(image: UIImage(bundleImageName: "Navigation/BackArrow"), color: .white)?.withRenderingMode(.alwaysTemplate) - - public static let titleFont = Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers]) - - var presentationData: NavigationBarPresentationData - - private var validLayout: (size: CGSize, defaultHeight: CGFloat, additionalTopHeight: CGFloat, additionalContentHeight: CGFloat, additionalBackgroundHeight: CGFloat, additionalCutout: CGSize?, leftInset: CGFloat, rightInset: CGFloat, appearsHidden: Bool, isLandscape: Bool)? - private var requestedLayout: Bool = false - var requestContainerLayout: (ContainedViewLayoutTransition) -> Void = { _ in } - - public var backPressed: () -> () = { } - - public var userInfo: Any? - public var makeCustomTransitionNode: ((NavigationBar, Bool) -> CustomNavigationTransitionNode?)? - public var allowsCustomTransition: (() -> Bool)? - - public var customSetContentNode: ((NavigationBarContentNode?, Bool) -> Void)? - - private var collapsed: Bool { - get { - return self.frame.size.height.isLess(than: 44.0) - } - } - - public let stripeNode: ASDisplayNode - public let clippingNode: SparseNode - private let buttonsContainerNode: ASDisplayNode - - public private(set) var contentNode: NavigationBarContentNode? - public private(set) var secondaryContentNode: ASDisplayNode? - public var secondaryContentNodeDisplayFraction: CGFloat = 1.0 - - private var itemTitleListenerKey: Int? - private var itemTitleViewListenerKey: Int? - - private var itemLeftButtonListenerKey: Int? - private var itemLeftButtonSetEnabledListenerKey: Int? - - private var itemRightButtonListenerKey: Int? - private var itemRightButtonsListenerKey: Int? - - private var itemBadgeListenerKey: Int? - - private var hintAnimateTitleNodeOnNextLayout: Bool = false - - private var _item: UINavigationItem? - public var item: UINavigationItem? { - get { - return self._item - } set(value) { - if let previousValue = self._item { - if let itemTitleListenerKey = self.itemTitleListenerKey { - previousValue.removeSetTitleListener(itemTitleListenerKey) - self.itemTitleListenerKey = nil - } - - if let itemLeftButtonListenerKey = self.itemLeftButtonListenerKey { - previousValue.removeSetLeftBarButtonItemListener(itemLeftButtonListenerKey) - self.itemLeftButtonListenerKey = nil - } - - if let itemLeftButtonSetEnabledListenerKey = self.itemLeftButtonSetEnabledListenerKey { - previousValue.leftBarButtonItem?.removeSetEnabledListener(itemLeftButtonSetEnabledListenerKey) - self.itemLeftButtonSetEnabledListenerKey = nil - } - - if let itemRightButtonListenerKey = self.itemRightButtonListenerKey { - previousValue.removeSetRightBarButtonItemListener(itemRightButtonListenerKey) - self.itemRightButtonListenerKey = nil - } - - if let itemRightButtonsListenerKey = self.itemRightButtonsListenerKey { - previousValue.removeSetMultipleRightBarButtonItemsListener(itemRightButtonsListenerKey) - self.itemRightButtonsListenerKey = nil - } - - if let itemBadgeListenerKey = self.itemBadgeListenerKey { - previousValue.removeSetBadgeListener(itemBadgeListenerKey) - self.itemBadgeListenerKey = nil - } - } - self._item = value - - self.leftButtonNode.removeFromSupernode() - self.rightButtonNode.removeFromSupernode() - - if let item = value { - self.title = item.title - self.itemTitleListenerKey = item.addSetTitleListener { [weak self] text, animated in - if let strongSelf = self { - let animateIn = animated && (strongSelf.title?.isEmpty ?? true) - strongSelf.title = text - if animateIn { - strongSelf.titleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) - } - } - } - - self.titleView = item.titleView - self.itemTitleViewListenerKey = item.addSetTitleViewListener { [weak self] titleView in - if let strongSelf = self { - strongSelf.titleView = titleView - } - } - - self.itemLeftButtonListenerKey = item.addSetLeftBarButtonItemListener { [weak self] previousItem, _, animated in - if let strongSelf = self { - if let itemLeftButtonSetEnabledListenerKey = strongSelf.itemLeftButtonSetEnabledListenerKey { - previousItem?.removeSetEnabledListener(itemLeftButtonSetEnabledListenerKey) - strongSelf.itemLeftButtonSetEnabledListenerKey = nil - } - - strongSelf.updateLeftButton(animated: animated) - strongSelf.invalidateCalculatedLayout() - strongSelf.requestLayout() - } - } - - self.itemRightButtonListenerKey = item.addSetRightBarButtonItemListener { [weak self] previousItem, currentItem, animated in - if let strongSelf = self { - strongSelf.updateRightButton(animated: animated) - strongSelf.invalidateCalculatedLayout() - strongSelf.requestLayout() - } - } - - self.itemRightButtonsListenerKey = item.addSetMultipleRightBarButtonItemsListener { [weak self] items, animated in - if let strongSelf = self { - strongSelf.updateRightButton(animated: animated) - strongSelf.invalidateCalculatedLayout() - strongSelf.requestLayout() - } - } - - self.itemBadgeListenerKey = item.addSetBadgeListener { [weak self] text in - if let strongSelf = self { - strongSelf.updateBadgeText(text: text) - } - } - self.updateBadgeText(text: item.badge) - - self.updateLeftButton(animated: false) - self.updateRightButton(animated: false) - } else { - self.title = nil - self.updateLeftButton(animated: false) - self.updateRightButton(animated: false) - } - self.invalidateCalculatedLayout() - self.requestLayout() - } - } - - public var customBackButtonText: String? - - private var title: String? { - didSet { - if let title = self.title { - self.titleNode.attributedText = NSAttributedString(string: title, font: NavigationBar.titleFont, textColor: self.presentationData.theme.primaryTextColor) - self.titleNode.accessibilityLabel = title - if self.titleNode.supernode == nil { - self.buttonsContainerNode.addSubnode(self.titleNode) - } - } else { - self.titleNode.removeFromSupernode() - } - - self.updateAccessibilityElements() - self.invalidateCalculatedLayout() - self.requestLayout() - } - } - - public private(set) var titleView: UIView? { - didSet { - if let oldValue = oldValue { - oldValue.removeFromSuperview() - } - - if let titleView = self.titleView { - self.buttonsContainerNode.view.addSubview(titleView) - } - - self.invalidateCalculatedLayout() - self.requestLayout() - } - } - - public var customHeaderContentView: NavigationBarHeaderView? { - didSet { - if self.customHeaderContentView !== oldValue { - self.customHeaderContentView?.removeFromSuperview() - - if let customHeaderContentView = self.customHeaderContentView { - self.buttonsContainerNode.view.addSubview(customHeaderContentView) - self.backButtonNode.isHidden = true - self.backButtonArrow.isHidden = true - } else { - self.backButtonNode.isHidden = false - self.backButtonArrow.isHidden = false - } - } - } - } - - public var layoutSuspended: Bool = false - - private let titleNode: ImmediateTextNode - - var previousItemListenerKey: Int? - var previousItemBackListenerKey: Int? - - private func updateAccessibilityElements() { - } - - override open var accessibilityElements: [Any]? { - get { - var accessibilityElements: [Any] = [] - if self.backButtonNode.supernode != nil { - addAccessibilityChildren(of: self.backButtonNode, container: self, to: &accessibilityElements) - } - if self.leftButtonNode.supernode != nil { - addAccessibilityChildren(of: self.leftButtonNode, container: self, to: &accessibilityElements) - } - if self.titleNode.supernode != nil { - addAccessibilityChildren(of: self.titleNode, container: self, to: &accessibilityElements) - accessibilityElements.append(self.titleNode) - } - if let titleView = self.titleView, titleView.superview != nil { - titleView.accessibilityFrame = UIAccessibility.convertToScreenCoordinates(titleView.bounds, in: titleView) - accessibilityElements.append(titleView) - } - if self.rightButtonNode.supernode != nil { - addAccessibilityChildren(of: self.rightButtonNode, container: self, to: &accessibilityElements) - } - if let customHeaderContentView = self.customHeaderContentView, customHeaderContentView.superview != nil { - customHeaderContentView.accessibilityFrame = UIAccessibility.convertToScreenCoordinates(customHeaderContentView.bounds, in: customHeaderContentView) - accessibilityElements.append(customHeaderContentView) - } - if let contentNode = self.contentNode { - addAccessibilityChildren(of: contentNode, container: self, to: &accessibilityElements) - } - if let secondaryContentNode = self.secondaryContentNode { - addAccessibilityChildren(of: secondaryContentNode, container: self, to: &accessibilityElements) - } - return accessibilityElements - } set(value) { - } - } - - override open func didLoad() { - super.didLoad() - - self.updateAccessibilityElements() - } - - public var enableAutomaticBackButton: Bool = true - - var _previousItem: NavigationPreviousAction? - public internal(set) var previousItem: NavigationPreviousAction? { - get { - if !self.enableAutomaticBackButton { - return nil - } - return self._previousItem - } set(value) { - if !self.enableAutomaticBackButton { - self._previousItem = nil - return - } - if self._previousItem != value { - if let previousValue = self._previousItem, case let .item(itemValue) = previousValue { - if let previousItemListenerKey = self.previousItemListenerKey { - itemValue.removeSetTitleListener(previousItemListenerKey) - self.previousItemListenerKey = nil - } - if let previousItemBackListenerKey = self.previousItemBackListenerKey { - itemValue.removeSetBackBarButtonItemListener(previousItemBackListenerKey) - self.previousItemBackListenerKey = nil - } - } - self._previousItem = value - - if let previousItem = value { - switch previousItem { - case let .item(itemValue): - self.previousItemListenerKey = itemValue.addSetTitleListener { [weak self] _, _ in - if let strongSelf = self, let previousItem = strongSelf.previousItem, case let .item(itemValue) = previousItem { - if let customBackButtonText = strongSelf.customBackButtonText { - strongSelf.backButtonNode.updateManualText(customBackButtonText, isBack: true) - } else if let backBarButtonItem = itemValue.backBarButtonItem { - strongSelf.backButtonNode.updateManualText(backBarButtonItem.title ?? "", isBack: true) - } else { - strongSelf.backButtonNode.updateManualText(itemValue.title ?? "", isBack: true) - } - strongSelf.invalidateCalculatedLayout() - strongSelf.requestLayout() - } - } - - self.previousItemBackListenerKey = itemValue.addSetBackBarButtonItemListener { [weak self] _, _, _ in - if let strongSelf = self, let previousItem = strongSelf.previousItem, case let .item(itemValue) = previousItem { - if let customBackButtonText = strongSelf.customBackButtonText { - strongSelf.backButtonNode.updateManualText(customBackButtonText, isBack: true) - } else if let backBarButtonItem = itemValue.backBarButtonItem { - strongSelf.backButtonNode.updateManualText(backBarButtonItem.title ?? "", isBack: true) - } else { - strongSelf.backButtonNode.updateManualText(itemValue.title ?? "", isBack: true) - } - strongSelf.invalidateCalculatedLayout() - strongSelf.requestLayout() - } - } - case .close: - break - } - } - self.updateLeftButton(animated: false) - - self.invalidateCalculatedLayout() - self.requestLayout() - } - } - } - - private func updateBadgeText(text: String?) { - let actualText = text ?? "" - if self.badgeNode.text != actualText { - self.badgeNode.text = actualText - self.badgeNode.isHidden = actualText.isEmpty - self.backButtonNode.manualAlpha = self.badgeNode.isHidden ? 1.0 : 0.0 - - self.invalidateCalculatedLayout() - self.requestLayout() - } - } - - private func updateLeftButton(animated: Bool) { - if let item = self.item { - var needsLeftButton = false - if let leftBarButtonItem = item.leftBarButtonItem, !leftBarButtonItem.backButtonAppearance { - needsLeftButton = true - } else if let previousItem = self.previousItem, case .close = previousItem { - needsLeftButton = true - } - - if needsLeftButton { - if animated { - if self.backButtonNode.view.superview != nil { - if let snapshotView = self.backButtonNode.view.snapshotContentTree() { - snapshotView.frame = self.backButtonNode.frame - self.backButtonNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.backButtonNode.view) - snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in - snapshotView?.removeFromSuperview() - }) - } - } - - if self.backButtonArrow.view.superview != nil { - if let snapshotView = self.backButtonArrow.view.snapshotContentTree() { - snapshotView.frame = self.backButtonArrow.frame - self.backButtonArrow.view.superview?.insertSubview(snapshotView, aboveSubview: self.backButtonArrow.view) - snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in - snapshotView?.removeFromSuperview() - }) - } - } - - if self.badgeNode.view.superview != nil { - if let snapshotView = self.badgeNode.view.snapshotContentTree() { - snapshotView.frame = self.badgeNode.frame - self.badgeNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.badgeNode.view) - snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in - snapshotView?.removeFromSuperview() - }) - } - } - } - - self.backButtonNode.removeFromSupernode() - self.backButtonArrow.removeFromSupernode() - self.badgeNode.removeFromSupernode() - - if let leftBarButtonItem = item.leftBarButtonItem { - self.leftButtonNode.updateItems([], animated: animated) - self.leftButtonNode.updateItems([leftBarButtonItem], animated: animated) - } else { - self.leftButtonNode.updateItems([], animated: animated) - self.leftButtonNode.updateItems([UIBarButtonItem(title: self.presentationData.strings.close, style: .plain, target: nil, action: nil)], animated: animated) - } - - if self.leftButtonNode.supernode == nil { - self.buttonsContainerNode.addSubnode(self.leftButtonNode) - } - - if animated { - self.leftButtonNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) - } - } else { - if animated, self.leftButtonNode.view.superview != nil { - if let snapshotView = self.leftButtonNode.view.snapshotContentTree() { - snapshotView.frame = self.leftButtonNode.frame - self.leftButtonNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.leftButtonNode.view) - snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in - snapshotView?.removeFromSuperview() - }) - } - } - self.leftButtonNode.removeFromSupernode() - - var backTitle: String? - if let customBackButtonText = self.customBackButtonText { - backTitle = customBackButtonText - } else if let leftBarButtonItem = item.leftBarButtonItem, leftBarButtonItem.backButtonAppearance { - backTitle = leftBarButtonItem.title - } else if let previousItem = self.previousItem { - switch previousItem { - case let .item(itemValue): - if let backBarButtonItem = itemValue.backBarButtonItem { - backTitle = backBarButtonItem.title ?? self.presentationData.strings.back - } else { - backTitle = itemValue.title ?? self.presentationData.strings.back - } - case .close: - backTitle = nil - } - } - - if let backTitle = backTitle { - self.backButtonNode.updateManualText(backTitle, isBack: true) - if self.backButtonNode.supernode == nil { - self.buttonsContainerNode.addSubnode(self.backButtonNode) - self.buttonsContainerNode.addSubnode(self.backButtonArrow) - self.buttonsContainerNode.addSubnode(self.badgeNode) - } - - if animated { - self.backButtonNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) - self.backButtonArrow.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) - self.badgeNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) - } - } else { - self.backButtonNode.removeFromSupernode() - } - } - } else { - self.leftButtonNode.removeFromSupernode() - self.backButtonNode.removeFromSupernode() - self.backButtonArrow.removeFromSupernode() - self.badgeNode.removeFromSupernode() - } - - self.updateAccessibilityElements() - } - - private func updateRightButton(animated: Bool) { - if let item = self.item { - var items: [UIBarButtonItem] = [] - if let rightBarButtonItems = item.rightBarButtonItems, !rightBarButtonItems.isEmpty { - items = rightBarButtonItems - } else if let rightBarButtonItem = item.rightBarButtonItem { - items = [rightBarButtonItem] - } - - self.rightButtonNodeUpdated = true - - if !items.isEmpty { - self.rightButtonNode.updateItems([], animated: animated) - self.rightButtonNode.updateItems(items, animated: animated) - if self.rightButtonNode.supernode == nil { - self.buttonsContainerNode.addSubnode(self.rightButtonNode) - } - } else { - if animated, self.rightButtonNode.view.superview != nil { - if let snapshotView = self.rightButtonNode.view.snapshotContentTree() { - snapshotView.frame = self.rightButtonNode.frame - self.rightButtonNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.rightButtonNode.view) - snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in - snapshotView?.removeFromSuperview() - }) - } - } - self.rightButtonNode.removeFromSupernode() - } - } else { - if animated, self.rightButtonNode.view.superview != nil { - if let snapshotView = self.rightButtonNode.view.snapshotContentTree() { - snapshotView.frame = self.rightButtonNode.frame - self.rightButtonNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.rightButtonNode.view) - snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in - snapshotView?.removeFromSuperview() - }) - } - } - self.rightButtonNode.removeFromSupernode() - } - - self.updateAccessibilityElements() - } - - public let backgroundNode: NavigationBackgroundNode - public let backButtonNode: NavigationButtonNode - public let badgeNode: NavigationBarBadgeNode - public let backButtonArrow: ASImageNode - public let leftButtonNode: NavigationButtonNode - public let rightButtonNode: NavigationButtonNode - private var rightButtonNodeUpdated: Bool = false - public let additionalContentNode: SparseNode - - private let navigationBackgroundCutoutView: NavigationBackgroundCutoutView - - public func reattachAdditionalContentNode() { - if self.additionalContentNode.supernode !== self { - self.insertSubnode(self.additionalContentNode, aboveSubnode: self.clippingNode) - } - } - - private var _transitionState: NavigationBarTransitionState? - var transitionState: NavigationBarTransitionState? { - get { - return self._transitionState - } set(value) { - let updateNodes = self._transitionState?.navigationBar !== value?.navigationBar - - self._transitionState = value - - if updateNodes { - if let transitionTitleNode = self.transitionTitleNode { - transitionTitleNode.removeFromSupernode() - self.transitionTitleNode = nil - } - - if let transitionBackButtonNode = self.transitionBackButtonNode { - transitionBackButtonNode.removeFromSupernode() - self.transitionBackButtonNode = nil - } - - if let transitionBackArrowNode = self.transitionBackArrowNode { - transitionBackArrowNode.removeFromSupernode() - self.transitionBackArrowNode = nil - } - - if let transitionBadgeNode = self.transitionBadgeNode { - transitionBadgeNode.removeFromSupernode() - self.transitionBadgeNode = nil - } - - if let value = value { - switch value.role { - case .top: - if let transitionTitleNode = value.navigationBar?.makeTransitionTitleNode(foregroundColor: self.presentationData.theme.primaryTextColor) { - self.transitionTitleNode = transitionTitleNode - if self.leftButtonNode.supernode != nil { - self.buttonsContainerNode.insertSubnode(transitionTitleNode, belowSubnode: self.leftButtonNode) - } else if self.backButtonNode.supernode != nil { - self.buttonsContainerNode.insertSubnode(transitionTitleNode, belowSubnode: self.backButtonNode) - } else { - self.buttonsContainerNode.addSubnode(transitionTitleNode) - } - } - case .bottom: - if let transitionBackButtonNode = value.navigationBar?.makeTransitionBackButtonNode(accentColor: self.presentationData.theme.buttonColor) { - self.transitionBackButtonNode = transitionBackButtonNode - self.buttonsContainerNode.addSubnode(transitionBackButtonNode) - } - if let transitionBackArrowNode = value.navigationBar?.makeTransitionBackArrowNode(accentColor: self.presentationData.theme.buttonColor) { - self.transitionBackArrowNode = transitionBackArrowNode - self.buttonsContainerNode.addSubnode(transitionBackArrowNode) - } - if let transitionBadgeNode = value.navigationBar?.makeTransitionBadgeNode() { - self.transitionBadgeNode = transitionBadgeNode - self.buttonsContainerNode.addSubnode(transitionBadgeNode) - } - } - } - } - - self.requestedLayout = true - self.layout() - } - } - - private var transitionTitleNode: ASDisplayNode? - private var transitionBackButtonNode: NavigationButtonNode? - private var transitionBackArrowNode: ASDisplayNode? - private var transitionBadgeNode: ASDisplayNode? - - public var secondaryContentHeight: CGFloat - - public init(presentationData: NavigationBarPresentationData) { - self.presentationData = presentationData - self.stripeNode = ASDisplayNode() - - self.titleNode = ImmediateTextNode() - self.titleNode.isAccessibilityElement = true - self.titleNode.accessibilityTraits = .header - - self.backButtonNode = NavigationButtonNode() - self.backButtonNode.hitTestSlop = UIEdgeInsets(top: 0.0, left: -20.0, bottom: 0.0, right: 0.0) - - self.badgeNode = NavigationBarBadgeNode(fillColor: self.presentationData.theme.buttonColor, strokeColor: self.presentationData.theme.buttonColor, textColor: self.presentationData.theme.badgeTextColor) - self.badgeNode.isUserInteractionEnabled = false - self.badgeNode.isHidden = true - self.backButtonArrow = ASImageNode() - self.backButtonArrow.displayWithoutProcessing = true - self.backButtonArrow.displaysAsynchronously = false - self.backButtonArrow.isUserInteractionEnabled = false - self.leftButtonNode = NavigationButtonNode() - self.rightButtonNode = NavigationButtonNode() - self.rightButtonNode.hitTestSlop = UIEdgeInsets(top: -4.0, left: -4.0, bottom: -4.0, right: -10.0) - - self.clippingNode = SparseNode() - self.clippingNode.clipsToBounds = true - - self.buttonsContainerNode = ASDisplayNode() - self.buttonsContainerNode.clipsToBounds = true - - self.backButtonNode.color = self.presentationData.theme.buttonColor - self.backButtonNode.disabledColor = self.presentationData.theme.disabledButtonColor - self.leftButtonNode.color = self.presentationData.theme.buttonColor - self.leftButtonNode.disabledColor = self.presentationData.theme.disabledButtonColor - self.rightButtonNode.color = self.presentationData.theme.buttonColor - self.rightButtonNode.disabledColor = self.presentationData.theme.disabledButtonColor - self.rightButtonNode.rippleColor = self.presentationData.theme.primaryTextColor.withAlphaComponent(0.05) - self.backButtonArrow.image = NavigationBar.backArrowImage(color: self.presentationData.theme.buttonColor) - if let title = self.title { - self.titleNode.attributedText = NSAttributedString(string: title, font: NavigationBar.titleFont, textColor: self.presentationData.theme.primaryTextColor) - self.titleNode.accessibilityLabel = title - } - self.stripeNode.backgroundColor = self.presentationData.theme.separatorColor - - self.backgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.backgroundColor, enableBlur: self.presentationData.theme.enableBackgroundBlur) - self.additionalContentNode = SparseNode() - - self.secondaryContentHeight = NavigationBar.defaultSecondaryContentHeight - - self.navigationBackgroundCutoutView = NavigationBackgroundCutoutView(frame: CGRect()) - self.navigationBackgroundCutoutView.targetNode = self.backgroundNode - - super.init() - - self.addSubnode(self.backgroundNode) - self.addSubnode(self.buttonsContainerNode) - self.addSubnode(self.clippingNode) - self.addSubnode(self.additionalContentNode) - - self.backgroundColor = nil - self.isOpaque = false - - self.stripeNode.isLayerBacked = true - self.stripeNode.displaysAsynchronously = false - self.addSubnode(self.stripeNode) - - self.titleNode.displaysAsynchronously = false - self.titleNode.maximumNumberOfLines = 1 - self.titleNode.truncationType = .end - self.titleNode.isOpaque = false - - self.backButtonNode.highlightChanged = { [weak self] index, highlighted in - if let strongSelf = self, index == 0 { - strongSelf.backButtonArrow.alpha = (highlighted ? 0.4 : 1.0) - strongSelf.badgeNode.alpha = (highlighted ? 0.4 : 1.0) - } - } - self.backButtonNode.pressed = { [weak self] index in - if let strongSelf = self, index == 0 { - if let leftBarButtonItem = strongSelf.item?.leftBarButtonItem, leftBarButtonItem.backButtonAppearance { - leftBarButtonItem.performActionOnTarget() - } else { - strongSelf.backPressed() - } - } - } - - self.leftButtonNode.pressed = { [weak self] index in - if let item = self?.item { - if index == 0 { - if let leftBarButtonItem = item.leftBarButtonItem { - leftBarButtonItem.performActionOnTarget() - } else if let previousItem = self?.previousItem, case .close = previousItem { - self?.backPressed() - } - } - } - } - - self.rightButtonNode.pressed = { [weak self] index in - if let item = self?.item { - if let rightBarButtonItems = item.rightBarButtonItems, !rightBarButtonItems.isEmpty { - if index < rightBarButtonItems.count { - rightBarButtonItems[index].performActionOnTarget() - } - } else if let rightBarButtonItem = item.rightBarButtonItem { - rightBarButtonItem.performActionOnTarget() - } - } - } - } - - public var isBackgroundVisible: Bool { - return self.backgroundNode.alpha == 1.0 - } - - public func updateBackgroundAlpha(_ alpha: CGFloat, transition: ContainedViewLayoutTransition) { - let alpha = max(0.0, min(1.0, alpha)) - transition.updateAlpha(node: self.backgroundNode, alpha: alpha, delay: 0.15) - transition.updateAlpha(node: self.stripeNode, alpha: alpha, delay: 0.15) - } - - public func updatePresentationData(_ presentationData: NavigationBarPresentationData, transition: ContainedViewLayoutTransition = .immediate) { - if presentationData.theme !== self.presentationData.theme || presentationData.strings !== self.presentationData.strings { - self.presentationData = presentationData - - self.backgroundNode.updateColor(color: self.presentationData.theme.backgroundColor, transition: transition) - - self.backButtonNode.color = self.presentationData.theme.buttonColor - self.backButtonNode.disabledColor = self.presentationData.theme.disabledButtonColor - self.leftButtonNode.color = self.presentationData.theme.buttonColor - self.leftButtonNode.disabledColor = self.presentationData.theme.disabledButtonColor - self.rightButtonNode.color = self.presentationData.theme.buttonColor - self.rightButtonNode.disabledColor = self.presentationData.theme.disabledButtonColor - self.rightButtonNode.rippleColor = self.presentationData.theme.primaryTextColor.withAlphaComponent(0.05) - self.backButtonArrow.image = NavigationBar.backArrowImage(color: self.presentationData.theme.buttonColor) - if let title = self.title { - self.titleNode.attributedText = NSAttributedString(string: title, font: NavigationBar.titleFont, textColor: self.presentationData.theme.primaryTextColor) - self.titleNode.accessibilityLabel = title - } - self.stripeNode.backgroundColor = self.presentationData.theme.separatorColor - - self.badgeNode.updateTheme(fillColor: self.presentationData.theme.buttonColor, strokeColor: self.presentationData.theme.buttonColor, textColor: self.presentationData.theme.badgeTextColor) - - self.updateLeftButton(animated: false) - self.requestLayout() - } - } - - private func requestLayout() { - self.requestedLayout = true - self.setNeedsLayout() - } - - override open func layout() { - super.layout() - - if let validLayout = self.validLayout, self.requestedLayout { - self.requestedLayout = false - self.updateLayout(size: validLayout.size, defaultHeight: validLayout.defaultHeight, additionalTopHeight: validLayout.additionalTopHeight, additionalContentHeight: validLayout.additionalContentHeight, additionalBackgroundHeight: validLayout.additionalBackgroundHeight, additionalCutout: validLayout.additionalCutout, leftInset: validLayout.leftInset, rightInset: validLayout.rightInset, appearsHidden: validLayout.appearsHidden, isLandscape: validLayout.isLandscape, transition: .immediate) - } - } - - func updateLayout(size: CGSize, defaultHeight: CGFloat, additionalTopHeight: CGFloat, additionalContentHeight: CGFloat, additionalBackgroundHeight: CGFloat, additionalCutout: CGSize?, leftInset: CGFloat, rightInset: CGFloat, appearsHidden: Bool, isLandscape: Bool, transition: ContainedViewLayoutTransition) { - if self.layoutSuspended { - return - } - - self.validLayout = (size, defaultHeight, additionalTopHeight, additionalContentHeight, additionalBackgroundHeight, additionalCutout, leftInset, rightInset, appearsHidden, isLandscape) - - let backgroundFrame = CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height + additionalBackgroundHeight)) - if self.backgroundNode.frame != backgroundFrame { - transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame) - self.backgroundNode.update(size: backgroundFrame.size, transition: transition) - - transition.updateFrame(view: self.navigationBackgroundCutoutView, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size)) - } - self.navigationBackgroundCutoutView.update(size: backgroundFrame.size, cutout: additionalCutout, transition: transition) - - let apparentAdditionalHeight: CGFloat = self.secondaryContentNode != nil ? (self.secondaryContentHeight * self.secondaryContentNodeDisplayFraction) : 0.0 - - let leftButtonInset: CGFloat = leftInset + 16.0 - let backButtonInset: CGFloat = leftInset + 27.0 - - transition.updateFrame(node: self.clippingNode, frame: CGRect(origin: CGPoint(), size: size)) - transition.updateFrame(node: self.additionalContentNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height + additionalBackgroundHeight))) - transition.updateFrame(node: self.buttonsContainerNode, frame: CGRect(origin: CGPoint(), size: size)) - var expansionHeight: CGFloat = 0.0 - if let contentNode = self.contentNode { - var contentNodeFrame: CGRect - switch contentNode.mode { - case .replacement: - expansionHeight = contentNode.height - defaultHeight - contentNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height - additionalContentHeight)) - case .expansion: - expansionHeight = contentNode.height - - let additionalExpansionHeight: CGFloat = self.secondaryContentNode != nil && appearsHidden ? (self.secondaryContentHeight * self.secondaryContentNodeDisplayFraction) : 0.0 - contentNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - (appearsHidden ? 0.0 : additionalContentHeight) - expansionHeight - apparentAdditionalHeight - additionalExpansionHeight), size: CGSize(width: size.width, height: expansionHeight)) - if appearsHidden { - if self.secondaryContentNode != nil { - contentNodeFrame.origin.y += self.secondaryContentHeight * self.secondaryContentNodeDisplayFraction - } - } - } - transition.updateFrame(node: contentNode, frame: contentNodeFrame) - contentNode.updateLayout(size: contentNodeFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition) - } - - transition.updateFrame(node: self.stripeNode, frame: CGRect(x: (additionalCutout?.width ?? 0.0), y: size.height + additionalBackgroundHeight, width: size.width - (additionalCutout?.width ?? 0.0), height: UIScreenPixel)) - - let nominalHeight: CGFloat = defaultHeight - let contentVerticalOrigin = additionalTopHeight - - var leftTitleInset: CGFloat = leftInset + 1.0 - var rightTitleInset: CGFloat = rightInset + 1.0 - if self.backButtonNode.supernode != nil { - let backButtonSize = self.backButtonNode.updateLayout(constrainedSize: CGSize(width: size.width, height: nominalHeight), isLandscape: isLandscape, isLeftAligned: true) - leftTitleInset = backButtonSize.width + backButtonInset + 1.0 - - let topHitTestSlop = (nominalHeight - backButtonSize.height) * 0.5 - self.backButtonNode.hitTestSlop = UIEdgeInsets(top: -topHitTestSlop, left: -27.0, bottom: -topHitTestSlop, right: -8.0) - - if let transitionState = self.transitionState { - let progress = transitionState.progress - - switch transitionState.role { - case .top: - let initialX: CGFloat = backButtonInset - let finalX: CGFloat = floor((size.width - backButtonSize.width) / 2.0) - size.width - - let backButtonFrame = CGRect(origin: CGPoint(x: initialX * (1.0 - progress) + finalX * progress, y: contentVerticalOrigin + floor((nominalHeight - backButtonSize.height) / 2.0)), size: backButtonSize) - if self.backButtonNode.frame != backButtonFrame { - self.backButtonNode.frame = backButtonFrame - } - let backButtonAlpha = self.backButtonNode.alpha - if self.backButtonNode.alpha != backButtonAlpha { - self.backButtonNode.alpha = backButtonAlpha - } - - if let transitionTitleNode = self.transitionTitleNode { - let transitionTitleSize = transitionTitleNode.measure(CGSize(width: size.width, height: nominalHeight)) - - let initialX: CGFloat = backButtonInset + floor((backButtonSize.width - transitionTitleSize.width) / 2.0) - let finalX: CGFloat = floor((size.width - transitionTitleSize.width) / 2.0) - size.width - - transitionTitleNode.frame = CGRect(origin: CGPoint(x: initialX * (1.0 - progress) + finalX * progress, y: contentVerticalOrigin + floor((nominalHeight - transitionTitleSize.height) / 2.0)), size: transitionTitleSize) - transitionTitleNode.alpha = progress * progress - } - - self.backButtonArrow.frame = CGRect(origin: CGPoint(x: leftInset + 8.0 - progress * size.width, y: contentVerticalOrigin + floor((nominalHeight - 22.0) / 2.0)), size: CGSize(width: 13.0, height: 22.0)) - self.backButtonArrow.alpha = max(0.0, 1.0 - progress * 1.3) - self.badgeNode.alpha = max(0.0, 1.0 - progress * 1.3) - case .bottom: - self.backButtonNode.alpha = 1.0 - self.backButtonNode.frame = CGRect(origin: CGPoint(x: backButtonInset, y: contentVerticalOrigin + floor((nominalHeight - backButtonSize.height) / 2.0)), size: backButtonSize) - self.backButtonArrow.alpha = 1.0 - self.backButtonArrow.frame = CGRect(origin: CGPoint(x: leftInset + 8.0, y: contentVerticalOrigin + floor((nominalHeight - 22.0) / 2.0)), size: CGSize(width: 13.0, height: 22.0)) - self.badgeNode.alpha = 1.0 - } - } else { - self.backButtonNode.alpha = 1.0 - transition.updateFrame(node: self.backButtonNode, frame: CGRect(origin: CGPoint(x: backButtonInset, y: contentVerticalOrigin + floor((nominalHeight - backButtonSize.height) / 2.0)), size: backButtonSize)) - - self.backButtonArrow.alpha = 1.0 - transition.updateFrame(node: self.backButtonArrow, frame: CGRect(origin: CGPoint(x: leftInset + 8.0, y: contentVerticalOrigin + floor((nominalHeight - 22.0) / 2.0)), size: CGSize(width: 13.0, height: 22.0))) - self.badgeNode.alpha = 1.0 - } - } else if self.leftButtonNode.supernode != nil { - let leftButtonSize = self.leftButtonNode.updateLayout(constrainedSize: CGSize(width: size.width, height: nominalHeight), isLandscape: isLandscape, isLeftAligned: true) - leftTitleInset = leftButtonSize.width + leftButtonInset + 1.0 - - var transition = transition - if self.leftButtonNode.frame.width.isZero { - transition = .immediate - } - - self.leftButtonNode.alpha = 1.0 - transition.updateFrame(node: self.leftButtonNode, frame: CGRect(origin: CGPoint(x: leftButtonInset, y: contentVerticalOrigin + floor((nominalHeight - leftButtonSize.height) / 2.0)), size: leftButtonSize)) - } - - let badgeSize = self.badgeNode.measure(CGSize(width: 200.0, height: 100.0)) - let backButtonArrowFrame = self.backButtonArrow.frame - transition.updateFrame(node: self.badgeNode, frame: CGRect(origin: backButtonArrowFrame.origin.offsetBy(dx: 16.0, dy: 2.0), size: badgeSize)) - - if self.rightButtonNode.supernode != nil { - let rightButtonSize = self.rightButtonNode.updateLayout(constrainedSize: (CGSize(width: size.width, height: nominalHeight)), isLandscape: isLandscape, isLeftAligned: false) - rightTitleInset = rightButtonSize.width + leftButtonInset + 1.0 - self.rightButtonNode.alpha = 1.0 - - var transition = transition - if self.rightButtonNode.frame.width.isZero || self.rightButtonNodeUpdated { - transition = .immediate - } - - transition.updateFrame(node: self.rightButtonNode, frame: CGRect(origin: CGPoint(x: size.width - leftButtonInset - rightButtonSize.width, y: contentVerticalOrigin + floor((nominalHeight - rightButtonSize.height) / 2.0)), size: rightButtonSize)) - } - self.rightButtonNodeUpdated = false - - if let transitionState = self.transitionState { - let progress = transitionState.progress - - switch transitionState.role { - case .top: - break - case .bottom: - if let transitionBackButtonNode = self.transitionBackButtonNode { - let transitionBackButtonSize = transitionBackButtonNode.updateLayout(constrainedSize: CGSize(width: size.width, height: nominalHeight), isLandscape: isLandscape, isLeftAligned: true) - let initialX: CGFloat = backButtonInset + size.width * 0.3 - let finalX: CGFloat = floor((size.width - transitionBackButtonSize.width) / 2.0) - - transitionBackButtonNode.frame = CGRect(origin: CGPoint(x: initialX * (1.0 - progress) + finalX * progress, y: contentVerticalOrigin + floor((nominalHeight - transitionBackButtonSize.height) / 2.0)), size: transitionBackButtonSize) - transitionBackButtonNode.alpha = (1.0 - progress) * (1.0 - progress) - } - - if let transitionBackArrowNode = self.transitionBackArrowNode { - let initialX: CGFloat = leftInset + 8.0 + size.width * 0.3 - let finalX: CGFloat = leftInset + 8.0 - - transitionBackArrowNode.frame = CGRect(origin: CGPoint(x: initialX * (1.0 - progress) + finalX * progress, y: contentVerticalOrigin + floor((nominalHeight - 22.0) / 2.0)), size: CGSize(width: 13.0, height: 22.0)) - transitionBackArrowNode.alpha = max(0.0, 1.0 - progress * 1.3) - - if let transitionBadgeNode = self.transitionBadgeNode { - transitionBadgeNode.frame = CGRect(origin: transitionBackArrowNode.frame.origin.offsetBy(dx: 16.0, dy: 2.0), size: transitionBadgeNode.bounds.size) - transitionBadgeNode.alpha = transitionBackArrowNode.alpha - } - } - } - } - - leftTitleInset = floor(leftTitleInset) - if Int(leftTitleInset) % 2 != 0 { - leftTitleInset -= 1.0 - } - - if let customHeaderContentView = self.customHeaderContentView { - let headerSize = CGSize(width: size.width, height: nominalHeight) - var customHeaderFrame = CGRect(origin: CGPoint(x: 0.0, y: contentVerticalOrigin), size: headerSize) - if appearsHidden { - customHeaderFrame.origin.y = -customHeaderFrame.height - } - transition.updateFrame(view: customHeaderContentView, frame: customHeaderFrame) - } - - if self.titleNode.supernode != nil { - let titleSize = self.titleNode.updateLayout(CGSize(width: max(1.0, size.width - max(leftTitleInset, rightTitleInset) * 2.0), height: nominalHeight)) - - if let transitionState = self.transitionState, let otherNavigationBar = transitionState.navigationBar { - let progress = transitionState.progress - - switch transitionState.role { - case .top: - let initialX = floor((size.width - titleSize.width) / 2.0) - let finalX: CGFloat = leftButtonInset - - self.titleNode.frame = CGRect(origin: CGPoint(x: initialX * (1.0 - progress) + finalX * progress, y: contentVerticalOrigin + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize) - self.titleNode.alpha = (1.0 - progress) * (1.0 - progress) - case .bottom: - var initialX: CGFloat = backButtonInset - if otherNavigationBar.backButtonNode.supernode != nil { - initialX += floor((otherNavigationBar.backButtonNode.frame.size.width - titleSize.width) / 2.0) - } - initialX += size.width * 0.3 - let finalX: CGFloat = floor((size.width - titleSize.width) / 2.0) - - self.titleNode.frame = CGRect(origin: CGPoint(x: initialX * (1.0 - progress) + finalX * progress, y: contentVerticalOrigin + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize) - self.titleNode.alpha = progress * progress - } - } else { - var transition = transition - if self.titleNode.frame.width.isZero { - transition = .immediate - } - self.titleNode.alpha = 1.0 - - var titleOffset: CGFloat = 0.0 - if self.presentationData.theme.backgroundColor == .clear && self.presentationData.theme.separatorColor == .clear { - titleOffset += 3.0 - } - transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: contentVerticalOrigin + titleOffset + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize)) - } - } - - if let titleView = self.titleView { - let titleSize = CGSize(width: max(1.0, size.width - max(leftTitleInset, rightTitleInset) * 2.0), height: nominalHeight) - let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: contentVerticalOrigin + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize) - var titleViewTransition = transition - if titleView.frame.isEmpty { - titleViewTransition = .immediate - titleView.frame = titleFrame - } - - titleViewTransition.updateFrame(view: titleView, frame: titleFrame) - - if let titleView = titleView as? NavigationBarTitleView { - let titleWidth = size.width - (leftTitleInset > 0.0 ? leftTitleInset : rightTitleInset) - (rightTitleInset > 0.0 ? rightTitleInset : leftTitleInset) - - let _ = titleView.updateLayout(size: titleFrame.size, clearBounds: CGRect(origin: CGPoint(x: leftTitleInset - titleFrame.minX, y: 0.0), size: CGSize(width: titleWidth, height: titleFrame.height)), transition: titleViewTransition) - } - - if let transitionState = self.transitionState, let otherNavigationBar = transitionState.navigationBar { - let progress = transitionState.progress - - switch transitionState.role { - case .top: - let initialX = floor((size.width - titleSize.width) / 2.0) - let finalX: CGFloat = leftButtonInset - - titleView.frame = CGRect(origin: CGPoint(x: initialX * (1.0 - progress) + finalX * progress, y: contentVerticalOrigin + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize) - titleView.alpha = (1.0 - progress) * (1.0 - progress) - case .bottom: - var initialX: CGFloat = backButtonInset - if otherNavigationBar.backButtonNode.supernode != nil { - initialX += floor((otherNavigationBar.backButtonNode.frame.size.width - titleSize.width) / 2.0) - } - initialX += size.width * 0.3 - let finalX: CGFloat = floor((size.width - titleSize.width) / 2.0) - - titleView.frame = CGRect(origin: CGPoint(x: initialX * (1.0 - progress) + finalX * progress, y: contentVerticalOrigin + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize) - titleView.alpha = progress * progress - } - } else { - if self.hintAnimateTitleNodeOnNextLayout { - self.hintAnimateTitleNodeOnNextLayout = false - if let titleView = titleView as? NavigationBarTitleView { - titleView.animateLayoutTransition() - } - } - titleView.alpha = 1.0 - transition.updateFrame(view: titleView, frame: titleFrame) - } - } - } - - public func makeTransitionTitleNode(foregroundColor: UIColor) -> ASDisplayNode? { - if let titleView = self.titleView { - if let transitionView = titleView as? NavigationBarTitleTransitionNode { - return transitionView.makeTransitionMirrorNode() - } else { - return nil - } - } else if let title = self.title { - let node = ImmediateTextNode() - node.attributedText = NSAttributedString(string: title, font: NavigationBar.titleFont, textColor: foregroundColor) - return node } else { return nil } } - - public func makeTransitionBackButtonNode(accentColor: UIColor) -> NavigationButtonNode? { - if self.backButtonNode.supernode != nil { - let node = NavigationButtonNode() - node.manualAlpha = self.backButtonNode.manualAlpha - node.updateManualText(self.backButtonNode.manualText) - node.color = accentColor - if let validLayout = self.validLayout { - let _ = node.updateLayout(constrainedSize: CGSize(width: validLayout.size.width, height: validLayout.defaultHeight), isLandscape: validLayout.isLandscape, isLeftAligned: true) - node.frame = self.backButtonNode.frame - } - return node - } else { - return nil - } - } - - public func makeTransitionBackButtonView(accentColor: UIColor) -> UIView? { - if self.backButtonNode.supernode != nil { - let node = NavigationButtonNode() - node.manualAlpha = self.backButtonNode.manualAlpha - node.updateManualText(self.backButtonNode.manualText) - node.color = accentColor - if let validLayout = self.validLayout { - let _ = node.updateLayout(constrainedSize: CGSize(width: validLayout.size.width, height: validLayout.defaultHeight), isLandscape: validLayout.isLandscape, isLeftAligned: true) - node.frame = self.backButtonNode.frame - } - return node.view - } else { - return nil - } - } - - public func makeTransitionRightButtonNode(accentColor: UIColor) -> NavigationButtonNode? { - if self.rightButtonNode.supernode != nil { - let node = NavigationButtonNode() - var items: [UIBarButtonItem] = [] - if let item = self.item { - if let rightBarButtonItems = item.rightBarButtonItems, !rightBarButtonItems.isEmpty { - items = rightBarButtonItems - } else if let rightBarButtonItem = item.rightBarButtonItem { - items = [rightBarButtonItem] - } - } - node.updateItems(items, animated: false) - node.color = accentColor - if let validLayout = self.validLayout { - let _ = node.updateLayout(constrainedSize: CGSize(width: validLayout.size.width, height: validLayout.defaultHeight), isLandscape: validLayout.isLandscape, isLeftAligned: false) - node.frame = self.backButtonNode.frame - } - return node - } else { - return nil - } - } - - public func makeTransitionBackArrowNode(accentColor: UIColor) -> ASDisplayNode? { - if self.backButtonArrow.supernode != nil { - let node = ASImageNode() - node.image = NavigationBar.backArrowImage(color: accentColor) - node.frame = self.backButtonArrow.frame - node.displayWithoutProcessing = true - node.displaysAsynchronously = false - return node - } else { - return nil - } - } - - public func makeTransitionBackArrowView(accentColor: UIColor) -> UIView? { - if self.backButtonArrow.supernode != nil { - let view = UIImageView() - view.image = NavigationBar.backArrowImage(color: accentColor) - view.frame = self.backButtonArrow.frame - return view - } else { - return nil - } - } - - public func makeTransitionBadgeNode() -> ASDisplayNode? { - if self.badgeNode.supernode != nil && !self.badgeNode.isHidden { - let node = NavigationBarBadgeNode(fillColor: self.presentationData.theme.buttonColor, strokeColor: self.presentationData.theme.buttonColor, textColor: self.presentationData.theme.badgeTextColor) - node.text = self.badgeNode.text - let nodeSize = node.measure(CGSize(width: 200.0, height: 100.0)) - node.frame = CGRect(origin: CGPoint(), size: nodeSize) - return node - } else { - return nil - } - } - - public var intrinsicCanTransitionInline: Bool = true - public var shouldTransitionInline: (() -> Bool)? - - public var passthroughTouches = true - - public var canTransitionInline: Bool { - if let contentNode = self.contentNode, case .replacement = contentNode.mode { - return false - } else { - if let shouldTransitionInline = self.shouldTransitionInline { - if !shouldTransitionInline() { - return false - } - } - return self.intrinsicCanTransitionInline - } - } - - public func contentHeight(defaultHeight: CGFloat) -> CGFloat { - var result: CGFloat = 0.0 - if let contentNode = self.contentNode { - switch contentNode.mode { - case .expansion: - result += defaultHeight + contentNode.height - case .replacement: - result += contentNode.height - } - } else { - result += defaultHeight - } - - if let _ = self.secondaryContentNode { - result += self.secondaryContentHeight * self.secondaryContentNodeDisplayFraction - } - - return result - } - - public func setContentNode(_ contentNode: NavigationBarContentNode?, animated: Bool) { - if let customSetContentNode = self.customSetContentNode { - customSetContentNode(contentNode, animated) - return - } - - if self.contentNode !== contentNode { - if let previous = self.contentNode { - if animated { - previous.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak self, weak previous] _ in - if let strongSelf = self, let previous = previous { - if previous !== strongSelf.contentNode { - previous.removeFromSupernode() - } - } - }) - } else { - previous.removeFromSupernode() - } - } - self.contentNode = contentNode - self.contentNode?.requestContainerLayout = { [weak self] transition in - self?.requestContainerLayout(transition) - } - if let contentNode = contentNode { - contentNode.clipsToBounds = true - contentNode.layer.removeAnimation(forKey: "opacity") - self.insertSubnode(contentNode, belowSubnode: self.stripeNode) - if animated { - contentNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) - } - - if case .replacement = contentNode.mode, !self.buttonsContainerNode.alpha.isZero { - self.buttonsContainerNode.alpha = 0.0 - if animated { - self.buttonsContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) - } - } - - if !self.bounds.size.width.isZero { - self.requestedLayout = true - self.layout() - } else { - self.requestLayout() - } - } else if self.buttonsContainerNode.alpha.isZero { - self.buttonsContainerNode.alpha = 1.0 - if animated { - self.buttonsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) - } - } - } - } - - public func setSecondaryContentNode(_ secondaryContentNode: ASDisplayNode?, animated: Bool = false) { - if self.secondaryContentNode !== secondaryContentNode { - if let previous = self.secondaryContentNode, previous.supernode === self.clippingNode { - if animated { - previous.layer.animateAlpha(from: previous.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak previous] finished in - if finished { - previous?.removeFromSupernode() - previous?.layer.removeAllAnimations() - } - }) - } else { - previous.removeFromSupernode() - } - } - self.secondaryContentNode = secondaryContentNode - if let secondaryContentNode = secondaryContentNode { - self.clippingNode.addSubnode(secondaryContentNode) - - if animated { - secondaryContentNode.layer.animateAlpha(from: 0.0, to: secondaryContentNode.alpha, duration: 0.3) - } - } - } - } - - public func executeBack() -> Bool { - if self.backButtonNode.isInHierarchy { - self.backButtonNode.pressed(0) - } else if self.leftButtonNode.isInHierarchy { - self.leftButtonNode.pressed(0) - } else { - self.backButtonNode.pressed(0) - } - return true - } - - public func setHidden(_ hidden: Bool, animated: Bool) { - if let contentNode = self.contentNode, case .replacement = contentNode.mode { - } else { - let targetAlpha: CGFloat = hidden ? 0.0 : 1.0 - let previousAlpha = self.buttonsContainerNode.alpha - if previousAlpha != targetAlpha { - self.buttonsContainerNode.alpha = targetAlpha - if animated { - self.buttonsContainerNode.layer.animateAlpha(from: previousAlpha, to: targetAlpha, duration: 0.2) - } - } - } - } - - override open func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - if let result = self.additionalContentNode.view.hitTest(self.view.convert(point, to: self.additionalContentNode.view), with: event) { - return result - } - - if self.frame.minY > -10.0, let customHeaderContentView = self.customHeaderContentView, let result = customHeaderContentView.hitTest(self.view.convert(point, to: customHeaderContentView), with: event) { - return result - } - - guard let result = super.hitTest(point, with: event) else { - return nil - } - - if self.passthroughTouches && (result == self.view || result == self.buttonsContainerNode.view) { - return nil - } - - return result - } } + +public protocol NavigationButtonCustomDisplayNode { + var isHighlightable: Bool { get } +} + +public protocol NavigationButtonNode: ASDisplayNode { + func updateManualAlpha(alpha: CGFloat, transition: ContainedViewLayoutTransition) + var mainContentNode: ASDisplayNode? { get } + var contentsColor: UIColor? { get set } +} + +public protocol NavigationBar: ASDisplayNode { + typealias Style = NavigationBarStyle + + var backPressed: () -> Void { get set } + + var userInfo: Any? { get set } + var makeCustomTransitionNode: ((NavigationBar, Bool) -> CustomNavigationTransitionNode?)? { get set } + var allowsCustomTransition: (() -> Bool)? { get set } + + var stripeNode: ASDisplayNode { get } + var clippingNode: SparseNode { get } + + var backgroundView: UIView { get } + var customOverBackgroundContentView: UIView { get } + var contentNode: NavigationBarContentNode? { get } + var secondaryContentNode: ASDisplayNode? { get } + var secondaryContentNodeDisplayFraction: CGFloat { get set } + + var item: UINavigationItem? { get set } + var customBackButtonText: String? { get } + var titleView: UIView? { get } + var layoutSuspended: Bool { get set } + + var enableAutomaticBackButton: Bool { get set } + var previousItem: NavigationPreviousAction? { get set } + + var backgroundNode: NavigationBackgroundNode { get } + var backButtonNode: NavigationButtonNode { get } + var badgeNode: NavigationBarBadgeNode { get } + var backButtonArrow: ASImageNode { get } + var leftButtonNode: NavigationButtonNode { get } + var rightButtonNode: NavigationButtonNode { get } + var additionalContentNode: SparseNode { get } + + func reattachAdditionalContentNode() + + var secondaryContentHeight: CGFloat { get set } + + var isBackgroundVisible: Bool { get } + + func updateBackgroundAlpha(_ alpha: CGFloat, transition: ContainedViewLayoutTransition) + func updatePresentationData(_ presentationData: NavigationBarPresentationData, transition: ContainedViewLayoutTransition) + + var intrinsicCanTransitionInline: Bool { get set } + + var passthroughTouches: Bool { get set } + + var canTransitionInline: Bool { get } + + func contentHeight(defaultHeight: CGFloat) -> CGFloat + func setContentNode(_ contentNode: NavigationBarContentNode?, animated: Bool) + func setSecondaryContentNode(_ secondaryContentNode: ASDisplayNode?, animated: Bool) + func executeBack() -> Bool + func setHidden(_ hidden: Bool, animated: Bool) + + var requestContainerLayout: ((ContainedViewLayoutTransition) -> Void)? { get set } + + func updateLayout(size: CGSize, defaultHeight: CGFloat, additionalTopHeight: CGFloat, additionalContentHeight: CGFloat, additionalBackgroundHeight: CGFloat, additionalCutout: CGSize?, leftInset: CGFloat, rightInset: CGFloat, appearsHidden: Bool, isLandscape: Bool, transition: ContainedViewLayoutTransition) + + func updateEdgeEffectExtension(value: CGFloat, transition: ContainedViewLayoutTransition) +} + +public var defaultNavigationBarImpl: ((NavigationBarPresentationData) -> NavigationBar)? diff --git a/submodules/Display/Source/NavigationBarBadge.swift b/submodules/Display/Source/NavigationBarBadge.swift index f1236dcc..734eda03 100644 --- a/submodules/Display/Source/NavigationBarBadge.swift +++ b/submodules/Display/Source/NavigationBarBadge.swift @@ -12,7 +12,7 @@ public final class NavigationBarBadgeNode: ASDisplayNode { private let font: UIFont = Font.regular(13.0) - var text: String = "" { + public var text: String = "" { didSet { self.textNode.attributedText = NSAttributedString(string: self.text, font: self.font, textColor: self.textColor) self.invalidateCalculatedLayout() @@ -39,7 +39,7 @@ public final class NavigationBarBadgeNode: ASDisplayNode { self.addSubnode(self.textNode) } - func updateTheme(fillColor: UIColor, strokeColor: UIColor, textColor: UIColor) { + public func updateTheme(fillColor: UIColor, strokeColor: UIColor, textColor: UIColor) { self.fillColor = fillColor self.strokeColor = strokeColor self.textColor = textColor diff --git a/submodules/Display/Source/NavigationBarContentNode.swift b/submodules/Display/Source/NavigationBarContentNode.swift index 76862873..6a165a64 100644 --- a/submodules/Display/Source/NavigationBarContentNode.swift +++ b/submodules/Display/Source/NavigationBarContentNode.swift @@ -26,6 +26,7 @@ open class NavigationBarContentNode: ASDisplayNode { return .replacement } - open func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { + open func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { + return size } } diff --git a/submodules/Display/Source/NavigationBarTitleView.swift b/submodules/Display/Source/NavigationBarTitleView.swift index 408cfb2f..c843151d 100644 --- a/submodules/Display/Source/NavigationBarTitleView.swift +++ b/submodules/Display/Source/NavigationBarTitleView.swift @@ -1,8 +1,10 @@ import Foundation import UIKit -public protocol NavigationBarTitleView { +public protocol NavigationBarTitleView: UIView { + var requestUpdate: ((ContainedViewLayoutTransition) -> Void)? { get set } + func animateLayoutTransition() - func updateLayout(size: CGSize, clearBounds: CGRect, transition: ContainedViewLayoutTransition) -> CGRect + func updateLayout(availableSize: CGSize, transition: ContainedViewLayoutTransition) -> CGSize } diff --git a/submodules/Display/Source/NavigationTransitionCoordinator.swift b/submodules/Display/Source/NavigationTransitionCoordinator.swift index c6167746..707bd537 100644 --- a/submodules/Display/Source/NavigationTransitionCoordinator.swift +++ b/submodules/Display/Source/NavigationTransitionCoordinator.swift @@ -201,10 +201,9 @@ final class NavigationTransitionCoordinator { position = progress } - transition.animateView { - topNavigationBar.transitionState = NavigationBarTransitionState(navigationBar: bottomNavigationBar, transition: self.transition, role: .top, progress: position) - bottomNavigationBar.transitionState = NavigationBarTransitionState(navigationBar: topNavigationBar, transition: self.transition, role: .bottom, progress: position) - } + let _ = position + let _ = topNavigationBar + let _ = bottomNavigationBar } } @@ -218,15 +217,16 @@ final class NavigationTransitionCoordinator { position = progress } - topNavigationBar.transitionState = NavigationBarTransitionState(navigationBar: bottomNavigationBar, transition: self.transition, role: .top, progress: position) - bottomNavigationBar.transitionState = NavigationBarTransitionState(navigationBar: topNavigationBar, transition: self.transition, role: .bottom, progress: position) + let _ = position + let _ = topNavigationBar + let _ = bottomNavigationBar } } func endNavigationBarTransition() { if let topNavigationBar = self.topNavigationBar, let bottomNavigationBar = self.bottomNavigationBar, self.inlineNavigationBarTransition { - topNavigationBar.transitionState = nil - bottomNavigationBar.transitionState = nil + let _ = topNavigationBar + let _ = bottomNavigationBar } } diff --git a/submodules/Display/Source/SparseContainerView.swift b/submodules/Display/Source/SparseContainerView.swift new file mode 100644 index 00000000..e9f29977 --- /dev/null +++ b/submodules/Display/Source/SparseContainerView.swift @@ -0,0 +1,47 @@ +import Foundation +import UIKit +import AsyncDisplayKit + +open class SparseNode: ASDisplayNode { + override open func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if self.alpha.isZero { + return nil + } + for view in self.view.subviews.reversed() { + if let result = view.hitTest(self.view.convert(point, to: view), with: event), result.isUserInteractionEnabled { + return result + } + } + + if !self.bounds.inset(by: self.hitTestSlop).contains(point) { + return nil + } + + let result = super.hitTest(point, with: event) + if result != self.view { + return result + } else { + return nil + } + } +} + +open class SparseContainerView: UIView { + override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if self.alpha.isZero { + return nil + } + for view in self.subviews.reversed() { + if let result = view.hitTest(self.convert(point, to: view), with: event), result.isUserInteractionEnabled { + return result + } + } + + let result = super.hitTest(point, with: event) + if result != self { + return result + } else { + return nil + } + } +} diff --git a/submodules/Display/Source/UIKitUtils.swift b/submodules/Display/Source/UIKitUtils.swift index 130accaf..23a90c4c 100644 --- a/submodules/Display/Source/UIKitUtils.swift +++ b/submodules/Display/Source/UIKitUtils.swift @@ -108,9 +108,13 @@ public extension UIColor { var green: CGFloat = 0.0 var blue: CGFloat = 0.0 if self.getRed(&red, green: &green, blue: &blue, alpha: nil) { - return (UInt32(max(0.0, red) * 255.0) << 16) | (UInt32(max(0.0, green) * 255.0) << 8) | (UInt32(max(0.0, blue) * 255.0)) + let r: UInt32 = UInt32(max(0.0, red) * 255.0) + let g: UInt32 = UInt32(max(0.0, green) * 255.0) + let b: UInt32 = UInt32(max(0.0, blue) * 255.0) + return (r << 16) | (g << 8) | b } else if self.getWhite(&red, alpha: nil) { - return (UInt32(max(0.0, red) * 255.0) << 16) | (UInt32(max(0.0, red) * 255.0) << 8) | (UInt32(max(0.0, red) * 255.0)) + let w: UInt32 = UInt32(max(0.0, red) * 255.0) + return (w << 16) | (w << 8) | w } else { return 0 } @@ -122,9 +126,15 @@ public extension UIColor { var blue: CGFloat = 0.0 var alpha: CGFloat = 0.0 if self.getRed(&red, green: &green, blue: &blue, alpha: &alpha) { - return (UInt32(alpha * 255.0) << 24) | (UInt32(max(0.0, red) * 255.0) << 16) | (UInt32(max(0.0, green) * 255.0) << 8) | (UInt32(max(0.0, blue) * 255.0)) + let a: UInt32 = UInt32(alpha * 255.0) + let r: UInt32 = UInt32(max(0.0, red) * 255.0) + let g: UInt32 = UInt32(max(0.0, green) * 255.0) + let b: UInt32 = UInt32(max(0.0, blue) * 255.0) + return (a << 24) | (r << 16) | (g << 8) | b } else if self.getWhite(&red, alpha: &alpha) { - return (UInt32(max(0.0, alpha) * 255.0) << 24) | (UInt32(max(0.0, red) * 255.0) << 16) | (UInt32(max(0.0, red) * 255.0) << 8) | (UInt32(max(0.0, red) * 255.0)) + let a: UInt32 = UInt32(max(0.0, alpha) * 255.0) + let w: UInt32 = UInt32(max(0.0, red) * 255.0) + return (a << 24) | (w << 16) | (w << 8) | w } else { return 0 } diff --git a/submodules/Display/Source/ViewController.swift b/submodules/Display/Source/ViewController.swift index 593005a7..a1ca2547 100644 --- a/submodules/Display/Source/ViewController.swift +++ b/submodules/Display/Source/ViewController.swift @@ -217,6 +217,18 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject { open var navigationBarRequiresEntireLayoutUpdate: Bool { return true } + + public struct TabBarSearchState: Equatable { + public var isActive: Bool + + public init(isActive: Bool) { + self.isActive = isActive + } + } + + public private(set) var tabBarSearchState: TabBarSearchState? + public var tabBarSearchStateUpdated: ((ContainedViewLayoutTransition) -> Void)? + public var currentTabBarSearchNode: (() -> ASDisplayNode?)? private weak var activeInputViewCandidate: UIResponder? private weak var activeInputView: UIResponder? @@ -237,16 +249,24 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject { open var interactiveNavivationGestureEdgeWidth: InteractiveTransitionGestureRecognizerEdgeWidth? { return nil } + + open var navigationEdgeEffectExtension: CGFloat { + return 0.0 + } + + public func updateNavigationEdgeEffectExtension(transition: ContainedViewLayoutTransition) { + if let navigationBar = self.navigationBar { + navigationBar.updateEdgeEffectExtension(value: max(0.0, self.navigationEdgeEffectExtension - navigationBar.frame.maxY), transition: transition) + } + } open func navigationLayout(layout: ContainerViewLayout) -> NavigationLayout { let statusBarHeight: CGFloat = layout.statusBarHeight ?? 0.0 var defaultNavigationBarHeight: CGFloat if self._presentedInModal && self._hasGlassStyle { - defaultNavigationBarHeight = 66.0 - } else if self._presentedInModal && layout.orientation == .portrait { - defaultNavigationBarHeight = 56.0 + defaultNavigationBarHeight = 68.0 } else { - defaultNavigationBarHeight = 44.0 + defaultNavigationBarHeight = 60.0 } let navigationBarHeight: CGFloat = statusBarHeight + (self.navigationBar?.contentHeight(defaultHeight: defaultNavigationBarHeight) ?? defaultNavigationBarHeight) @@ -375,7 +395,7 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject { public init(navigationBarPresentationData: NavigationBarPresentationData?) { self.statusBar = StatusBar() if let navigationBarPresentationData = navigationBarPresentationData { - self.navigationBar = NavigationBar(presentationData: navigationBarPresentationData) + self.navigationBar = defaultNavigationBarImpl!(navigationBarPresentationData) } else { self.navigationBar = nil } @@ -447,14 +467,29 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject { } if let navigationBar = self.navigationBar { if let contentNode = navigationBar.contentNode, case .expansion = contentNode.mode, !self.displayNavigationBar { - navigationBarFrame.origin.y -= navigationLayout.defaultContentHeight - navigationBarFrame.size.height += contentNode.height + navigationLayout.defaultContentHeight + statusBarHeight + navigationBarFrame.origin.y -= navigationLayout.defaultContentHeight + statusBarHeight + navigationBarFrame.size.height += contentNode.height + navigationLayout.defaultContentHeight + statusBarHeight * 2.0 + if self._presentedInModal && self._hasGlassStyle { + navigationBarFrame.size.height += 8.0 + } } + //navigationBar.backgroundColor = .blue if let _ = navigationBar.contentNode, let _ = navigationBar.secondaryContentNode, !self.displayNavigationBar { navigationBarFrame.size.height += navigationBar.secondaryContentHeight } - navigationBar.updateLayout(size: navigationBarFrame.size, defaultHeight: navigationLayout.defaultContentHeight, additionalTopHeight: statusBarHeight, additionalContentHeight: self.additionalNavigationBarHeight, additionalBackgroundHeight: additionalBackgroundHeight, additionalCutout: additionalCutout, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, appearsHidden: !self.displayNavigationBar, isLandscape: isLandscape, transition: transition) + var additionalTopHeight = statusBarHeight + if !self.displayNavigationBar { + additionalTopHeight -= statusBarHeight + if statusBarHeight != 0.0 { + additionalTopHeight += 6.0 + } + } + if self._presentedInModal && self._hasGlassStyle { + additionalTopHeight += 8.0 + } + + navigationBar.updateLayout(size: navigationBarFrame.size, defaultHeight: navigationLayout.defaultContentHeight, additionalTopHeight: additionalTopHeight, additionalContentHeight: self.additionalNavigationBarHeight, additionalBackgroundHeight: additionalBackgroundHeight, additionalCutout: additionalCutout, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, appearsHidden: !self.displayNavigationBar, isLandscape: isLandscape, transition: transition) if !transition.isAnimated { navigationBar.layer.removeAnimation(forKey: "bounds") navigationBar.layer.removeAnimation(forKey: "position") @@ -542,14 +577,14 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject { UIView.transition(with: navigationBar.view, duration: 0.3, options: [.transitionCrossDissolve], animations: { }, completion: nil) } - self.navigationBar?.updatePresentationData(presentationData) + self.navigationBar?.updatePresentationData(presentationData, transition: .immediate) if let parent = self.parent as? TabBarController { if parent.currentController === self { if animated, let navigationBar = parent.navigationBar { UIView.transition(with: navigationBar.view, duration: 0.3, options: [.transitionCrossDissolve], animations: { }, completion: nil) } - parent.navigationBar?.updatePresentationData(presentationData) + parent.navigationBar?.updatePresentationData(presentationData, transition: .immediate) } } } @@ -705,9 +740,22 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject { open func tabBarDisabledAction() { } + + open func tabBarActivateSearch() { + } + + open func tabBarDeactivateSearch() { + } open func tabBarItemSwipeAction(direction: TabBarItemSwipeDirection) { } + + public func updateTabBarSearchState(_ tabBarSearchState: TabBarSearchState?, transition: ContainedViewLayoutTransition) { + if self.tabBarSearchState != tabBarSearchState { + self.tabBarSearchState = tabBarSearchState + self.tabBarSearchStateUpdated?(transition) + } + } open func updatePossibleControllerDropContent(content: NavigationControllerDropContent?) { } diff --git a/submodules/FeaturedStickersScreen/Sources/FeaturedStickersScreen.swift b/submodules/FeaturedStickersScreen/Sources/FeaturedStickersScreen.swift index 25d2f27a..9ed63103 100644 --- a/submodules/FeaturedStickersScreen/Sources/FeaturedStickersScreen.swift +++ b/submodules/FeaturedStickersScreen/Sources/FeaturedStickersScreen.swift @@ -919,7 +919,7 @@ public final class FeaturedStickersScreen: ViewController { private func updatePresentationData() { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData), transition: .immediate) self.searchNavigationNode?.updatePresentationData(theme: self.presentationData.theme, strings: self.presentationData.strings) @@ -980,7 +980,7 @@ private final class SearchNavigationContentNode: NavigationBarContentNode { self.cancel = cancel - self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: theme), strings: strings, fieldStyle: .modern, cancelText: strings.Common_Done) + self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: theme), presentationTheme: theme, strings: strings, fieldStyle: .modern, cancelText: strings.Common_Done) let placeholderText = placeholder?(strings) ?? strings.Common_Search let searchBarFont = Font.regular(17.0) @@ -1004,7 +1004,7 @@ private final class SearchNavigationContentNode: NavigationBarContentNode { self.theme = theme self.strings = strings - self.searchBar.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: theme), strings: strings) + self.searchBar.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: theme), presentationTheme: theme, strings: strings) } func setQueryUpdated(_ f: @escaping (String, String?) -> Void) { @@ -1019,10 +1019,12 @@ private final class SearchNavigationContentNode: NavigationBarContentNode { return 54.0 } - override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { + override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { let searchBarFrame = CGRect(origin: CGPoint(x: 0.0, y: 1.0 + size.height - self.nominalHeight), size: CGSize(width: size.width, height: 54.0)) self.searchBar.frame = searchBarFrame self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition) + + return size } func activate() { diff --git a/submodules/GalleryUI/Sources/GalleryController.swift b/submodules/GalleryUI/Sources/GalleryController.swift index d8af3d8f..e36181f6 100644 --- a/submodules/GalleryUI/Sources/GalleryController.swift +++ b/submodules/GalleryUI/Sources/GalleryController.swift @@ -576,8 +576,8 @@ private func galleryEntriesForMessageHistoryEntries(_ entries: [MessageHistoryEn } public class GalleryController: ViewController, StandalonePresentableController, KeyShortcutResponder, GalleryControllerProtocol { - public static let darkNavigationTheme = NavigationBarTheme(buttonColor: .white, disabledButtonColor: UIColor(rgb: 0x525252), primaryTextColor: .white, backgroundColor: UIColor(white: 0.0, alpha: 0.6), enableBackgroundBlur: false, separatorColor: UIColor(white: 0.0, alpha: 0.8), badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) - public static let lightNavigationTheme = NavigationBarTheme(buttonColor: UIColor(rgb: 0x0088ff), disabledButtonColor: UIColor(rgb: 0xd0d0d0), primaryTextColor: .black, backgroundColor: UIColor(red: 0.968626451, green: 0.968626451, blue: 0.968626451, alpha: 1.0), enableBackgroundBlur: false, separatorColor: UIColor(red: 0.6953125, green: 0.6953125, blue: 0.6953125, alpha: 1.0), badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) + public static let darkNavigationTheme = NavigationBarTheme(overallDarkAppearance: true, buttonColor: .white, disabledButtonColor: UIColor(rgb: 0x525252), primaryTextColor: .white, backgroundColor: UIColor(white: 0.0, alpha: 0.6), enableBackgroundBlur: false, separatorColor: UIColor(white: 0.0, alpha: 0.8), badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) + public static let lightNavigationTheme = NavigationBarTheme(overallDarkAppearance: false, buttonColor: UIColor(rgb: 0x0088ff), disabledButtonColor: UIColor(rgb: 0xd0d0d0), primaryTextColor: .black, backgroundColor: UIColor(red: 0.968626451, green: 0.968626451, blue: 0.968626451, alpha: 1.0), enableBackgroundBlur: false, separatorColor: UIColor(red: 0.6953125, green: 0.6953125, blue: 0.6953125, alpha: 1.0), badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) private var galleryNode: GalleryControllerNode { return self.displayNode as! GalleryControllerNode @@ -942,12 +942,12 @@ public class GalleryController: ViewController, StandalonePresentableController, switch style { case .dark: strongSelf.statusBar.statusBarStyle = .White - strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: GalleryController.darkNavigationTheme, strings: NavigationBarStrings(presentationStrings: strongSelf.presentationData.strings))) + strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: GalleryController.darkNavigationTheme, strings: NavigationBarStrings(presentationStrings: strongSelf.presentationData.strings)), transition: .immediate) strongSelf.galleryNode.backgroundNode.backgroundColor = UIColor.black strongSelf.galleryNode.isBackgroundExtendedOverNavigationBar = true case .light: strongSelf.statusBar.statusBarStyle = .Black - strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: GalleryController.darkNavigationTheme, strings: NavigationBarStrings(presentationStrings: strongSelf.presentationData.strings))) + strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: GalleryController.darkNavigationTheme, strings: NavigationBarStrings(presentationStrings: strongSelf.presentationData.strings)), transition: .immediate) strongSelf.galleryNode.backgroundNode.backgroundColor = UIColor(rgb: 0xbdbdc2) strongSelf.galleryNode.isBackgroundExtendedOverNavigationBar = false } diff --git a/submodules/GalleryUI/Sources/GalleryTitleView.swift b/submodules/GalleryUI/Sources/GalleryTitleView.swift index 8fd42e7c..bfae2810 100644 --- a/submodules/GalleryUI/Sources/GalleryTitleView.swift +++ b/submodules/GalleryUI/Sources/GalleryTitleView.swift @@ -14,6 +14,8 @@ final class GalleryTitleView: UIView, NavigationBarTitleView { private let authorNameNode: ASTextNode private let dateNode: ASTextNode + var requestUpdate: ((ContainedViewLayoutTransition) -> Void)? + override init(frame: CGRect) { self.authorNameNode = ASTextNode() self.authorNameNode.displaysAsynchronously = false @@ -41,7 +43,9 @@ final class GalleryTitleView: UIView, NavigationBarTitleView { self.dateNode.attributedText = NSAttributedString(string: dateText, font: dateFont, textColor: .white) } - func updateLayout(size: CGSize, clearBounds: CGRect, transition: ContainedViewLayoutTransition) -> CGRect { + func updateLayout(availableSize: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { + let size = availableSize + let leftInset: CGFloat = 0.0 let rightInset: CGFloat = 0.0 @@ -56,7 +60,7 @@ final class GalleryTitleView: UIView, NavigationBarTitleView { self.dateNode.frame = CGRect(origin: CGPoint(x: floor((size.width - dateSize.width) / 2.0), y: floor((size.height - dateSize.height - authorNameSize.height - labelsSpacing) / 2.0) + authorNameSize.height + labelsSpacing), size: dateSize) } - return CGRect() + return availableSize } func animateLayoutTransition() { diff --git a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift index 43b4fc92..48fea8c8 100644 --- a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift @@ -1651,7 +1651,7 @@ private class ImageRecognitionOverlayContentNode: GalleryOverlayContentNode { if isHidden && !self.buttonNode.isSelected { buttonPosition = CGPoint(x: size.width - insets.right - buttonSize.width - 59.0, y: -50.0) } else { - buttonPosition = CGPoint(x: size.width - insets.right - buttonSize.width - (self.buttonNode.isSelected ? 24.0 : 59.0), y: insets.top - 50.0) + buttonPosition = CGPoint(x: size.width - insets.right - buttonSize.width - (self.buttonNode.isSelected ? 24.0 : 59.0), y: insets.top - 58.0) } transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: buttonPosition, size: CGSize(width: buttonSize.width + 24.0, height: buttonSize.height + 24.0))) diff --git a/submodules/HashtagSearchUI/BUILD b/submodules/HashtagSearchUI/BUILD index eecdc4a8..a7010192 100644 --- a/submodules/HashtagSearchUI/BUILD +++ b/submodules/HashtagSearchUI/BUILD @@ -32,6 +32,8 @@ swift_library( "//submodules/TelegramUI/Components/AnimatedTextComponent", "//submodules/Components/BlurredBackgroundComponent", "//submodules/UIKitRuntimeUtils", + "//submodules/TelegramUI/Components/HorizontalTabsComponent", + "//submodules/TelegramUI/Components/GlassBackgroundComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift index 81195028..bc86c96b 100644 --- a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift +++ b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift @@ -59,7 +59,7 @@ public final class HashtagSearchController: TelegramBaseController { } self.presentationData = presentationData - super.init(context: context, navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), mediaAccessoryPanelVisibility: .specific(size: .compact), locationBroadcastPanelSource: .none, groupCallPanelSource: .none) + super.init(context: context, navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData, style: .glass)) self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style @@ -87,7 +87,7 @@ public final class HashtagSearchController: TelegramBaseController { if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData, style: .glass), transition: .immediate) self.controllerNode.updatePresentationData(self.presentationData) } } diff --git a/submodules/HashtagSearchUI/Sources/HashtagSearchNavigationContentNode.swift b/submodules/HashtagSearchUI/Sources/HashtagSearchNavigationContentNode.swift index 1a7d6f3e..8390c824 100644 --- a/submodules/HashtagSearchUI/Sources/HashtagSearchNavigationContentNode.swift +++ b/submodules/HashtagSearchUI/Sources/HashtagSearchNavigationContentNode.swift @@ -8,7 +8,8 @@ import TelegramPresentationData import SearchBarNode import ComponentFlow import ComponentDisplayAdapters -import TabSelectorComponent +import HorizontalTabsComponent +import GlassBackgroundComponent private let searchBarFont = Font.regular(17.0) @@ -23,6 +24,9 @@ final class HashtagSearchNavigationContentNode: NavigationBarContentNode { var onReturn: (String) -> Void = { _ in } private let searchBar: SearchBarNode + + private let tabsBackgroundContainer: GlassBackgroundContainerView + private let tabsBackgroundView: GlassBackgroundView private let tabSelector = ComponentView() private var queryUpdated: ((String) -> Void)? @@ -31,7 +35,7 @@ final class HashtagSearchNavigationContentNode: NavigationBarContentNode { var selectedIndex: Int = 0 { didSet { if let (size, leftInset, rightInset) = self.validLayout { - self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .animated(duration: 0.35, curve: .spring)) + let _ = self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .animated(duration: 0.35, curve: .spring)) } } } @@ -40,7 +44,7 @@ final class HashtagSearchNavigationContentNode: NavigationBarContentNode { didSet { if self.transitionFraction != oldValue { if let (size, leftInset, rightInset) = self.validLayout { - self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: self.transitionFraction == nil ? .animated(duration: 0.35, curve: .spring) : .immediate) + let _ = self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: self.transitionFraction == nil ? .animated(duration: 0.35, curve: .spring) : .immediate) } } } @@ -79,12 +83,18 @@ final class HashtagSearchNavigationContentNode: NavigationBarContentNode { var initialQuery = initialQuery initialQuery.removeFirst() - self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: theme, hasSeparator: false), strings: strings, fieldStyle: .modern, icon: icon, displayBackground: false) + self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: theme, hasSeparator: false), presentationTheme: theme, strings: strings, fieldStyle: .glass, icon: icon, displayBackground: false) self.searchBar.text = initialQuery self.searchBar.placeholderString = NSAttributedString(string: strings.HashtagSearch_SearchPlaceholder, font: searchBarFont, textColor: theme.rootController.navigationSearchBar.inputPlaceholderTextColor) + self.tabsBackgroundContainer = GlassBackgroundContainerView() + self.tabsBackgroundView = GlassBackgroundView() + super.init() + self.tabsBackgroundContainer.contentView.addSubview(self.tabsBackgroundView) + self.view.addSubview(self.tabsBackgroundContainer) + self.searchBar.autocapitalization = .none if hasCurrentChat { @@ -111,7 +121,7 @@ final class HashtagSearchNavigationContentNode: NavigationBarContentNode { func updateTheme(_ theme: PresentationTheme) { self.theme = theme - self.searchBar.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: theme, hasSeparator: false), strings: self.strings) + self.searchBar.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: theme, hasSeparator: false), presentationTheme: theme, strings: self.strings) } func setQueryUpdated(_ f: @escaping (String) -> Void) { @@ -120,7 +130,7 @@ final class HashtagSearchNavigationContentNode: NavigationBarContentNode { override var nominalHeight: CGFloat { if self.hasCurrentChat { - return 54.0 + 44.0 + return 64.0 + 44.0 } else { return 45.0 } @@ -128,58 +138,98 @@ final class HashtagSearchNavigationContentNode: NavigationBarContentNode { private var validLayout: (CGSize, CGFloat, CGFloat)? - override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { + override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { self.validLayout = (size, leftInset, rightInset) let sideInset: CGFloat = 6.0 - let searchBarFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - self.nominalHeight + 5.0), size: CGSize(width: size.width, height: 54.0)) + let searchBarFrame = CGRect(origin: CGPoint(x: 0.0, y: 6.0), size: CGSize(width: size.width, height: 44.0)) self.searchBar.frame = searchBarFrame self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: leftInset + sideInset, rightInset: rightInset + sideInset, transition: transition) if self.hasTabs { - var items: [TabSelectorComponent.Item] = [] + var items: [HorizontalTabsComponent.Tab] = [] if self.hasCurrentChat { - items.append(TabSelectorComponent.Item(id: AnyHashable(0), title: self.strings.HashtagSearch_ThisChat)) + items.append(HorizontalTabsComponent.Tab( + id: AnyHashable(0), + content: .title(HorizontalTabsComponent.Tab.Title(text: self.strings.HashtagSearch_ThisChat, entities: [], enableAnimations: false)), + badge: nil, + action: { [weak self] in + guard let self else { + return + } + self.indexUpdated?(0) + }, + contextAction: nil, + deleteAction: nil + )) } - items.append(TabSelectorComponent.Item(id: AnyHashable(1), title: self.strings.HashtagSearch_MyMessages)) - items.append(TabSelectorComponent.Item(id: AnyHashable(2), title: self.strings.HashtagSearch_PublicPosts)) + + items.append(HorizontalTabsComponent.Tab( + id: AnyHashable(1), + content: .title(HorizontalTabsComponent.Tab.Title(text: self.strings.HashtagSearch_MyMessages, entities: [], enableAnimations: false)), + badge: nil, + action: { [weak self] in + guard let self else { + return + } + self.indexUpdated?(1) + }, + contextAction: nil, + deleteAction: nil + )) + + items.append(HorizontalTabsComponent.Tab( + id: AnyHashable(2), + content: .title(HorizontalTabsComponent.Tab.Title(text: self.strings.HashtagSearch_PublicPosts, entities: [], enableAnimations: false)), + badge: nil, + action: { [weak self] in + guard let self else { + return + } + self.indexUpdated?(2) + }, + contextAction: nil, + deleteAction: nil + )) let tabSelectorSize = self.tabSelector.update( transition: ComponentTransition(transition), - component: AnyComponent(TabSelectorComponent( - colors: TabSelectorComponent.Colors( - foreground: self.theme.list.itemSecondaryTextColor, - selection: self.theme.list.itemAccentColor - ), + component: AnyComponent(HorizontalTabsComponent( + context: nil, theme: self.theme, - customLayout: TabSelectorComponent.CustomLayout( - font: Font.medium(14.0), - spacing: self.hasCurrentChat ? 24.0 : 8.0, - lineSelection: true - ), - items: items, - selectedId: AnyHashable(self.selectedIndex), - setSelectedId: { [weak self] id in - guard let self, let index = id.base as? Int else { - return - } - self.indexUpdated?(index) - }, - transitionFraction: self.transitionFraction + tabs: items, + selectedTab: AnyHashable(self.selectedIndex), + isEditing: false )), environment: {}, - containerSize: CGSize(width: size.width, height: 44.0) + containerSize: CGSize(width: size.width - (leftInset + 16.0) * 2.0, height: 44.0) ) let tabSelectorFrameOriginX = floorToScreenPixels((size.width - tabSelectorSize.width) / 2.0) - let tabSelectorFrame = CGRect(origin: CGPoint(x: tabSelectorFrameOriginX, y: size.height - tabSelectorSize.height - 10.0), size: tabSelectorSize) - if let tabSelectorView = self.tabSelector.view { + let tabSelectorFrame = CGRect(origin: CGPoint(x: tabSelectorFrameOriginX, y: searchBarFrame.maxY + 10.0), size: tabSelectorSize) + + transition.updateFrame(view: self.tabsBackgroundContainer, frame: tabSelectorFrame) + self.tabsBackgroundContainer.update(size: tabSelectorFrame.size, isDark: self.theme.overallDarkAppearance, transition: ComponentTransition(transition)) + + transition.updateFrame(view: self.tabsBackgroundView, frame: CGRect(origin: CGPoint(), size: tabSelectorFrame.size)) + self.tabsBackgroundView.update(size: tabSelectorFrame.size, cornerRadius: tabSelectorFrame.height * 0.5, isDark: self.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), transition: ComponentTransition(transition)) + + if let tabSelectorView = self.tabSelector.view as? HorizontalTabsComponent.View { if tabSelectorView.superview == nil { - self.view.addSubview(tabSelectorView) + self.tabsBackgroundView.contentView.addSubview(tabSelectorView) + tabSelectorView.setOverlayContainerView(overlayContainerView: self.view) } - transition.updateFrame(view: tabSelectorView, frame: tabSelectorFrame) + transition.updateFrame(view: tabSelectorView, frame: CGRect(origin: CGPoint(), size: tabSelectorFrame.size)) + + var transitionFraction: CGFloat = 0.0 + if let transitionFractionValue = self.transitionFraction { + transitionFraction = -transitionFractionValue + } + tabSelectorView.updateTabSwitchFraction(fraction: transitionFraction, isDragging: false, transition: ComponentTransition(transition)) } } + + return size } func activate() { diff --git a/submodules/HashtagSearchUI/Sources/HashtagSearchRecentListNode.swift b/submodules/HashtagSearchUI/Sources/HashtagSearchRecentListNode.swift index b6238082..d2d29fda 100644 --- a/submodules/HashtagSearchUI/Sources/HashtagSearchRecentListNode.swift +++ b/submodules/HashtagSearchUI/Sources/HashtagSearchRecentListNode.swift @@ -200,7 +200,7 @@ final class HashtagSearchRecentQueryItemNode: ItemListRevealOptionsItemNode { self.iconNode = ASImageNode() self.iconNode.displaysAsynchronously = false - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.backgroundNode) self.addSubnode(self.separatorNode) diff --git a/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift b/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift index 0fb1d85b..d7c20dfd 100644 --- a/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift +++ b/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift @@ -138,7 +138,7 @@ public final class HorizontalPeerItemNode: ListViewItemNode { self.onlineNode = PeerOnlineMarkerNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.peerNode) self.addSubnode(self.badgeBackgroundNode) diff --git a/submodules/InviteLinksUI/Sources/AdditionalLinkItem.swift b/submodules/InviteLinksUI/Sources/AdditionalLinkItem.swift index c66e876f..474cded4 100644 --- a/submodules/InviteLinksUI/Sources/AdditionalLinkItem.swift +++ b/submodules/InviteLinksUI/Sources/AdditionalLinkItem.swift @@ -170,7 +170,7 @@ public class AdditionalLinkItemNode: ListViewItemNode, ItemListItemNode { self.highlightedBackgroundNode = ASDisplayNode() self.highlightedBackgroundNode.isLayerBacked = true - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.isAccessibilityElement = true diff --git a/submodules/InviteLinksUI/Sources/FolderInviteLinkListController.swift b/submodules/InviteLinksUI/Sources/FolderInviteLinkListController.swift index 2dd9b16a..3c39e1cd 100644 --- a/submodules/InviteLinksUI/Sources/FolderInviteLinkListController.swift +++ b/submodules/InviteLinksUI/Sources/FolderInviteLinkListController.swift @@ -451,7 +451,7 @@ public func folderInviteLinkListController(context: AccountContext, updatedPrese let state = stateValue.with({ $0 }) - let promptController = promptController(sharedContext: context.sharedContext, updatedPresentationData: updatedPresentationData, text: presentationData.strings.FolderLinkScreen_NameLink_Title, titleFont: .bold, value: state.title ?? "", characterLimit: 32, apply: { value in + let promptController = promptController(context: context, updatedPresentationData: updatedPresentationData, text: presentationData.strings.FolderLinkScreen_NameLink_Title, titleFont: .bold, value: state.title ?? "", characterLimit: 32, apply: { value in if let value { updateState { state in var state = state @@ -763,15 +763,21 @@ public func folderInviteLinkListController(context: AccountContext, updatedPrese if hasChanges { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.FolderLinkScreen_SaveAlertTitle, text: presentationData.strings.FolderLinkScreen_SaveAlertText, actions: [ - TextAlertAction(type: .genericAction, title: presentationData.strings.FolderLinkScreen_SaveAlertActionDiscard, action: { - f() - dismissImpl?() - }), - TextAlertAction(type: .defaultAction, title: state.selectedPeerIds.isEmpty ? presentationData.strings.FolderLinkScreen_SaveAlertActionApply : presentationData.strings.FolderLinkScreen_SaveAlertActionContinue, action: { - applyChangesImpl?() - }) - ]), nil) + let alertController = textAlertController( + context: context, + title: presentationData.strings.FolderLinkScreen_SaveAlertTitle, + text: presentationData.strings.FolderLinkScreen_SaveAlertText, + actions: [ + TextAlertAction(type: .genericAction, title: presentationData.strings.FolderLinkScreen_SaveAlertActionDiscard, action: { + f() + dismissImpl?() + }), + TextAlertAction(type: .defaultAction, title: state.selectedPeerIds.isEmpty ? presentationData.strings.FolderLinkScreen_SaveAlertActionApply : presentationData.strings.FolderLinkScreen_SaveAlertActionContinue, action: { + applyChangesImpl?() + }) + ] + ) + presentControllerImpl?(alertController, nil) return false } else { f() diff --git a/submodules/InviteLinksUI/Sources/InviteLinkHeaderItem.swift b/submodules/InviteLinksUI/Sources/InviteLinkHeaderItem.swift index d51c7950..6cf26088 100644 --- a/submodules/InviteLinksUI/Sources/InviteLinkHeaderItem.swift +++ b/submodules/InviteLinksUI/Sources/InviteLinkHeaderItem.swift @@ -94,7 +94,7 @@ class InviteLinkHeaderItemNode: ListViewItemNode { self.animationNode = DefaultAnimatedStickerNodeImpl() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.titleNode) self.addSubnode(self.textNode.textNode) diff --git a/submodules/InviteLinksUI/Sources/InviteLinkInviteHeaderItem.swift b/submodules/InviteLinksUI/Sources/InviteLinkInviteHeaderItem.swift index 22ec3233..733ee8b9 100644 --- a/submodules/InviteLinksUI/Sources/InviteLinkInviteHeaderItem.swift +++ b/submodules/InviteLinksUI/Sources/InviteLinkInviteHeaderItem.swift @@ -86,7 +86,7 @@ class InviteLinkInviteHeaderItemNode: ListViewItemNode { self.iconNode.displaysAsynchronously = false self.iconNode.displayWithoutProcessing = true - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.titleNode) self.addSubnode(self.textNode) diff --git a/submodules/InviteLinksUI/Sources/InviteLinkInviteManageItem.swift b/submodules/InviteLinksUI/Sources/InviteLinkInviteManageItem.swift index 64b7f3b3..925c6454 100644 --- a/submodules/InviteLinksUI/Sources/InviteLinkInviteManageItem.swift +++ b/submodules/InviteLinksUI/Sources/InviteLinkInviteManageItem.swift @@ -74,7 +74,7 @@ class InviteLinkInviteManageItemNode: ListViewItemNode { self.backgroundNode = ASDisplayNode() self.buttonNode = HighlightableButtonNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.backgroundNode) self.addSubnode(self.buttonNode) diff --git a/submodules/InviteLinksUI/Sources/InviteRequestsSearchItem.swift b/submodules/InviteLinksUI/Sources/InviteRequestsSearchItem.swift index 60dd4a8a..28f3932f 100644 --- a/submodules/InviteLinksUI/Sources/InviteRequestsSearchItem.swift +++ b/submodules/InviteLinksUI/Sources/InviteRequestsSearchItem.swift @@ -38,7 +38,7 @@ final class SearchNavigationContentNode: NavigationBarContentNode, ItemListContr self.cancel = cancel - self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: theme, hasSeparator: false), strings: strings, fieldStyle: .modern, displayBackground: false) + self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: theme, hasSeparator: false), presentationTheme: theme, strings: strings, fieldStyle: .modern, displayBackground: false) super.init() @@ -66,7 +66,7 @@ final class SearchNavigationContentNode: NavigationBarContentNode, ItemListContr func updateTheme(_ theme: PresentationTheme) { self.theme = theme - self.searchBar.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: self.theme), strings: self.strings) + self.searchBar.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: self.theme), presentationTheme: self.theme, strings: self.strings) self.updatePlaceholder() } @@ -78,10 +78,12 @@ final class SearchNavigationContentNode: NavigationBarContentNode, ItemListContr return 56.0 } - override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { + override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { let searchBarFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - self.nominalHeight), size: CGSize(width: size.width, height: 56.0)) self.searchBar.frame = searchBarFrame self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition) + + return size } func activate() { diff --git a/submodules/InviteLinksUI/Sources/ItemListFolderInviteLinkItem.swift b/submodules/InviteLinksUI/Sources/ItemListFolderInviteLinkItem.swift index 70cdea58..5382dfed 100644 --- a/submodules/InviteLinksUI/Sources/ItemListFolderInviteLinkItem.swift +++ b/submodules/InviteLinksUI/Sources/ItemListFolderInviteLinkItem.swift @@ -203,7 +203,7 @@ public class ItemListFolderInviteLinkItemNode: ListViewItemNode, ItemListItemNod self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.fieldNode) self.addSubnode(self.addressNode) diff --git a/submodules/InviteLinksUI/Sources/ItemListFolderInviteLinkListItem.swift b/submodules/InviteLinksUI/Sources/ItemListFolderInviteLinkListItem.swift index d34dab06..6ccdd7c2 100644 --- a/submodules/InviteLinksUI/Sources/ItemListFolderInviteLinkListItem.swift +++ b/submodules/InviteLinksUI/Sources/ItemListFolderInviteLinkListItem.swift @@ -210,7 +210,7 @@ public class ItemListFolderInviteLinkListItemNode: ItemListRevealOptionsItemNode self.highlightedBackgroundNode = ASDisplayNode() self.highlightedBackgroundNode.isLayerBacked = true - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.isAccessibilityElement = true diff --git a/submodules/InviteLinksUI/Sources/ItemListInviteLinkDateLimitItem.swift b/submodules/InviteLinksUI/Sources/ItemListInviteLinkDateLimitItem.swift index f329c40b..3555521e 100644 --- a/submodules/InviteLinksUI/Sources/ItemListInviteLinkDateLimitItem.swift +++ b/submodules/InviteLinksUI/Sources/ItemListInviteLinkDateLimitItem.swift @@ -170,7 +170,7 @@ private final class ItemListInviteLinkTimeLimitItemNode: ListViewItemNode { self.customTextNode.isUserInteractionEnabled = false self.customTextNode.displaysAsynchronously = false - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.lowTextNode) self.addSubnode(self.mediumTextNode) diff --git a/submodules/InviteLinksUI/Sources/ItemListInviteLinkItem.swift b/submodules/InviteLinksUI/Sources/ItemListInviteLinkItem.swift index d4846030..a00ee4df 100644 --- a/submodules/InviteLinksUI/Sources/ItemListInviteLinkItem.swift +++ b/submodules/InviteLinksUI/Sources/ItemListInviteLinkItem.swift @@ -235,7 +235,7 @@ public class ItemListInviteLinkItemNode: ListViewItemNode, ItemListItemNode { self.highlightedBackgroundNode = ASDisplayNode() self.highlightedBackgroundNode.isLayerBacked = true - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.isAccessibilityElement = true diff --git a/submodules/InviteLinksUI/Sources/ItemListInviteLinkUsageLimitItem.swift b/submodules/InviteLinksUI/Sources/ItemListInviteLinkUsageLimitItem.swift index db379ed7..3bc59945 100644 --- a/submodules/InviteLinksUI/Sources/ItemListInviteLinkUsageLimitItem.swift +++ b/submodules/InviteLinksUI/Sources/ItemListInviteLinkUsageLimitItem.swift @@ -186,7 +186,7 @@ private final class ItemListInviteLinkUsageLimitItemNode: ListViewItemNode { self.customTextNode.isUserInteractionEnabled = false self.customTextNode.displaysAsynchronously = false - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.lowTextNode) self.addSubnode(self.mediumTextNode) diff --git a/submodules/InviteLinksUI/Sources/ItemListInviteRequestItem.swift b/submodules/InviteLinksUI/Sources/ItemListInviteRequestItem.swift index 5a66bf6e..a233c6cd 100644 --- a/submodules/InviteLinksUI/Sources/ItemListInviteRequestItem.swift +++ b/submodules/InviteLinksUI/Sources/ItemListInviteRequestItem.swift @@ -223,7 +223,7 @@ public class ItemListInviteRequestItemNode: ListViewItemNode, ItemListItemNode { self.contentWrapperNode = ASDisplayNode() - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.isAccessibilityElement = true diff --git a/submodules/InviteLinksUI/Sources/ItemListPermanentInviteLinkItem.swift b/submodules/InviteLinksUI/Sources/ItemListPermanentInviteLinkItem.swift index 7ff09939..1761e1f6 100644 --- a/submodules/InviteLinksUI/Sources/ItemListPermanentInviteLinkItem.swift +++ b/submodules/InviteLinksUI/Sources/ItemListPermanentInviteLinkItem.swift @@ -210,7 +210,7 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.fieldNode) self.addSubnode(self.addressNode) @@ -403,8 +403,8 @@ public class ItemListPermanentInviteLinkItemNode: ListViewItemNode, ItemListItem } let fieldHeight: CGFloat = 52.0 - let fieldSpacing: CGFloat = 16.0 - let buttonHeight: CGFloat = 50.0 + let fieldSpacing: CGFloat = 10.0 + let buttonHeight: CGFloat = 52.0 let justCreatedCallSeparatorSpacing: CGFloat = 16.0 let justCreatedCallTextSpacing: CGFloat = 45.0 diff --git a/submodules/ItemListAddressItem/Sources/ItemListAddressItem.swift b/submodules/ItemListAddressItem/Sources/ItemListAddressItem.swift index 595695b6..b4d71c20 100644 --- a/submodules/ItemListAddressItem/Sources/ItemListAddressItem.swift +++ b/submodules/ItemListAddressItem/Sources/ItemListAddressItem.swift @@ -142,7 +142,7 @@ public class ItemListAddressItemNode: ListViewItemNode { self.iconNode = ASImageNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.labelNode) self.addSubnode(self.textNode) diff --git a/submodules/ItemListAvatarAndNameInfoItem/Sources/ItemListAvatarAndNameItem.swift b/submodules/ItemListAvatarAndNameInfoItem/Sources/ItemListAvatarAndNameItem.swift index 3e4bece6..b2843f1b 100644 --- a/submodules/ItemListAvatarAndNameInfoItem/Sources/ItemListAvatarAndNameItem.swift +++ b/submodules/ItemListAvatarAndNameInfoItem/Sources/ItemListAvatarAndNameItem.swift @@ -327,7 +327,7 @@ public class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNo self.callButton = HighlightableButtonNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.isAccessibilityElement = true diff --git a/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift b/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift index 49b9f75a..3f877671 100644 --- a/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift +++ b/submodules/ItemListPeerActionItem/Sources/ItemListPeerActionItem.swift @@ -160,7 +160,7 @@ public final class ItemListPeerActionItemNode: ListViewItemNode { self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.iconNode) self.addSubnode(self.titleNode) @@ -257,12 +257,16 @@ public final class ItemListPeerActionItemNode: ListViewItemNode { case .blocks: strongSelf.topStripeNode.backgroundColor = item.presentationData.theme.list.itemBlocksSeparatorColor strongSelf.bottomStripeNode.backgroundColor = item.presentationData.theme.list.itemBlocksSeparatorColor - strongSelf.backgroundNode.backgroundColor = item.presentationData.theme.list.itemBlocksBackgroundColor + if !item.alwaysPlain { + strongSelf.backgroundNode.backgroundColor = item.presentationData.theme.list.itemBlocksBackgroundColor + } strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor case .plain: strongSelf.topStripeNode.backgroundColor = item.presentationData.theme.list.itemPlainSeparatorColor strongSelf.bottomStripeNode.backgroundColor = item.presentationData.theme.list.itemPlainSeparatorColor - strongSelf.backgroundNode.backgroundColor = item.presentationData.theme.list.plainBackgroundColor + if !item.alwaysPlain { + strongSelf.backgroundNode.backgroundColor = item.presentationData.theme.list.plainBackgroundColor + } strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor } } diff --git a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift index d9517501..036b86ab 100644 --- a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift +++ b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift @@ -855,7 +855,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo self.highlightedBackgroundNode = ASDisplayNode() self.highlightedBackgroundNode.isLayerBacked = true - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.isAccessibilityElement = true diff --git a/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift b/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift index 81f65a8c..149742ff 100644 --- a/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift +++ b/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift @@ -263,7 +263,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode { self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.containerNode) diff --git a/submodules/ItemListUI/BUILD b/submodules/ItemListUI/BUILD index 71afc199..cbd9e076 100644 --- a/submodules/ItemListUI/BUILD +++ b/submodules/ItemListUI/BUILD @@ -34,6 +34,8 @@ swift_library( "//submodules/Components/ComponentDisplayAdapters", "//submodules/TelegramUI/Components/TextNodeWithEntities", "//submodules/TelegramUI/Components/ListItemComponentAdaptor", + "//submodules/TelegramUI/Components/GlassBackgroundComponent", + "//submodules/TelegramUI/Components/HorizontalTabsComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/ItemListUI/Sources/ItemListController.swift b/submodules/ItemListUI/Sources/ItemListController.swift index 9149f3f6..8eda7718 100644 --- a/submodules/ItemListUI/Sources/ItemListController.swift +++ b/submodules/ItemListUI/Sources/ItemListController.swift @@ -281,11 +281,12 @@ open class ItemListController: ViewController, KeyShortcutResponder, Presentable self.presentationData = presentationData self.hideNavigationBarBackground = hideNavigationBarBackground - super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme, hideBackground: hideNavigationBarBackground, hideSeparator: hideNavigationBarBackground), strings: NavigationBarStrings(presentationStrings: presentationData.strings))) + super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme, hideBackground: hideNavigationBarBackground, hideSeparator: hideNavigationBarBackground, style: .glass), strings: NavigationBarStrings(presentationStrings: presentationData.strings))) self.isOpaqueWhenInOverlay = true self.blocksBackgroundWhenInOverlay = true self.automaticallyControlPresentationContextLayout = false + self._hasGlassStyle = true self.statusBar.statusBarStyle = presentationData.theme.rootController.statusBarStyle.style @@ -326,13 +327,23 @@ open class ItemListController: ViewController, KeyShortcutResponder, Presentable Queue.mainQueue().async { if let strongSelf = self { let previousState = previousControllerState.swap(controllerState) + let isFirstTime = previousState == nil if previousState?.title != controllerState.title { + var previousHadContentNode = false + switch previousState?.title { + case .textWithTabs: + previousHadContentNode = true + default: + break + } switch controllerState.title { case let .text(text): strongSelf.title = text strongSelf.navigationItem.titleView = nil strongSelf.segmentedTitleView = nil - strongSelf.navigationBar?.setContentNode(nil, animated: false) + if previousHadContentNode { + strongSelf.navigationBar?.setContentNode(nil, animated: false) + } if strongSelf.isNodeLoaded { strongSelf.controllerNode.panRecognizer?.isEnabled = false } @@ -340,7 +351,9 @@ open class ItemListController: ViewController, KeyShortcutResponder, Presentable strongSelf.title = "" strongSelf.navigationItem.titleView = ItemListTextWithSubtitleTitleView(theme: controllerState.presentationData.theme, title: title, subtitle: subtitle) strongSelf.segmentedTitleView = nil - strongSelf.navigationBar?.setContentNode(nil, animated: false) + if previousHadContentNode { + strongSelf.navigationBar?.setContentNode(nil, animated: false) + } if strongSelf.isNodeLoaded { strongSelf.controllerNode.panRecognizer?.isEnabled = false } @@ -358,7 +371,9 @@ open class ItemListController: ViewController, KeyShortcutResponder, Presentable } } } - strongSelf.navigationBar?.setContentNode(nil, animated: false) + if previousHadContentNode { + strongSelf.navigationBar?.setContentNode(nil, animated: false) + } if strongSelf.isNodeLoaded { strongSelf.controllerNode.panRecognizer?.isEnabled = false } @@ -515,10 +530,10 @@ open class ItemListController: ViewController, KeyShortcutResponder, Presentable } } - if strongSelf.presentationData != controllerState.presentationData { + if strongSelf.presentationData != controllerState.presentationData || isFirstTime { strongSelf.presentationData = controllerState.presentationData - strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: strongSelf.presentationData.theme, hideBackground: strongSelf.hideNavigationBarBackground, hideSeparator: strongSelf.hideNavigationBarBackground), strings: NavigationBarStrings(presentationStrings: strongSelf.presentationData.strings))) + strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: strongSelf.presentationData.theme, hideBackground: strongSelf.hideNavigationBarBackground, hideSeparator: strongSelf.hideNavigationBarBackground, edgeEffectColor: state.0.style == .blocks ? strongSelf.presentationData.theme.list.blocksBackgroundColor : strongSelf.presentationData.theme.list.plainBackgroundColor, style: .glass), strings: NavigationBarStrings(presentationStrings: strongSelf.presentationData.strings)), transition: .immediate) strongSelf.statusBar.updateStatusBarStyle(strongSelf.presentationData.theme.rootController.statusBarStyle.style, animated: true) strongSelf.segmentedTitleView?.theme = controllerState.presentationData.theme @@ -675,7 +690,8 @@ private final class ItemListTextWithSubtitleTitleView: UIView, NavigationBarTitl private let titleNode: ImmediateTextNode private let subtitleNode: ImmediateTextNode - private var validLayout: (CGSize, CGRect)? + private var validLayout: CGSize? + var requestUpdate: ((ContainedViewLayoutTransition) -> Void)? init(theme: PresentationTheme, title: String, subtitle: String) { self.titleNode = ImmediateTextNode() @@ -705,21 +721,23 @@ private final class ItemListTextWithSubtitleTitleView: UIView, NavigationBarTitl func updateTheme(theme: PresentationTheme) { self.titleNode.attributedText = NSAttributedString(string: self.titleNode.attributedText?.string ?? "", font: Font.medium(17.0), textColor: theme.rootController.navigationBar.primaryTextColor) self.subtitleNode.attributedText = NSAttributedString(string: self.subtitleNode.attributedText?.string ?? "", font: Font.regular(13.0), textColor: theme.rootController.navigationBar.secondaryTextColor) - if let (size, clearBounds) = self.validLayout { - let _ = self.updateLayout(size: size, clearBounds: clearBounds, transition: .immediate) + if let size = self.validLayout { + let _ = self.updateLayout(availableSize: size, transition: .immediate) } } override func layoutSubviews() { super.layoutSubviews() - if let (size, clearBounds) = self.validLayout { - let _ = self.updateLayout(size: size, clearBounds: clearBounds, transition: .immediate) + if let size = self.validLayout { + let _ = self.updateLayout(availableSize: size, transition: .immediate) } } - func updateLayout(size: CGSize, clearBounds: CGRect, transition: ContainedViewLayoutTransition) -> CGRect { - self.validLayout = (size, clearBounds) + func updateLayout(availableSize: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { + let size = availableSize + + self.validLayout = size let titleSize = self.titleNode.updateLayout(size) let subtitleSize = self.subtitleNode.updateLayout(size) @@ -731,7 +749,7 @@ private final class ItemListTextWithSubtitleTitleView: UIView, NavigationBarTitl self.titleNode.frame = titleFrame self.subtitleNode.frame = subtitleFrame - return titleFrame + return availableSize } func animateLayoutTransition() { diff --git a/submodules/ItemListUI/Sources/ItemListControllerNode.swift b/submodules/ItemListUI/Sources/ItemListControllerNode.swift index 46657a20..1babb422 100644 --- a/submodules/ItemListUI/Sources/ItemListControllerNode.swift +++ b/submodules/ItemListUI/Sources/ItemListControllerNode.swift @@ -910,11 +910,7 @@ open class ItemListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate { if let validLayout = self.validLayout { updatedNode.updateLayout(layout: validLayout.0, navigationBarHeight: validLayout.1, transition: .immediate) } - if updatedNode.addedUnderNavigationBar { - self.insertSubnode(updatedNode, belowSubnode: self.navigationBar) - } else { - self.addSubnode(updatedNode) - } + self.insertSubnode(updatedNode, belowSubnode: self.navigationBar) updatedNode.activate() } } else { diff --git a/submodules/ItemListUI/Sources/ItemListControllerSegmentedTitleView.swift b/submodules/ItemListUI/Sources/ItemListControllerSegmentedTitleView.swift index f2d0b36f..bce38315 100644 --- a/submodules/ItemListUI/Sources/ItemListControllerSegmentedTitleView.swift +++ b/submodules/ItemListUI/Sources/ItemListControllerSegmentedTitleView.swift @@ -3,9 +3,12 @@ import UIKit import Display import TelegramPresentationData import ComponentFlow -import TabSelectorComponent +import GlassBackgroundComponent +import HorizontalTabsComponent public final class ItemListControllerSegmentedTitleView: UIView { + private let backgroundContainer: GlassBackgroundContainerView + private let backgroundView: GlassBackgroundView private let tabSelector = ComponentView() public var theme: PresentationTheme { @@ -42,7 +45,13 @@ public final class ItemListControllerSegmentedTitleView: UIView { self.segments = segments self.index = selectedIndex + self.backgroundContainer = GlassBackgroundContainerView() + self.backgroundView = GlassBackgroundView() + super.init(frame: CGRect()) + + self.addSubview(self.backgroundContainer) + self.backgroundContainer.contentView.addSubview(self.backgroundView) } required public init?(coder aDecoder: NSCoder) { @@ -62,10 +71,19 @@ public final class ItemListControllerSegmentedTitleView: UIView { return } - let mappedItems = zip(0 ..< self.segments.count, self.segments).map { index, segment in - return TabSelectorComponent.Item( + let mappedItems: [HorizontalTabsComponent.Tab] = zip(0 ..< self.segments.count, self.segments).map { index, segment in + return HorizontalTabsComponent.Tab( id: AnyHashable(index), - title: segment + content: .title(HorizontalTabsComponent.Tab.Title(text: segment, entities: [], enableAnimations: false)), + badge: nil, + action: { [weak self] in + guard let self else { + return + } + self.indexUpdated?(index) + }, + contextAction: nil, + deleteAction: nil ) } @@ -77,34 +95,32 @@ public final class ItemListControllerSegmentedTitleView: UIView { let tabSelectorSize = self.tabSelector.update( transition: transition, - component: AnyComponent(TabSelectorComponent( - colors: TabSelectorComponent.Colors( - foreground: self.theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.8), - selection: self.theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.05) - ), + component: AnyComponent(HorizontalTabsComponent( + context: nil, theme: self.theme, - customLayout: TabSelectorComponent.CustomLayout( - font: Font.medium(15.0), - spacing: 8.0 - ), - items: mappedItems, - selectedId: AnyHashable(self.index), - setSelectedId: { [weak self] id in - guard let self, let index = id.base as? Int else { - return - } - self.indexUpdated?(index) - } + tabs: mappedItems, + selectedTab: AnyHashable(self.index), + isEditing: false, + layout: .fit )), environment: {}, containerSize: CGSize(width: size.width, height: 44.0) ) + let tabSelectorFrame = CGRect(origin: CGPoint(x: floor((size.width - tabSelectorSize.width) / 2.0), y: floor((size.height - tabSelectorSize.height) / 2.0)), size: tabSelectorSize) - if let tabSelectorView = self.tabSelector.view { + + transition.setFrame(view: self.backgroundContainer, frame: tabSelectorFrame) + self.backgroundContainer.update(size: tabSelectorFrame.size, isDark: self.theme.overallDarkAppearance, transition: transition) + + transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: tabSelectorFrame.size)) + self.backgroundView.update(size: tabSelectorFrame.size, cornerRadius: tabSelectorFrame.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), transition: transition) + + if let tabSelectorView = self.tabSelector.view as? HorizontalTabsComponent.View { if tabSelectorView.superview == nil { - self.addSubview(tabSelectorView) + self.backgroundView.contentView.addSubview(tabSelectorView) + tabSelectorView.setOverlayContainerView(overlayContainerView: self) } - transition.setFrame(view: tabSelectorView, frame: tabSelectorFrame) + transition.setFrame(view: tabSelectorView, frame: CGRect(origin: CGPoint(), size: tabSelectorFrame.size)) } } } diff --git a/submodules/ItemListUI/Sources/ItemListControllerTabsContentNode.swift b/submodules/ItemListUI/Sources/ItemListControllerTabsContentNode.swift index 0e2526e2..71f3c5fa 100644 --- a/submodules/ItemListUI/Sources/ItemListControllerTabsContentNode.swift +++ b/submodules/ItemListUI/Sources/ItemListControllerTabsContentNode.swift @@ -63,10 +63,10 @@ final class ItemListControllerTabsContentNode: NavigationBarContentNode { guard let (size, leftInset, rightInset) = self.validLayout else { return } - self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: transition) + let _ = self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: transition) } - override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { + override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { let isFirstTime = self.validLayout == nil self.validLayout = (size, leftInset, rightInset) @@ -116,6 +116,8 @@ final class ItemListControllerTabsContentNode: NavigationBarContentNode { if isFirstTime { self.requestContainerLayout(.immediate) } + + return size } override var height: CGFloat { diff --git a/submodules/ItemListUI/Sources/Items/ItemListActionItem.swift b/submodules/ItemListUI/Sources/Items/ItemListActionItem.swift index a9e6d0dd..c60eccae 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListActionItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListActionItem.swift @@ -125,7 +125,7 @@ public class ItemListActionItemNode: ListViewItemNode, ItemListItemNode { self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.titleNode) diff --git a/submodules/ItemListUI/Sources/Items/ItemListActivityTextItem.swift b/submodules/ItemListUI/Sources/Items/ItemListActivityTextItem.swift index 434b524c..9a0a5eff 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListActivityTextItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListActivityTextItem.swift @@ -85,7 +85,7 @@ public class ItemListActivityTextItemNode: ListViewItemNode { self.activityIndicator = ActivityIndicator(type: ActivityIndicatorType.custom(.black, 16.0, 2.0, false)) - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.titleNode) self.addSubnode(self.activityIndicator) diff --git a/submodules/ItemListUI/Sources/Items/ItemListCheckboxItem.swift b/submodules/ItemListUI/Sources/Items/ItemListCheckboxItem.swift index 667a471d..8743226e 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListCheckboxItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListCheckboxItem.swift @@ -168,7 +168,7 @@ public class ItemListCheckboxItemNode: ItemListRevealOptionsItemNode { self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.contentParentNode) self.contentParentNode.addSubnode(self.contentContainerNode) diff --git a/submodules/ItemListUI/Sources/Items/ItemListDisclosureItem.swift b/submodules/ItemListUI/Sources/Items/ItemListDisclosureItem.swift index 7b03132a..5d518b5f 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListDisclosureItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListDisclosureItem.swift @@ -254,7 +254,7 @@ public class ItemListDisclosureItemNode: ListViewItemNode, ItemListItemNode { self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.titleNode.textNode) self.addSubnode(self.labelNode) diff --git a/submodules/ItemListUI/Sources/Items/ItemListEditableItem.swift b/submodules/ItemListUI/Sources/Items/ItemListEditableItem.swift index 3ba05e2b..df2372c1 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListEditableItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListEditableItem.swift @@ -83,8 +83,8 @@ open class ItemListRevealOptionsItemNode: ListViewItemNode, ASGestureRecognizerD return !self.isDisplayingRevealedOptions } - override public init(layerBacked: Bool, dynamicBounce: Bool, rotated: Bool, seeThrough: Bool) { - super.init(layerBacked: layerBacked, dynamicBounce: dynamicBounce, rotated: rotated, seeThrough: seeThrough) + override public init(layerBacked: Bool, rotated: Bool, seeThrough: Bool) { + super.init(layerBacked: layerBacked, rotated: rotated, seeThrough: seeThrough) } open var controlsContainer: ASDisplayNode { diff --git a/submodules/ItemListUI/Sources/Items/ItemListExpandableSwitchItem.swift b/submodules/ItemListUI/Sources/Items/ItemListExpandableSwitchItem.swift index a7f52e9e..6f95c58d 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListExpandableSwitchItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListExpandableSwitchItem.swift @@ -267,7 +267,7 @@ public class ItemListExpandableSwitchItemNode: ListViewItemNode, ItemListItemNod self.subItemContainer = ASDisplayNode() self.subItemContainer.clipsToBounds = true - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.titleNode) self.addSubnode(self.titleValueNode) diff --git a/submodules/ItemListUI/Sources/Items/ItemListInfoItem.swift b/submodules/ItemListUI/Sources/Items/ItemListInfoItem.swift index f39f8b3f..bf113249 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListInfoItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListInfoItem.swift @@ -181,7 +181,7 @@ public class InfoItemNode: ListViewItemNode { self.closeButton.hitTestSlop = UIEdgeInsets(top: -8.0, left: -8.0, bottom: -8.0, right: -8.0) self.closeButton.displaysAsynchronously = false - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.badgeNode) self.addSubnode(self.labelNode) diff --git a/submodules/ItemListUI/Sources/Items/ItemListMultilineInputItem.swift b/submodules/ItemListUI/Sources/Items/ItemListMultilineInputItem.swift index c2465dec..e0e98a5c 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListMultilineInputItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListMultilineInputItem.swift @@ -152,7 +152,7 @@ public class ItemListMultilineInputItemNode: ListViewItemNode, ASEditableTextNod self.limitTextNode = TextNode() self.limitTextNode.displaysAsynchronously = false - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.textClippingNode.addSubnode(self.textNode) self.addSubnode(self.textClippingNode) diff --git a/submodules/ItemListUI/Sources/Items/ItemListMultilineTextItem.swift b/submodules/ItemListUI/Sources/Items/ItemListMultilineTextItem.swift index fc92b444..98865359 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListMultilineTextItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListMultilineTextItem.swift @@ -127,7 +127,7 @@ public class ItemListMultilineTextItemNode: ListViewItemNode { self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.textNode) self.addSubnode(self.activateArea) diff --git a/submodules/ItemListUI/Sources/Items/ItemListPlaceholderItem.swift b/submodules/ItemListUI/Sources/Items/ItemListPlaceholderItem.swift index 4a87d011..4bbd49de 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListPlaceholderItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListPlaceholderItem.swift @@ -93,7 +93,7 @@ public class ItemListPlaceholderItemNode: ListViewItemNode, ItemListItemNode { self.textNode = TextNode() self.textNode.isUserInteractionEnabled = false - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.textNode) } diff --git a/submodules/ItemListUI/Sources/Items/ItemListSectionHeaderItem.swift b/submodules/ItemListUI/Sources/Items/ItemListSectionHeaderItem.swift index 61c141fd..f19047c2 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListSectionHeaderItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListSectionHeaderItem.swift @@ -139,7 +139,7 @@ public class ItemListSectionHeaderItemNode: ListViewItemNode { self.activateArea = AccessibilityAreaNode() self.activateArea.accessibilityTraits = [.staticText, .header] - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.titleNode) self.addSubnode(self.accessoryTextNode) diff --git a/submodules/ItemListUI/Sources/Items/ItemListSingleLineInputItem.swift b/submodules/ItemListUI/Sources/Items/ItemListSingleLineInputItem.swift index f2bdd08d..0b012154 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListSingleLineInputItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListSingleLineInputItem.swift @@ -177,7 +177,7 @@ public class ItemListSingleLineInputItemNode: ListViewItemNode, UITextFieldDeleg self.labelNode = TextNode() self.labelNode.isUserInteractionEnabled = false - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.titleNode.textNode) self.addSubnode(self.textNode) diff --git a/submodules/ItemListUI/Sources/Items/ItemListSwitchItem.swift b/submodules/ItemListUI/Sources/Items/ItemListSwitchItem.swift index 18a0027c..b9d897c1 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListSwitchItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListSwitchItem.swift @@ -207,7 +207,7 @@ public class ItemListSwitchItemNode: ListViewItemNode, ItemListItemNode { self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.titleNode) self.addSubnode(self.switchNode) diff --git a/submodules/ItemListUI/Sources/Items/ItemListTextItem.swift b/submodules/ItemListUI/Sources/Items/ItemListTextItem.swift index ad988d97..5c5c50a2 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListTextItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListTextItem.swift @@ -132,7 +132,7 @@ public class ItemListTextItemNode: ListViewItemNode, ItemListItemNode { self.activateArea = AccessibilityAreaNode() self.activateArea.accessibilityTraits = .staticText - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.textNode.textNode) self.addSubnode(self.activateArea) diff --git a/submodules/ItemListUI/Sources/Items/ItemListTextWithLabelItem.swift b/submodules/ItemListUI/Sources/Items/ItemListTextWithLabelItem.swift index 2b27d407..9e3232df 100644 --- a/submodules/ItemListUI/Sources/Items/ItemListTextWithLabelItem.swift +++ b/submodules/ItemListUI/Sources/Items/ItemListTextWithLabelItem.swift @@ -134,7 +134,7 @@ public class ItemListTextWithLabelItemNode: ListViewItemNode { self.textNode.contentMode = .left self.textNode.contentsScale = UIScreen.main.scale - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.isAccessibilityElement = true diff --git a/submodules/ItemListVenueItem/Sources/ItemListVenueItem.swift b/submodules/ItemListVenueItem/Sources/ItemListVenueItem.swift index 0cff7f0a..b8492924 100644 --- a/submodules/ItemListVenueItem/Sources/ItemListVenueItem.swift +++ b/submodules/ItemListVenueItem/Sources/ItemListVenueItem.swift @@ -171,7 +171,7 @@ public class ItemListVenueItemNode: ListViewItemNode, ItemListItemNode { self.highlightedBackgroundNode = ASDisplayNode() self.highlightedBackgroundNode.isLayerBacked = true - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.isAccessibilityElement = true diff --git a/submodules/ListMessageItem/Sources/ListMessageHoleItem.swift b/submodules/ListMessageItem/Sources/ListMessageHoleItem.swift index d712fed0..f4960900 100644 --- a/submodules/ListMessageItem/Sources/ListMessageHoleItem.swift +++ b/submodules/ListMessageItem/Sources/ListMessageHoleItem.swift @@ -66,7 +66,7 @@ final class ListMessageHoleItemNode: ListViewItemNode { private var activityIndicator: UIActivityIndicatorView? init() { - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) } override func didLoad() { diff --git a/submodules/ListMessageItem/Sources/ListMessageNode.swift b/submodules/ListMessageItem/Sources/ListMessageNode.swift index af9ba2c7..19d1b2ee 100644 --- a/submodules/ListMessageItem/Sources/ListMessageNode.swift +++ b/submodules/ListMessageItem/Sources/ListMessageNode.swift @@ -10,7 +10,7 @@ public class ListMessageNode: ListViewItemNode { var interaction: ListMessageItemInteraction? required init() { - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) } func setupItem(_ item: ListMessageItem) { diff --git a/submodules/ListMessageItem/Sources/ListMessageSnippetItemNode.swift b/submodules/ListMessageItem/Sources/ListMessageSnippetItemNode.swift index de538291..fd7134aa 100644 --- a/submodules/ListMessageItem/Sources/ListMessageSnippetItemNode.swift +++ b/submodules/ListMessageItem/Sources/ListMessageSnippetItemNode.swift @@ -18,9 +18,17 @@ import TelegramStringFormatting import WallpaperResources import UrlEscaping -private let iconFont = Font.with(size: 30.0, design: .round, weight: .bold) +private let iconFont = Font.with(size: 28.0, design: .round, weight: .bold) private let iconTextBackgroundImage = generateStretchableFilledCircleImage(radius: 6.0, color: UIColor(rgb: 0xFF9500)) +private let iconTextGlassBackgroundImage = generateImage(CGSize(width: 40.0, height: 40.0), contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(UIColor(rgb: 0xFF9500).cgColor) + + let path = UIBezierPath(roundedRect: CGRect(origin: .zero, size: size), cornerRadius: 12.0) + context.addPath(path.cgPath) + context.fillPath() +}) public final class ListMessageSnippetItemNode: ListMessageNode { private let contextSourceNode: ContextExtractedContentContainingNode @@ -273,7 +281,7 @@ public final class ListMessageSnippetItemNode: ListMessageNode { var iconImageReferenceAndRepresentation: (AnyMediaReference, TelegramMediaImageRepresentation)? var updateIconImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>? - let applyIconTextBackgroundImage = iconTextBackgroundImage + let applyIconTextBackgroundImage = item.systemStyle == .glass ? iconTextGlassBackgroundImage : iconTextBackgroundImage var primaryUrl: String? @@ -637,7 +645,7 @@ public final class ListMessageSnippetItemNode: ListMessageNode { var iconImageApply: (() -> Void)? if let iconImageReferenceAndRepresentation = iconImageReferenceAndRepresentation { let iconSize = CGSize(width: 40.0, height: 40.0) - let imageCorners = ImageCorners(radius: 6.0) + let imageCorners = ImageCorners(radius: item.systemStyle == .glass ? 12.0 : 6.0, curve: item.systemStyle == .glass ? .continuous : .circular) let arguments = TransformImageArguments(corners: imageCorners, imageSize: iconImageReferenceAndRepresentation.1.dimensions.cgSize.aspectFilled(iconSize), boundingSize: iconSize, intrinsicInsets: UIEdgeInsets(), emptyColor: item.presentationData.theme.theme.list.mediaPlaceholderColor) iconImageApply = iconImageLayout(arguments) } @@ -754,7 +762,7 @@ public final class ListMessageSnippetItemNode: ListMessageNode { } let iconFrame = CGRect(origin: CGPoint(x: params.leftInset + leftOffset + 12.0, y: 12.0), size: CGSize(width: 40.0, height: 40.0)) - transition.updateFrame(node: strongSelf.iconTextNode, frame: CGRect(origin: CGPoint(x: iconFrame.minX + floorToScreenPixels((iconFrame.width - iconTextLayout.size.width) / 2.0), y: iconFrame.minY + floorToScreenPixels((iconFrame.height - iconTextLayout.size.height) / 2.0) + 2.0), size: iconTextLayout.size)) + transition.updateFrame(node: strongSelf.iconTextNode, frame: CGRect(origin: CGPoint(x: iconFrame.minX + floorToScreenPixels((iconFrame.width - iconTextLayout.size.width) / 2.0) + 1.0 - UIScreenPixel, y: iconFrame.minY + floorToScreenPixels((iconFrame.height - iconTextLayout.size.height) / 2.0) + 2.0), size: iconTextLayout.size)) let _ = iconTextApply() diff --git a/submodules/ListSectionHeaderNode/BUILD b/submodules/ListSectionHeaderNode/BUILD index 38262bee..11c8ff2c 100644 --- a/submodules/ListSectionHeaderNode/BUILD +++ b/submodules/ListSectionHeaderNode/BUILD @@ -10,9 +10,10 @@ swift_library( "-warnings-as-errors", ], deps = [ - "//submodules/AsyncDisplayKit:AsyncDisplayKit", - "//submodules/Display:Display", - "//submodules/TelegramPresentationData:TelegramPresentationData", + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/TelegramPresentationData", + "//submodules/ImageBlur", ], visibility = [ "//visibility:public", diff --git a/submodules/ListSectionHeaderNode/Sources/ListSectionHeaderNode.swift b/submodules/ListSectionHeaderNode/Sources/ListSectionHeaderNode.swift index c781d07f..e933bbd6 100644 --- a/submodules/ListSectionHeaderNode/Sources/ListSectionHeaderNode.swift +++ b/submodules/ListSectionHeaderNode/Sources/ListSectionHeaderNode.swift @@ -3,6 +3,7 @@ import UIKit import AsyncDisplayKit import Display import TelegramPresentationData +import ImageBlur private let titleFont = Font.regular(13.0) private let actionFont = Font.regular(13.0) @@ -15,6 +16,7 @@ public enum ListSectionHeaderActionType { public final class ListSectionHeaderNode: ASDisplayNode { private let backgroundLayer: SimpleLayer private let label: ImmediateTextNode + private let labelBackgroundView: UIImageView private var actionButtonLabel: ImmediateTextNode? private var actionButton: HighlightableButtonNode? private var theme: PresentationTheme @@ -89,13 +91,16 @@ public final class ListSectionHeaderNode: ASDisplayNode { self.label.isAccessibilityElement = true self.label.displaysAsynchronously = false + self.labelBackgroundView = UIImageView() + super.init() self.layer.addSublayer(self.backgroundLayer) + //self.view.addSubview(self.labelBackgroundView) self.addSubnode(self.label) - self.backgroundLayer.backgroundColor = theme.chatList.sectionHeaderFillColor.cgColor + //self.backgroundLayer.backgroundColor = theme.chatList.sectionHeaderFillColor.cgColor } override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { @@ -135,7 +140,7 @@ public final class ListSectionHeaderNode: ASDisplayNode { if self.theme !== theme { self.theme = theme - self.backgroundLayer.backgroundColor = theme.chatList.sectionHeaderFillColor.cgColor + //self.backgroundLayer.backgroundColor = theme.chatList.sectionHeaderFillColor.cgColor self.label.attributedText = NSAttributedString(string: self.title ?? "", font: titleFont, textColor: self.theme.chatList.sectionHeaderTextColor) @@ -150,7 +155,22 @@ public final class ListSectionHeaderNode: ASDisplayNode { public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, showBackground: Bool = true) { self.validLayout = (size, leftInset, rightInset) let labelSize = self.label.updateLayout(CGSize(width: max(0.0, size.width - leftInset - rightInset - 18.0), height: size.height)) - self.label.frame = CGRect(origin: CGPoint(x: leftInset + 16.0, y: 6.0 + UIScreenPixel), size: CGSize(width: labelSize.width, height: size.height)) + let labelFrame = CGRect(origin: CGPoint(x: leftInset + 16.0, y: 6.0 + UIScreenPixel), size: CGSize(width: labelSize.width, height: labelSize.height)) + self.label.frame = labelFrame + + let labelBackgroundSize: CGFloat = labelSize.height + let labelBackgroundInnerInset: CGFloat = 2.0 + let labelBackgroundInset: CGFloat = 10.0 + labelBackgroundInnerInset + if self.labelBackgroundView.image?.size.width != labelBackgroundSize + labelBackgroundInset * 2.0 { + self.labelBackgroundView.image = blurredImage(generateImage(CGSize(width: labelBackgroundSize + labelBackgroundInset * 2.0, height: labelBackgroundSize + labelBackgroundInset * 2.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(UIColor.white.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(x: labelBackgroundInset - labelBackgroundInnerInset, y: labelBackgroundInset - labelBackgroundInnerInset), size: CGSize(width: labelBackgroundSize + labelBackgroundInnerInset * 2.0, height: labelBackgroundSize + labelBackgroundInnerInset * 2.0))) + })!, radius: 17, iterations: 3)?.stretchableImage(withLeftCapWidth: Int(labelBackgroundInset + labelBackgroundSize * 0.5), topCapHeight: Int(labelBackgroundInset + labelBackgroundSize * 0.5)).withRenderingMode(.alwaysTemplate) + } + self.labelBackgroundView.tintColor = self.theme.list.plainBackgroundColor.withAlphaComponent(self.theme.overallDarkAppearance ? 0.5 : 0.7) + + self.labelBackgroundView.frame = labelFrame.insetBy(dx: -labelBackgroundInset - 4.0, dy: -labelBackgroundInset) if let actionButton = self.actionButton, let actionButtonLabel = self.actionButtonLabel { let buttonSize = actionButtonLabel.updateLayout(CGSize(width: size.width, height: size.height)) diff --git a/submodules/LocationUI/Sources/LocationActionListItem.swift b/submodules/LocationUI/Sources/LocationActionListItem.swift index e84192d1..ea37651e 100644 --- a/submodules/LocationUI/Sources/LocationActionListItem.swift +++ b/submodules/LocationUI/Sources/LocationActionListItem.swift @@ -224,7 +224,7 @@ final class LocationActionListItemNode: ListViewItemNode { self.venueIconNode = TransformImageNode() - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.backgroundNode) self.addSubnode(self.separatorNode) diff --git a/submodules/LocationUI/Sources/LocationAttributionItem.swift b/submodules/LocationUI/Sources/LocationAttributionItem.swift index d090c91b..b38b7e90 100644 --- a/submodules/LocationUI/Sources/LocationAttributionItem.swift +++ b/submodules/LocationUI/Sources/LocationAttributionItem.swift @@ -65,7 +65,7 @@ private class LocationAttributionItemNode: ListViewItemNode { self.imageNode.displaysAsynchronously = false self.imageNode.displayWithoutProcessing = true - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.imageNode) } diff --git a/submodules/LocationUI/Sources/LocationInfoListItem.swift b/submodules/LocationUI/Sources/LocationInfoListItem.swift index 4c3bca6e..fda4a00e 100644 --- a/submodules/LocationUI/Sources/LocationInfoListItem.swift +++ b/submodules/LocationUI/Sources/LocationInfoListItem.swift @@ -98,7 +98,7 @@ public final class LocationInfoListItemNode: ListViewItemNode { self.venueIconNode = TransformImageNode() self.venueIconNode.isUserInteractionEnabled = false - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.backgroundNode) self.addSubnode(self.buttonNode) diff --git a/submodules/LocationUI/Sources/LocationLiveListItem.swift b/submodules/LocationUI/Sources/LocationLiveListItem.swift index 7df33503..5aaaf511 100644 --- a/submodules/LocationUI/Sources/LocationLiveListItem.swift +++ b/submodules/LocationUI/Sources/LocationLiveListItem.swift @@ -119,7 +119,7 @@ final class LocationLiveListItemNode: ListViewItemNode { self.avatarNode = AvatarNode(font: avatarFont) self.avatarNode.isLayerBacked = !smartInvertColorsEnabled() - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.backgroundNode) self.addSubnode(self.separatorNode) diff --git a/submodules/LocationUI/Sources/LocationPickerController.swift b/submodules/LocationUI/Sources/LocationPickerController.swift index 9942427e..571b440f 100644 --- a/submodules/LocationUI/Sources/LocationPickerController.swift +++ b/submodules/LocationUI/Sources/LocationPickerController.swift @@ -129,7 +129,7 @@ public final class LocationPickerController: ViewController, AttachmentContainab let navigationBarPresentationData = NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: strongSelf.presentationData.theme, hideBackground: style == .glass, hideSeparator: true), strings: NavigationBarStrings(presentationStrings: strongSelf.presentationData.strings)) - strongSelf.navigationBar?.updatePresentationData(navigationBarPresentationData) + strongSelf.navigationBar?.updatePresentationData(navigationBarPresentationData, transition: .immediate) strongSelf.searchNavigationContentNode?.updatePresentationData(strongSelf.presentationData) strongSelf.updateBarButtons() diff --git a/submodules/LocationUI/Sources/LocationPickerControllerNode.swift b/submodules/LocationUI/Sources/LocationPickerControllerNode.swift index fef407f8..1cab3b16 100644 --- a/submodules/LocationUI/Sources/LocationPickerControllerNode.swift +++ b/submodules/LocationUI/Sources/LocationPickerControllerNode.swift @@ -1382,7 +1382,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM transition.updateFrame(view: titleView, frame: titleFrame) } - let barButtonSize = CGSize(width: 40.0, height: 40.0) + let barButtonSize = CGSize(width: 44.0, height: 44.0) let cancelButtonSize = self.cancelButton.update( transition: ComponentTransition(transition), component: AnyComponent(GlassBarButtonComponent( @@ -1393,7 +1393,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM component: AnyComponentWithIdentity(id: isPickingLocation ? "back" : "close", component: AnyComponent( BundleIconComponent( name: isPickingLocation ? "Navigation/Back" : "Navigation/Close", - tintColor: self.presentationData.theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: self.presentationData.theme.chat.inputPanel.panelControlColor ) )), action: { [weak self] _ in @@ -1428,7 +1428,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM component: AnyComponentWithIdentity(id: "search", component: AnyComponent( BundleIconComponent( name: "Navigation/Search", - tintColor: self.presentationData.theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: self.presentationData.theme.chat.inputPanel.panelControlColor ) )), action: { [weak self] _ in diff --git a/submodules/LocationUI/Sources/LocationSearchNavigationContentNode.swift b/submodules/LocationUI/Sources/LocationSearchNavigationContentNode.swift index 8d2baf3c..2ece25f6 100644 --- a/submodules/LocationUI/Sources/LocationSearchNavigationContentNode.swift +++ b/submodules/LocationUI/Sources/LocationSearchNavigationContentNode.swift @@ -18,7 +18,7 @@ final class LocationSearchNavigationContentNode: NavigationBarContentNode { self.presentationData = presentationData self.interaction = interaction - self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: presentationData.theme, hasSeparator: false), strings: presentationData.strings, fieldStyle: .modern) + self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: presentationData.theme, hasSeparator: false), presentationTheme: presentationData.theme, strings: presentationData.strings, fieldStyle: .modern) self.searchBar.placeholderString = NSAttributedString(string: presentationData.strings.Map_Search, font: searchBarFont, textColor: presentationData.theme.rootController.navigationSearchBar.inputPlaceholderTextColor) super.init() @@ -38,10 +38,12 @@ final class LocationSearchNavigationContentNode: NavigationBarContentNode { return 56.0 } - override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { + override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { let searchBarFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - self.nominalHeight), size: CGSize(width: size.width, height: 56.0)) self.searchBar.frame = searchBarFrame self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition) + + return size } func activate() { @@ -58,6 +60,6 @@ final class LocationSearchNavigationContentNode: NavigationBarContentNode { func updatePresentationData(_ presentationData: PresentationData) { self.presentationData = presentationData - self.searchBar.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: presentationData.theme, hasSeparator: false), strings: presentationData.strings) + self.searchBar.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: presentationData.theme, hasSeparator: false), presentationTheme: presentationData.theme, strings: presentationData.strings) } } diff --git a/submodules/LocationUI/Sources/LocationSectionHeaderItem.swift b/submodules/LocationUI/Sources/LocationSectionHeaderItem.swift index 42f8bd29..3aeae080 100644 --- a/submodules/LocationUI/Sources/LocationSectionHeaderItem.swift +++ b/submodules/LocationUI/Sources/LocationSectionHeaderItem.swift @@ -58,7 +58,7 @@ private class LocationSectionHeaderItemNode: ListViewItemNode { private var layoutParams: ListViewItemLayoutParams? required init() { - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) } override func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) { diff --git a/submodules/LocationUI/Sources/LocationViewController.swift b/submodules/LocationUI/Sources/LocationViewController.swift index 173b9356..1a19c949 100644 --- a/submodules/LocationUI/Sources/LocationViewController.swift +++ b/submodules/LocationUI/Sources/LocationViewController.swift @@ -109,7 +109,7 @@ public final class LocationViewController: ViewController { } strongSelf.presentationData = presentationData - strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: strongSelf.presentationData.theme).withUpdatedSeparatorColor(.clear), strings: NavigationBarStrings(presentationStrings: strongSelf.presentationData.strings))) + strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: strongSelf.presentationData.theme).withUpdatedSeparatorColor(.clear), strings: NavigationBarStrings(presentationStrings: strongSelf.presentationData.strings)), transition: .immediate) strongSelf.updateRightBarButton() diff --git a/submodules/MediaPickerUI/BUILD b/submodules/MediaPickerUI/BUILD index 3a979c45..046b14d0 100644 --- a/submodules/MediaPickerUI/BUILD +++ b/submodules/MediaPickerUI/BUILD @@ -57,6 +57,7 @@ swift_library( "//submodules/TelegramUI/Components/EdgeEffect", "//submodules/TelegramUI/Components/GlassBarButtonComponent", "//submodules/TelegramUI/Components/LottieComponent", + "//submodules/TelegramUI/Components/AlertComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/MediaPickerUI/Sources/MediaGroupsAlbumGridItem.swift b/submodules/MediaPickerUI/Sources/MediaGroupsAlbumGridItem.swift index 0f9a4763..dfeba384 100644 --- a/submodules/MediaPickerUI/Sources/MediaGroupsAlbumGridItem.swift +++ b/submodules/MediaPickerUI/Sources/MediaGroupsAlbumGridItem.swift @@ -130,7 +130,7 @@ private final class MediaGroupsGridAlbumItemNode : ListViewItemNode { self.countNode = TextNode() self.countNode.isUserInteractionEnabled = false - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.containerNode) self.containerNode.addSubnode(self.imageNode) @@ -283,7 +283,7 @@ private class MediaGroupsAlbumGridItemNode: ListViewItemNode { self.listNode = ListView() self.listNode.transform = CATransform3DMakeRotation(-CGFloat.pi / 2.0, 0.0, 0.0, 1.0) - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.listNode) } diff --git a/submodules/MediaPickerUI/Sources/MediaGroupsAlbumItem.swift b/submodules/MediaPickerUI/Sources/MediaGroupsAlbumItem.swift index 7cc61779..d086a038 100644 --- a/submodules/MediaPickerUI/Sources/MediaGroupsAlbumItem.swift +++ b/submodules/MediaPickerUI/Sources/MediaGroupsAlbumItem.swift @@ -172,7 +172,7 @@ class MediaGroupsAlbumItemNode: ListViewItemNode { self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.iconNode) self.addSubnode(self.titleNode) diff --git a/submodules/MediaPickerUI/Sources/MediaGroupsHeaderItem.swift b/submodules/MediaPickerUI/Sources/MediaGroupsHeaderItem.swift index b934d07d..409f895d 100644 --- a/submodules/MediaPickerUI/Sources/MediaGroupsHeaderItem.swift +++ b/submodules/MediaPickerUI/Sources/MediaGroupsHeaderItem.swift @@ -59,7 +59,7 @@ private class MediaGroupsHeaderItemNode: ListViewItemNode { init() { self.titleNode = TextNode() - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.titleNode) } diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index b59c8fe0..a561b2df 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -33,6 +33,7 @@ import ComponentFlow import BundleIconComponent import LottieComponent import GlassBarButtonComponent +import AlertComponent final class MediaPickerInteraction { let downloadManager: AssetDownloadManager @@ -1436,7 +1437,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att } if asFile && hasHeic { - controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.MediaPicker_JpegConversionText, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.MediaPicker_KeepHeic, action: { + controller.present(textAlertController(context: controller.context, updatedPresentationData: self.controller?.updatedPresentationData, title: nil, text: self.presentationData.strings.MediaPicker_JpegConversionText, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.MediaPicker_KeepHeic, action: { proceed(false) }), TextAlertAction(type: .genericAction, title: self.presentationData.strings.MediaPicker_ConvertToJpeg, action: { proceed(true) @@ -2042,26 +2043,26 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att if let _ = item as? TGMediaPickerGalleryPhotoItem { if self.bannedSendPhotos != nil { - self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Chat_SendNotAllowedPhoto, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + self.present(textAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, title: nil, text: self.presentationData.strings.Chat_SendNotAllowedPhoto, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) return false } } else if let _ = item as? TGMediaPickerGalleryVideoItem { if self.bannedSendVideos != nil { - self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Chat_SendNotAllowedVideo, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + self.present(textAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, title: nil, text: self.presentationData.strings.Chat_SendNotAllowedVideo, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) return false } } else if let asset = item as? TGMediaAsset { if asset.isVideo { if self.bannedSendVideos != nil { - self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Chat_SendNotAllowedVideo, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + self.present(textAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, title: nil, text: self.presentationData.strings.Chat_SendNotAllowedVideo, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) return false } } else { if self.bannedSendPhotos != nil { - self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Chat_SendNotAllowedPhoto, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + self.present(textAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, title: nil, text: self.presentationData.strings.Chat_SendNotAllowedPhoto, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) return false } @@ -2112,6 +2113,10 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att } } + self.selectedButtonNode.action = { [weak self] in + self?.selectedPressed() + } + self.navigationItem.titleView = self.titleView if case let .assets(collection, mode) = self.subject, mode != .default { @@ -2184,8 +2189,6 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att // } // } - self.selectedButtonNode.addTarget(self, action: #selector(self.selectedPressed), forControlEvents: .touchUpInside) - self.scrollToTop = { [weak self] in if let strongSelf = self { if let webSearchController = strongSelf.webSearchController { @@ -2220,26 +2223,26 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att if let self = self, let selectionState = self.interaction?.selectionState { if let _ = item as? TGMediaPickerGalleryPhotoItem { if self.bannedSendPhotos != nil { - self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Chat_SendNotAllowedPhoto, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + self.present(textAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, title: nil, text: self.presentationData.strings.Chat_SendNotAllowedPhoto, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) return false } } else if let _ = item as? TGMediaPickerGalleryVideoItem { if self.bannedSendVideos != nil { - self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Chat_SendNotAllowedVideo, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + self.present(textAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, title: nil, text: self.presentationData.strings.Chat_SendNotAllowedVideo, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) return false } } else if let asset = item as? TGMediaAsset { if asset.isVideo { if self.bannedSendVideos != nil { - self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Chat_SendNotAllowedVideo, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + self.present(textAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, title: nil, text: self.presentationData.strings.Chat_SendNotAllowedVideo, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) return false } } else { if self.bannedSendPhotos != nil { - self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Chat_SendNotAllowedPhoto, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + self.present(textAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, title: nil, text: self.presentationData.strings.Chat_SendNotAllowedPhoto, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) return false } @@ -2548,7 +2551,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att let useGlassButtons = (isBack || !self.controllerNode.scrolledToTop) && !self.controllerNode.isSwitchingAssetGroup let barButtonSideInset: CGFloat = 16.0 - let barButtonSize = CGSize(width: 40.0, height: 40.0) + let barButtonSize = CGSize(width: 44.0, height: 44.0) var buttonTransition = ComponentTransition.easeInOut(duration: 0.25) if case let .animated(duration, _) = transition, duration > 0.25 { @@ -2580,13 +2583,13 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att transition: buttonTransition, component: AnyComponent(GlassBarButtonComponent( size: barButtonSize, - backgroundColor: self.presentationData.theme.rootController.navigationBar.glassBarButtonBackgroundColor, + backgroundColor: nil, isDark: self.presentationData.theme.overallDarkAppearance, - state: useGlassButtons ? .glass : .generic, + state: .glass, component: AnyComponentWithIdentity(id: isBack ? "back" : "close", component: AnyComponent( BundleIconComponent( name: isBack ? "Navigation/Back" : "Navigation/Close", - tintColor: self.presentationData.theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: self.presentationData.theme.chat.inputPanel.panelControlColor ) )), action: { [weak self] _ in @@ -2611,15 +2614,15 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att transition: buttonTransition, component: AnyComponent(GlassBarButtonComponent( size: barButtonSize, - backgroundColor: self.presentationData.theme.rootController.navigationBar.glassBarButtonBackgroundColor, + backgroundColor: nil, isDark: self.presentationData.theme.overallDarkAppearance, - state: useGlassButtons ? .glass : .generic, + state: .glass, component: AnyComponentWithIdentity(id: "more", component: AnyComponent( LottieComponent( content: LottieComponent.AppBundleContent( name: "anim_morewide" ), - color: self.presentationData.theme.rootController.navigationBar.glassBarButtonForegroundColor, + color: self.presentationData.theme.chat.inputPanel.panelControlColor, size: CGSize(width: 34.0, height: 34.0), playOnce: self.moreButtonPlayOnce ) @@ -2673,7 +2676,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att } else { navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData) } - self.navigationBar?.updatePresentationData(navigationBarPresentationData) + self.navigationBar?.updatePresentationData(navigationBarPresentationData, transition: .immediate) self.titleView.theme = self.presentationData.theme self.cancelButtonNode.theme = self.presentationData.theme self.moreButtonNode.theme = self.presentationData.theme @@ -2717,16 +2720,22 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att } else { text = self.presentationData.strings.Attachment_CancelSelectionAlertText } - - let controller = textAlertController(context: self.context, title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Attachment_CancelSelectionAlertNo, action: { - }), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Attachment_CancelSelectionAlertYes, action: { [weak self] in - self?.dismissAllTooltips() - completion() - })]) - controller.dismissed = { [weak self] _ in + let alertController = AlertScreen( + context: self.context, + title: nil, + text: text, + actions: [ + .init(title: self.presentationData.strings.Attachment_CancelSelectionAlertNo), + .init(title: self.presentationData.strings.Attachment_CancelSelectionAlertYes, type: .default, action: { [weak self] in + self?.dismissAllTooltips() + completion() + }), + ] + ) + alertController.dismissed = { [weak self] _ in self?.isDismissing = false } - self.present(controller, in: .window(.root)) + self.present(alertController, in: .window(.root)) } else { completion() } @@ -3777,126 +3786,188 @@ public func avatarMediaPickerController( performDelete: @escaping () -> Void, completion: @escaping (Any?, UIView?, CGRect, UIImage?, Bool, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void -) -> ViewController { - let presentationData = context.sharedContext.currentPresentationData.with({ $0 }) - let updatedPresentationData: (PresentationData, Signal) = (presentationData, .single(presentationData)) - let controller = AttachmentController( - context: context, - updatedPresentationData: updatedPresentationData, - style: .glass, - chatLocation: nil, - buttons: [.standalone], - initialButton: .standalone, - fromMenu: false, - hasTextInput: false, - makeEntityInputView: { - return nil - }) - controller.forceSourceRect = true - controller.getSourceRect = getSourceRect - controller.requestController = { [weak controller] _, present in - var mainButtonState: AttachmentMainButtonState? - - if canDelete { - mainButtonState = AttachmentMainButtonState(text: presentationData.strings.MediaPicker_RemovePhoto, font: .regular, background: .color(.clear), textColor: presentationData.theme.actionSheet.destructiveActionTextColor, isVisible: true, progress: .none, isEnabled: true, hasShimmer: false) +) -> (controller: ViewController?, holder: Any?) { + if #available(iOS 14.0, *), PHPhotoLibrary.authorizationStatus(for: .readWrite) != .authorized { + final class PickerDelegate: NSObject, PHPickerViewControllerDelegate { + var completion: ((Any?, UIView?, CGRect, UIImage?, Bool, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void)? + + func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { + picker.dismiss(animated: true) + + for item in results { + if item.itemProvider.canLoadObject(ofClass: UIImage.self) { + item.itemProvider.loadObject(ofClass: UIImage.self) { image, error in + if let uiImage = image as? UIImage { + Queue.mainQueue().async { + self.completion?(uiImage, nil, CGRect(), nil, false, { _ in return nil }, {}) + } + } + } + } + } + } } - let mediaPickerController = MediaPickerScreenImpl( + let holder = PickerDelegate() + holder.completion = completion + + let openMediaPicker = { + var configuration = PHPickerConfiguration(photoLibrary: .shared()) + configuration.filter = .images + configuration.selectionLimit = 1 + + let picker = PHPickerViewController(configuration: configuration) + picker.delegate = holder + (context.sharedContext.mainWindow?.viewController as? NavigationController)?.topViewController?.present(picker, animated: true, completion: nil) + } + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let controller = ActionSheetController(presentationData: presentationData) + let dismissAction: () -> Void = { [weak controller] in + controller?.dismissAnimated() + } + + var items: [ActionSheetButtonItem] = [ + ActionSheetButtonItem(title: presentationData.strings.Settings_SetNewProfilePhotoOrVideo, color: .accent, action: { + dismissAction() + openMediaPicker() + }), + ActionSheetButtonItem(title: presentationData.strings.ProfilePhoto_SetEmoji, color: .accent, action: { + dismissAction() + completion(nil, nil, CGRect(), nil, false, { _ in return nil }, {}) + }) + ] + if canDelete { + items.append(ActionSheetButtonItem(title: presentationData.strings.MediaPicker_RemovePhoto, color: .destructive, action: { + dismissAction() + performDelete() + })) + } + controller.setItemGroups([ + ActionSheetItemGroup(items: items), + ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) + ]) + return (controller, holder) + } else { + let presentationData = context.sharedContext.currentPresentationData.with({ $0 }) + let updatedPresentationData: (PresentationData, Signal) = (presentationData, .single(presentationData)) + let controller = AttachmentController( context: context, updatedPresentationData: updatedPresentationData, style: .glass, - peer: nil, - threadTitle: nil, chatLocation: nil, - bannedSendPhotos: nil, - bannedSendVideos: nil, - subject: .assets(nil, .createAvatar), - mainButtonState: mainButtonState, - mainButtonAction: { [weak controller] in - controller?.dismiss(animated: true) - performDelete() - } - ) - mediaPickerController.customSelection = { controller, result in - if let result = result as? PHAsset { - controller.updateHiddenMediaId(result.localIdentifier) - if let transitionView = controller.transitionView(for: result.localIdentifier, snapshot: false) { - let transitionOut: (Bool?) -> (UIView, CGRect)? = { isNew in - if let isNew { - if isNew { - controller.updateHiddenMediaId(nil) - if let transitionView = controller.defaultTransitionView() { - return (transitionView, transitionView.bounds) - } - } else if let transitionView = controller.transitionView(for: result.localIdentifier, snapshot: false) { - return (transitionView, transitionView.bounds) - } - } - return nil - } - completion(result, transitionView, transitionView.bounds, controller.transitionImage(for: result.localIdentifier), false, transitionOut, { [weak controller] in - controller?.updateHiddenMediaId(nil) - }) - } - } - } - mediaPickerController.openAvatarEditor = { [weak controller] in - completion(nil, nil, .zero, nil, false, { _ in return nil }, { + buttons: [.standalone], + initialButton: .standalone, + fromMenu: false, + hasTextInput: false, + makeEntityInputView: { + return nil }) - controller?.dismiss(animated: true) - } - mediaPickerController.openCamera = { [weak controller] cameraHolder in - let _ = controller - guard let cameraHolder = cameraHolder as? CameraHolder else { - return + controller.forceSourceRect = true + controller.getSourceRect = getSourceRect + controller.requestController = { [weak controller] _, present in + var mainButtonState: AttachmentMainButtonState? + + if canDelete { + mainButtonState = AttachmentMainButtonState(text: presentationData.strings.MediaPicker_RemovePhoto, font: .regular, background: .color(.clear), textColor: presentationData.theme.actionSheet.destructiveActionTextColor, isVisible: true, progress: .none, isEnabled: true, hasShimmer: false) } - var returnToCameraImpl: (() -> Void)? - - let cameraScreen = context.sharedContext.makeCameraScreen( + let mediaPickerController = MediaPickerScreenImpl( context: context, - mode: .avatar, - cameraHolder: cameraHolder, - transitionIn: CameraScreenTransitionIn( - sourceView: cameraHolder.parentView, - sourceRect: cameraHolder.parentView.bounds, - sourceCornerRadius: 0.0, - useFillAnimation: false - ), - transitionOut: { _ in - return CameraScreenTransitionOut( - destinationView: cameraHolder.parentView, - destinationRect: cameraHolder.parentView.bounds, - destinationCornerRadius: 0.0 - ) - }, - completion: { result, commit in - completion(result, nil, .zero, nil, true, { _ in return nil }, { - returnToCameraImpl?() - }) - }, - transitionedOut: { [weak cameraHolder] in - if let cameraHolder { - cameraHolder.restore() - } + updatedPresentationData: updatedPresentationData, + style: .glass, + peer: nil, + threadTitle: nil, + chatLocation: nil, + bannedSendPhotos: nil, + bannedSendVideos: nil, + subject: .assets(nil, .createAvatar), + mainButtonState: mainButtonState, + mainButtonAction: { [weak controller] in + controller?.dismiss(animated: true) + performDelete() } ) - controller?.push(cameraScreen) - - returnToCameraImpl = { [weak cameraScreen] in - if let cameraScreen = cameraScreen as? CameraScreen { - cameraScreen.returnFromEditor() + mediaPickerController.customSelection = { controller, result in + if let result = result as? PHAsset { + controller.updateHiddenMediaId(result.localIdentifier) + if let transitionView = controller.transitionView(for: result.localIdentifier, snapshot: false) { + let transitionOut: (Bool?) -> (UIView, CGRect)? = { isNew in + if let isNew { + if isNew { + controller.updateHiddenMediaId(nil) + if let transitionView = controller.defaultTransitionView() { + return (transitionView, transitionView.bounds) + } + } else if let transitionView = controller.transitionView(for: result.localIdentifier, snapshot: false) { + return (transitionView, transitionView.bounds) + } + } + return nil + } + completion(result, transitionView, transitionView.bounds, controller.transitionImage(for: result.localIdentifier), false, transitionOut, { [weak controller] in + controller?.updateHiddenMediaId(nil) + }) + } } } + mediaPickerController.openAvatarEditor = { [weak controller] in + completion(nil, nil, .zero, nil, false, { _ in return nil }, { + }) + controller?.dismiss(animated: true) + } + mediaPickerController.openCamera = { [weak controller] cameraHolder in + let _ = controller + guard let cameraHolder = cameraHolder as? CameraHolder else { + return + } + + var returnToCameraImpl: (() -> Void)? + + let cameraScreen = context.sharedContext.makeCameraScreen( + context: context, + mode: .avatar, + cameraHolder: cameraHolder, + transitionIn: CameraScreenTransitionIn( + sourceView: cameraHolder.parentView, + sourceRect: cameraHolder.parentView.bounds, + sourceCornerRadius: 0.0, + useFillAnimation: false + ), + transitionOut: { _ in + return CameraScreenTransitionOut( + destinationView: cameraHolder.parentView, + destinationRect: cameraHolder.parentView.bounds, + destinationCornerRadius: 0.0 + ) + }, + completion: { result, commit in + completion(result, nil, .zero, nil, true, { _ in return nil }, { + returnToCameraImpl?() + }) + }, + transitionedOut: { [weak cameraHolder] in + if let cameraHolder { + cameraHolder.restore() + } + } + ) + controller?.push(cameraScreen) + + returnToCameraImpl = { [weak cameraScreen] in + if let cameraScreen = cameraScreen as? CameraScreen { + cameraScreen.returnFromEditor() + } + } + } + present(mediaPickerController, mediaPickerController.mediaPickerContext) } - present(mediaPickerController, mediaPickerController.mediaPickerContext) + controller.willDismiss = { + dismissed() + } + controller.navigationPresentation = .flatModal + controller.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) + return (controller, nil) } - controller.willDismiss = { - dismissed() - } - controller.navigationPresentation = .flatModal - controller.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) - return controller } @@ -3961,12 +4032,13 @@ public func coverMediaPickerController( return controller } -private class SelectedButtonNode: HighlightTrackingButtonNode { +private class SelectedButtonNode: ASDisplayNode { private let containerView: UIView private let backgroundView: GlassBackgroundView? private let background: ASImageNode? private let icon = ASImageNode() private let label = ImmediateAnimatedCountLabelNode() + private let button = HighlightTrackingButton() private let glass: Bool @@ -3982,6 +4054,8 @@ private class SelectedButtonNode: HighlightTrackingButtonNode { private var count: Int32 = 0 + var action: () -> Void = {} + init(theme: PresentationTheme, glass: Bool) { self.theme = theme self.glass = glass @@ -4007,6 +4081,9 @@ private class SelectedButtonNode: HighlightTrackingButtonNode { self.view.addSubview(self.containerView) if let backgroundView = self.backgroundView { + backgroundView.contentView.addSubnode(self.icon) + backgroundView.contentView.addSubnode(self.label) + backgroundView.contentView.addSubview(self.button) self.containerView.addSubview(backgroundView) } if let background = self.background { @@ -4014,35 +4091,19 @@ private class SelectedButtonNode: HighlightTrackingButtonNode { self.containerView.addSubnode(background) } - self.containerView.addSubnode(self.icon) - self.containerView.addSubnode(self.label) + - self.highligthedChanged = { [weak self] highlighted in - if let self { - if glass { - let transition = ComponentTransition(animation: .curve(duration: highlighted ? 0.25 : 0.35, curve: .spring)) - if highlighted { - transition.setScale(view: self.containerView, scale: 1.2) - } else { - transition.setScale(view: self.containerView, scale: 1.0) - } - } else { - if highlighted { - self.containerView.layer.removeAnimation(forKey: "opacity") - self.containerView.alpha = 0.4 - } else { - self.containerView.alpha = 1.0 - self.containerView.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) - } - } - } - } + self.button.addTarget(self, action: #selector(self.tapped), for: .touchUpInside) + } + + @objc private func tapped() { + self.action() } func update(count: Int32) -> CGSize { self.count = count - let diameter: CGFloat = self.glass ? 40.0 : 21.0 + let diameter: CGFloat = self.glass ? 44.0 : 21.0 let font = self.glass ? Font.with(size: 17.0, weight: .medium, traits: [.monospacedNumbers]) : Font.with(size: 15.0, design: .round, weight: .semibold, traits: [.monospacedNumbers]) let stringValue = "\(max(1, count))" @@ -4078,11 +4139,13 @@ private class SelectedButtonNode: HighlightTrackingButtonNode { self.containerView.frame = backgroundFrame if let backgroundView = self.backgroundView { backgroundView.frame = backgroundFrame - backgroundView.update(size: backgroundFrame.size, cornerRadius: backgroundFrame.size.height * 0.5, isDark: false, tintColor: .init(kind: .custom, color: self.theme.list.itemCheckColors.fillColor), transition: .immediate) + backgroundView.update(size: backgroundFrame.size, cornerRadius: backgroundFrame.size.height * 0.5, isDark: false, tintColor: .init(kind: .custom, color: self.theme.list.itemCheckColors.fillColor), isInteractive: true, transition: .immediate) } if let background = self.background { background.frame = backgroundFrame } + + self.button.frame = CGRect(origin: .zero, size: size) return size } diff --git a/submodules/MediaPickerUI/Sources/MediaPickerTitleView.swift b/submodules/MediaPickerUI/Sources/MediaPickerTitleView.swift index aaaba5dc..cbb89912 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerTitleView.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerTitleView.swift @@ -18,7 +18,7 @@ final class MediaPickerTitleView: UIView { public var theme: PresentationTheme { didSet { - self.titleNode.attributedText = NSAttributedString(string: self.title, font: NavigationBar.titleFont, textColor: self.isDark ? .white : self.theme.rootController.navigationBar.primaryTextColor) + self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers]), textColor: self.isDark ? .white : self.theme.rootController.navigationBar.primaryTextColor) self.subtitleNode.attributedText = NSAttributedString(string: self.subtitle, font: Font.regular(12.0), textColor: self.isDark ? .white.withAlphaComponent(0.5) : self.theme.rootController.navigationBar.secondaryTextColor) self.segmentedControlNode.updateTheme(SegmentedControlTheme(theme: self.theme)) if self.glass { @@ -31,7 +31,7 @@ final class MediaPickerTitleView: UIView { public var isDark: Bool = false { didSet { if self.isDark != oldValue { - self.titleNode.attributedText = NSAttributedString(string: self.title, font: NavigationBar.titleFont, textColor: self.isDark ? .white : self.theme.rootController.navigationBar.primaryTextColor) + self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers]), textColor: self.isDark ? .white : self.theme.rootController.navigationBar.primaryTextColor) self.subtitleNode.attributedText = NSAttributedString(string: self.subtitle, font: Font.regular(12.0), textColor: self.isDark ? .white.withAlphaComponent(0.5) : self.theme.rootController.navigationBar.secondaryTextColor) if self.glass { self.arrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Navigation/TitleExpand"), color: self.isDark ? UIColor.white.withAlphaComponent(0.5) : self.theme.rootController.navigationBar.primaryTextColor.withAlphaComponent(0.4)) @@ -44,7 +44,7 @@ final class MediaPickerTitleView: UIView { public var title: String = "" { didSet { if self.title != oldValue { - self.titleNode.attributedText = NSAttributedString(string: self.title, font: NavigationBar.titleFont, textColor: self.isDark ? .white : self.theme.rootController.navigationBar.primaryTextColor) + self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers]), textColor: self.isDark ? .white : self.theme.rootController.navigationBar.primaryTextColor) self.setNeedsLayout() } } @@ -260,7 +260,7 @@ final class MediaPickerTitleView: UIView { totalHeight += subtitleSize.height } - let verticalOffset: CGFloat = self.glass ? 3.0 : 0.0 + let verticalOffset: CGFloat = self.glass ? -1.0 : 0.0 let arrowOffset: CGFloat = self.glass ? 1.0 : 5.0 var totalWidth = titleSize.width diff --git a/submodules/MtProtoKit/Sources/MTApiEnvironment.m b/submodules/MtProtoKit/Sources/MTApiEnvironment.m index e4d60104..14197ec5 100644 --- a/submodules/MtProtoKit/Sources/MTApiEnvironment.m +++ b/submodules/MtProtoKit/Sources/MTApiEnvironment.m @@ -1,7 +1,7 @@ #import #if TARGET_OS_IPHONE -# import +#import #else #endif @@ -10,888 +10,996 @@ #import -static NSData * _Nullable parseHexString(NSString * _Nonnull hex) { - if ([hex length] % 2 != 0) { - return nil; +static NSData *_Nullable parseHexString(NSString *_Nonnull hex) { + if ([hex length] % 2 != 0) { + return nil; + } + char buf[3]; + buf[2] = '\0'; + uint8_t *bytes = (uint8_t *)malloc(hex.length / 2); + uint8_t *bp = bytes; + for (CFIndex i = 0; i < [hex length]; i += 2) { + buf[0] = [hex characterAtIndex:i]; + buf[1] = [hex characterAtIndex:i + 1]; + char *b2 = NULL; + *bp++ = strtol(buf, &b2, 16); + if (b2 != buf + 2) { + return nil; } - char buf[3]; - buf[2] = '\0'; - uint8_t *bytes = (uint8_t *)malloc(hex.length / 2); - uint8_t *bp = bytes; - for (CFIndex i = 0; i < [hex length]; i += 2) { - buf[0] = [hex characterAtIndex:i]; - buf[1] = [hex characterAtIndex:i+1]; - char *b2 = NULL; - *bp++ = strtol(buf, &b2, 16); - if (b2 != buf + 2) { - return nil; - } - } - - return [NSData dataWithBytesNoCopy:bytes length:[hex length]/2 freeWhenDone:YES]; + } + + return [NSData dataWithBytesNoCopy:bytes + length:[hex length] / 2 + freeWhenDone:YES]; } -static NSString * _Nonnull dataToHexString(NSData * _Nonnull data) { - const unsigned char *dataBuffer = (const unsigned char *)[data bytes]; - if (dataBuffer == NULL) { - return @""; - } - - NSUInteger dataLength = [data length]; - NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)]; - - for (int i = 0; i < (int)dataLength; ++i) { - [hexString appendString:[NSString stringWithFormat:@"%02lx", (unsigned long)dataBuffer[i]]]; - } - - return hexString; +static NSString *_Nonnull dataToHexString(NSData *_Nonnull data) { + const unsigned char *dataBuffer = (const unsigned char *)[data bytes]; + if (dataBuffer == NULL) { + return @""; + } + + NSUInteger dataLength = [data length]; + NSMutableString *hexString = + [NSMutableString stringWithCapacity:(dataLength * 2)]; + + for (int i = 0; i < (int)dataLength; ++i) { + [hexString + appendString:[NSString stringWithFormat:@"%02lx", + (unsigned long)dataBuffer[i]]]; + } + + return hexString; } static NSData *base64_decode(NSString *str) { - if ([NSData instancesRespondToSelector:@selector(initWithBase64EncodedString:options:)]) { - NSData *data = [[NSData alloc] initWithBase64EncodedString:str options:NSDataBase64DecodingIgnoreUnknownCharacters]; - return data; - } else { + if ([NSData instancesRespondToSelector:@selector + (initWithBase64EncodedString:options:)]) { + NSData *data = [[NSData alloc] + initWithBase64EncodedString:str + options: + NSDataBase64DecodingIgnoreUnknownCharacters]; + return data; + } else { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" - return [[NSData alloc] initWithBase64Encoding:[str stringByReplacingOccurrencesOfString:@"[^A-Za-z0-9+/=]" withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(0, [str length])]]; + return [[NSData alloc] + initWithBase64Encoding: + [str stringByReplacingOccurrencesOfString:@"[^A-Za-z0-9+/=]" + withString:@"" + options:NSRegularExpressionSearch + range:NSMakeRange( + 0, [str length])]]; #pragma clang diagnostic pop - } + } } @implementation MTProxySecret -- (instancetype _Nullable)initWithSecret:(NSData * _Nonnull)secret { - self = [super init]; - if (self != nil) { - _secret = secret; - } - return self; +- (instancetype _Nullable)initWithSecret:(NSData *_Nonnull)secret { + self = [super init]; + if (self != nil) { + _secret = secret; + } + return self; } - (instancetype)initWithCoder:(NSCoder *)aDecoder { - self = [super init]; - if (self != nil) { - _secret = [aDecoder decodeObjectForKey:@"secret"]; - } - return self; + self = [super init]; + if (self != nil) { + _secret = [aDecoder decodeObjectForKey:@"secret"]; + } + return self; } - (void)encodeWithCoder:(NSCoder *)aCoder { - [aCoder encodeObject:_secret forKey:@"secret"]; + [aCoder encodeObject:_secret forKey:@"secret"]; } -+ (MTProxySecret * _Nullable)parse:(NSString * _Nonnull)string { - NSData *hexData = parseHexString(string); - if (hexData == nil) { - NSString *finalString = @""; - finalString = [finalString stringByAppendingString:[string stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"="]]]; - finalString = [finalString stringByReplacingOccurrencesOfString:@"-" withString:@"+"]; - finalString = [finalString stringByReplacingOccurrencesOfString:@"_" withString:@"/"]; - while (finalString.length % 4 != 0) { - finalString = [finalString stringByAppendingString:@"="]; - } - - hexData = base64_decode(finalString); ++ (MTProxySecret *_Nullable)parse:(NSString *_Nonnull)string { + NSData *hexData = parseHexString(string); + if (hexData == nil) { + NSString *finalString = @""; + finalString = [finalString + stringByAppendingString: + [string + stringByTrimmingCharactersInSet: + [NSCharacterSet characterSetWithCharactersInString:@"="]]]; + finalString = [finalString stringByReplacingOccurrencesOfString:@"-" + withString:@"+"]; + finalString = [finalString stringByReplacingOccurrencesOfString:@"_" + withString:@"/"]; + while (finalString.length % 4 != 0) { + finalString = [finalString stringByAppendingString:@"="]; } - if (hexData != nil) { - return [self parseData:hexData]; - } else { - return nil; - } -} -+ (MTProxySecret * _Nullable)parseData:(NSData * _Nonnull)data { - if (data == nil || data.length < 16) { - return nil; - } - - uint8_t firstByte = 0; - [data getBytes:&firstByte length:1]; - - if (data.length == 16) { - return [[MTProxySecretType0 alloc] initWithSecret:data]; - } else if (data.length == 17) { - if (firstByte == 0xdd) { - return [[MTProxySecretType1 alloc] initWithSecret:[data subdataWithRange:NSMakeRange(1, 16)]]; - } else { - return nil; - } - } else if (data.length >= 18 && firstByte == 0xee) { - NSString *domain = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(1 + 16, data.length - (1 + 16))] encoding:NSUTF8StringEncoding]; - if (domain == nil) { - return nil; - } - return [[MTProxySecretType2 alloc] initWithSecret:[data subdataWithRange:NSMakeRange(1, 16)] domain:domain]; - } else { - return nil; - } -} - -- (NSData * _Nonnull)serialize { - assert(false); + hexData = base64_decode(finalString); + } + if (hexData != nil) { + return [self parseData:hexData]; + } else { return nil; + } } -- (NSString * _Nonnull)serializeToString { - assert(false); ++ (MTProxySecret *_Nullable)parseData:(NSData *_Nonnull)data { + if (data == nil || data.length < 16) { return nil; + } + + uint8_t firstByte = 0; + [data getBytes:&firstByte length:1]; + + if (data.length == 16) { + return [[MTProxySecretType0 alloc] initWithSecret:data]; + } else if (data.length == 17) { + if (firstByte == 0xdd) { + return [[MTProxySecretType1 alloc] + initWithSecret:[data subdataWithRange:NSMakeRange(1, 16)]]; + } else { + return nil; + } + } else if (data.length >= 18 && firstByte == 0xee) { + NSString *domain = [[NSString alloc] + initWithData:[data subdataWithRange:NSMakeRange(1 + 16, + data.length - (1 + 16))] + encoding:NSUTF8StringEncoding]; + if (domain == nil) { + return nil; + } + return [[MTProxySecretType2 alloc] + initWithSecret:[data subdataWithRange:NSMakeRange(1, 16)] + domain:domain]; + } else { + return nil; + } +} + +- (NSData *_Nonnull)serialize { + assert(false); + return nil; +} + +- (NSString *_Nonnull)serializeToString { + assert(false); + return nil; } - (NSString *)description { - return dataToHexString([self serialize]); + return dataToHexString([self serialize]); } @end @implementation MTProxySecretType0 -- (instancetype _Nullable)initWithSecret:(NSData * _Nonnull)secret { - self = [super initWithSecret:secret]; - if (self != nil) { - } - return self; +- (instancetype _Nullable)initWithSecret:(NSData *_Nonnull)secret { + self = [super initWithSecret:secret]; + if (self != nil) { + } + return self; } - (instancetype)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if (self != nil) { - } - return self; + self = [super initWithCoder:aDecoder]; + if (self != nil) { + } + return self; } - (void)encodeWithCoder:(NSCoder *)aCoder { - [super encodeWithCoder:aCoder]; + [super encodeWithCoder:aCoder]; } -- (NSData * _Nonnull)serialize { - return self.secret; +- (NSData *_Nonnull)serialize { + return self.secret; } -- (NSString * _Nonnull)serializeToString { - return dataToHexString(self.serialize); +- (NSString *_Nonnull)serializeToString { + return dataToHexString(self.serialize); } - (BOOL)isEqual:(id)object { - if (![object isKindOfClass:[MTProxySecretType0 class]]) { - return false; - } - MTProxySecretType0 *other = object; - if (![self.secret isEqual:other.secret]) { - return false; - } - return true; + if (![object isKindOfClass:[MTProxySecretType0 class]]) { + return false; + } + MTProxySecretType0 *other = object; + if (![self.secret isEqual:other.secret]) { + return false; + } + return true; } @end @implementation MTProxySecretType1 -- (instancetype _Nullable)initWithSecret:(NSData * _Nonnull)secret { - self = [super initWithSecret:secret]; - if (self != nil) { - } - return self; +- (instancetype _Nullable)initWithSecret:(NSData *_Nonnull)secret { + self = [super initWithSecret:secret]; + if (self != nil) { + } + return self; } - (instancetype)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if (self != nil) { - } - return self; + self = [super initWithCoder:aDecoder]; + if (self != nil) { + } + return self; } - (void)encodeWithCoder:(NSCoder *)aCoder { - [super encodeWithCoder:aCoder]; + [super encodeWithCoder:aCoder]; } -- (NSData * _Nonnull)serialize { - NSMutableData *data = [[NSMutableData alloc] init]; - uint8_t marker = 0xdd; - [data appendBytes:&marker length:1]; - [data appendData:self.secret]; - return data; +- (NSData *_Nonnull)serialize { + NSMutableData *data = [[NSMutableData alloc] init]; + uint8_t marker = 0xdd; + [data appendBytes:&marker length:1]; + [data appendData:self.secret]; + return data; } -- (NSString * _Nonnull)serializeToString { - return dataToHexString(self.serialize); +- (NSString *_Nonnull)serializeToString { + return dataToHexString(self.serialize); } - (BOOL)isEqual:(id)object { - if (![object isKindOfClass:[MTProxySecretType1 class]]) { - return false; - } - MTProxySecretType1 *other = object; - if (![self.secret isEqual:other.secret]) { - return false; - } - return true; + if (![object isKindOfClass:[MTProxySecretType1 class]]) { + return false; + } + MTProxySecretType1 *other = object; + if (![self.secret isEqual:other.secret]) { + return false; + } + return true; } @end @implementation MTProxySecretType2 -- (instancetype _Nullable)initWithSecret:(NSData * _Nonnull)secret domain:(NSString * _Nonnull)domain { - self = [super initWithSecret:secret]; - if (self != nil) { - _domain = domain; - } - return self; +- (instancetype _Nullable)initWithSecret:(NSData *_Nonnull)secret + domain:(NSString *_Nonnull)domain { + self = [super initWithSecret:secret]; + if (self != nil) { + _domain = domain; + } + return self; } - (instancetype)initWithCoder:(NSCoder *)aDecoder { - self = [super initWithCoder:aDecoder]; - if (self != nil) { - _domain = [aDecoder decodeObjectForKey:@"domain"]; - } - return self; + self = [super initWithCoder:aDecoder]; + if (self != nil) { + _domain = [aDecoder decodeObjectForKey:@"domain"]; + } + return self; } - (void)encodeWithCoder:(NSCoder *)aCoder { - [super encodeWithCoder:aCoder]; - [aCoder encodeObject:_domain forKey:@"domain"]; + [super encodeWithCoder:aCoder]; + [aCoder encodeObject:_domain forKey:@"domain"]; } -- (NSData * _Nonnull)serialize { - NSMutableData *data = [[NSMutableData alloc] init]; - uint8_t marker = 0xee; - [data appendBytes:&marker length:1]; - [data appendData:self.secret]; - [data appendData:[_domain dataUsingEncoding:NSUTF8StringEncoding]]; - return data; +- (NSData *_Nonnull)serialize { + NSMutableData *data = [[NSMutableData alloc] init]; + uint8_t marker = 0xee; + [data appendBytes:&marker length:1]; + [data appendData:self.secret]; + [data appendData:[_domain dataUsingEncoding:NSUTF8StringEncoding]]; + return data; } -- (NSString * _Nonnull)serializeToString { - NSData *data = [self serialize]; - if ([data respondsToSelector:@selector(base64EncodedDataWithOptions:)]) { - return [[data base64EncodedStringWithOptions:kNilOptions] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"="]]; - } else { +- (NSString *_Nonnull)serializeToString { + NSData *data = [self serialize]; + if ([data respondsToSelector:@selector(base64EncodedDataWithOptions:)]) { + return [[data base64EncodedStringWithOptions:kNilOptions] + stringByTrimmingCharactersInSet: + [NSCharacterSet characterSetWithCharactersInString:@"="]]; + } else { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" return [self.serialize base64Encoding]; #pragma clang diagnostic pop - } + } } - (BOOL)isEqual:(id)object { - if (![object isKindOfClass:[MTProxySecretType2 class]]) { - return false; - } - MTProxySecretType2 *other = object; - if (![self.secret isEqual:other.secret]) { - return false; - } - if (![self.domain isEqual:other.domain]) { - return false; - } - return true; + if (![object isKindOfClass:[MTProxySecretType2 class]]) { + return false; + } + MTProxySecretType2 *other = object; + if (![self.secret isEqual:other.secret]) { + return false; + } + if (![self.domain isEqual:other.domain]) { + return false; + } + return true; } @end @implementation MTSocksProxySettings -- (instancetype)initWithIp:(NSString *)ip port:(uint16_t)port username:(NSString *)username password:(NSString *)password secret:(NSData *)secret { - self = [super init]; - if (self != nil) { - _ip = ip; - _port = port; - _username = username; - _password = password; - _secret = secret; - } - return self; +- (instancetype)initWithIp:(NSString *)ip + port:(uint16_t)port + username:(NSString *)username + password:(NSString *)password + secret:(NSData *)secret { + self = [super init]; + if (self != nil) { + _ip = ip; + _port = port; + _username = username; + _password = password; + _secret = secret; + } + return self; } - (BOOL)isEqual:(id)object { - if (![object isKindOfClass:[MTSocksProxySettings class]]) { - return false; - } - MTSocksProxySettings *other = object; - if ((other->_ip != nil) != (_ip != nil) || (_ip != nil && ![_ip isEqual:other->_ip])) { - return false; - } - if (other->_port != _port) { - return false; - } - if ((other->_username != nil) != (_username != nil) || (_username != nil && ![_username isEqual:other->_username])) { - return false; - } - if ((other->_password != nil) != (_password != nil) || (_password != nil && ![_password isEqual:other->_password])) { - return false; - } - if ((other->_secret != nil) != (_secret != nil) || (_secret != nil && ![_secret isEqual:other->_secret])) { - return false; - } - return true; + if (![object isKindOfClass:[MTSocksProxySettings class]]) { + return false; + } + MTSocksProxySettings *other = object; + if ((other->_ip != nil) != (_ip != nil) || + (_ip != nil && ![_ip isEqual:other->_ip])) { + return false; + } + if (other->_port != _port) { + return false; + } + if ((other->_username != nil) != (_username != nil) || + (_username != nil && ![_username isEqual:other->_username])) { + return false; + } + if ((other->_password != nil) != (_password != nil) || + (_password != nil && ![_password isEqual:other->_password])) { + return false; + } + if ((other->_secret != nil) != (_secret != nil) || + (_secret != nil && ![_secret isEqual:other->_secret])) { + return false; + } + return true; } - (NSString *)description { - return [NSString stringWithFormat:@"%@:%d+%@+%@+%@", _ip, (int)_port, _username, _password, [_secret description]]; + return + [NSString stringWithFormat:@"%@:%d+%@+%@+%@", _ip, (int)_port, _username, + _password, [_secret description]]; } @end @implementation MTNetworkSettings -- (instancetype)initWithReducedBackupDiscoveryTimeout:(bool)reducedBackupDiscoveryTimeout { - self = [super init]; - if (self != nil) { - _reducedBackupDiscoveryTimeout = reducedBackupDiscoveryTimeout; - } - return self; +- (instancetype)initWithReducedBackupDiscoveryTimeout: + (bool)reducedBackupDiscoveryTimeout { + self = [super init]; + if (self != nil) { + _reducedBackupDiscoveryTimeout = reducedBackupDiscoveryTimeout; + } + return self; } - (BOOL)isEqual:(id)object { - if (![object isKindOfClass:[MTNetworkSettings class]]) { - return false; - } - MTNetworkSettings *other = object; - if (_reducedBackupDiscoveryTimeout != other->_reducedBackupDiscoveryTimeout) { - return false; - } - return true; + if (![object isKindOfClass:[MTNetworkSettings class]]) { + return false; + } + MTNetworkSettings *other = object; + if (_reducedBackupDiscoveryTimeout != other->_reducedBackupDiscoveryTimeout) { + return false; + } + return true; } @end @implementation MTApiEnvironment --(instancetype)init { - self = [self initWithDeviceModelName:nil]; - if (self != nil) - { - - } - return self; +- (instancetype)init { + self = [self initWithDeviceModelName:nil]; + if (self != nil) { + } + return self; } --(id _Nonnull)initWithDeviceModelName:(NSString * _Nullable)deviceModelName { - self = [super init]; - if (self != nil) - { - if (deviceModelName != nil) { - _deviceModel = deviceModelName; - } else { - _deviceModel = [self platformString]; - } - _deviceModelName = deviceModelName; -#if TARGET_OS_IPHONE - _systemVersion = [[UIDevice currentDevice] systemVersion]; -#else - NSProcessInfo *pInfo = [NSProcessInfo processInfo]; - _systemVersion = [[[pInfo operatingSystemVersionString] componentsSeparatedByString:@" "] objectAtIndex:1]; -#endif - -NSString *suffix = @""; -#if TARGET_OS_OSX - NSString *value = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"SOURCE"]; - if (value != nil) { - suffix = [NSString stringWithFormat:@"%@", value]; - } -#endif - - //SOURCE - NSString *versionString = [[NSString alloc] initWithFormat:@"%@ (%@) %@", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"], [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"], suffix]; - _appVersion = versionString; - - _systemLangCode = [[NSLocale preferredLanguages] objectAtIndex:0]; - #if TARGET_OS_OSX - _langPack = @"macos"; - #else - _langPack = @"ios"; - #endif - _langPackCode = @""; - - [self _updateApiInitializationHash]; +- (id _Nonnull)initWithDeviceModelName:(NSString *_Nullable)deviceModelName { + self = [super init]; + if (self != nil) { + // GHOSTGRAM: Check for device spoofing from UserDefaults + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + BOOL spoofEnabled = [defaults boolForKey:@"DeviceSpoof.isEnabled"]; + NSInteger profileId = + [defaults integerForKey:@"DeviceSpoof.selectedProfileId"]; + + NSString *spoofedModel = nil; + NSString *spoofedVersion = nil; + + if (spoofEnabled && profileId != 0) { + if (profileId == 100) { + // Custom profile + spoofedModel = [defaults stringForKey:@"DeviceSpoof.customDeviceModel"]; + spoofedVersion = + [defaults stringForKey:@"DeviceSpoof.customSystemVersion"]; + } else { + // Preset profiles + NSDictionary *models = @{ + @1 : @"iPhone 14 Pro", + @2 : @"iPhone 15 Pro Max", + @3 : @"Samsung SM-S918B", + @4 : @"Google Pixel 8 Pro", + @5 : @"PC 64bit", + @6 : @"MacBook Pro", + @7 : @"Web", + @8 : @"HUAWEI MNA-LX9", + @9 : @"Xiaomi 2311DRK48G" + }; + NSDictionary *versions = @{ + @1 : @"iOS 17.2", + @2 : @"iOS 17.4", + @3 : @"Android 14", + @4 : @"Android 14", + @5 : @"Windows 11", + @6 : @"macOS 14.3", + @7 : @"Chrome 121", + @8 : @"HarmonyOS 4.0", + @9 : @"Android 14" + }; + spoofedModel = models[@(profileId)]; + spoofedVersion = versions[@(profileId)]; + } } - return self; + + if (spoofedModel.length > 0) { + _deviceModel = spoofedModel; + } else if (deviceModelName != nil) { + _deviceModel = deviceModelName; + } else { + _deviceModel = [self platformString]; + } + _deviceModelName = deviceModelName; + + if (spoofedVersion.length > 0) { + _systemVersion = spoofedVersion; + } else { +#if TARGET_OS_IPHONE + _systemVersion = [[UIDevice currentDevice] systemVersion]; +#else + NSProcessInfo *pInfo = [NSProcessInfo processInfo]; + _systemVersion = [[[pInfo operatingSystemVersionString] + componentsSeparatedByString:@" "] objectAtIndex:1]; +#endif + } + + NSString *suffix = @""; +#if TARGET_OS_OSX + NSString *value = + [[[NSBundle mainBundle] infoDictionary] objectForKey:@"SOURCE"]; + if (value != nil) { + suffix = [NSString stringWithFormat:@"%@", value]; + } +#endif + + // SOURCE + NSString *versionString = [[NSString alloc] + initWithFormat:@"%@ (%@) %@", + [[[NSBundle mainBundle] infoDictionary] + objectForKey:@"CFBundleShortVersionString"], + [[[NSBundle mainBundle] infoDictionary] + objectForKey:@"CFBundleVersion"], + suffix]; + _appVersion = versionString; + + _systemLangCode = [[NSLocale preferredLanguages] objectAtIndex:0]; +#if TARGET_OS_OSX + _langPack = @"macos"; +#else + _langPack = @"ios"; +#endif + _langPackCode = @""; + + [self _updateApiInitializationHash]; + } + return self; } - (void)_updateApiInitializationHash { - _apiInitializationHash = [[NSString alloc] initWithFormat:@"apiId=%" PRId32 "&deviceModel=%@&systemVersion=%@&appVersion=%@&langCode=%@&layer=%@&langPack=%@&langPackCode=%@&proxy=%@&systemCode=%@", _apiId, _deviceModel, _systemVersion, _appVersion, _systemLangCode, _layer, _langPack, _langPackCode, _socksProxySettings, _systemCode]; + _apiInitializationHash = [[NSString alloc] + initWithFormat: + @"apiId=%" PRId32 + "&deviceModel=%@&systemVersion=%@&appVersion=%@&langCode=%@&layer=%@" + "&langPack=%@&langPackCode=%@&proxy=%@&systemCode=%@", + _apiId, _deviceModel, _systemVersion, _appVersion, _systemLangCode, + _layer, _langPack, _langPackCode, _socksProxySettings, _systemCode]; } - (void)setLayer:(NSNumber *)layer { - _layer = layer; - - [self _updateApiInitializationHash]; + _layer = layer; + + [self _updateApiInitializationHash]; } - (void)setAppVersion:(NSString *)appVersion { - _appVersion = appVersion; - - [self _updateApiInitializationHash]; + _appVersion = appVersion; + + [self _updateApiInitializationHash]; } - (void)setLangPack:(NSString *)langPack { - _langPack = langPack; - - [self _updateApiInitializationHash]; + _langPack = langPack; + + [self _updateApiInitializationHash]; } - (void)setLangPackCode:(NSString *)langPackCode { - _langPackCode = langPackCode; - - [self _updateApiInitializationHash]; + _langPackCode = langPackCode; + + [self _updateApiInitializationHash]; } -- (NSString *)platformString -{ +- (NSString *)platformString { #if TARGET_OS_IPHONE - NSString *platform = [self platform]; - - if ([platform isEqualToString:@"iPhone1,1"]) - return @"iPhone"; - if ([platform isEqualToString:@"iPhone1,2"]) - return @"iPhone 3G"; - if ([platform isEqualToString:@"iPhone2,1"]) - return @"iPhone 3GS"; - if ([platform hasPrefix:@"iPhone3"]) - return @"iPhone 4"; - if ([platform hasPrefix:@"iPhone4"]) - return @"iPhone 4S"; - if ([platform isEqualToString:@"iPhone5,1"] || - [platform isEqualToString:@"iPhone5,2"]) - return @"iPhone 5"; - if ([platform isEqualToString:@"iPhone5,3"] || - [platform isEqualToString:@"iPhone5,4"]) - return @"iPhone 5C"; - if ([platform hasPrefix:@"iPhone6"]) - return @"iPhone 5S"; - if ([platform isEqualToString:@"iPhone7,1"]) - return @"iPhone 6 Plus"; - if ([platform isEqualToString:@"iPhone7,2"]) - return @"iPhone 6"; - if ([platform isEqualToString:@"iPhone8,1"]) - return @"iPhone 6S"; - if ([platform isEqualToString:@"iPhone8,2"]) - return @"iPhone 6S Plus"; - if ([platform isEqualToString:@"iPhone8,4"]) - return @"iPhone SE"; - if ([platform isEqualToString:@"iPhone9,1"] || - [platform isEqualToString:@"iPhone9,3"]) - return @"iPhone 7"; - if ([platform isEqualToString:@"iPhone9,2"] || - [platform isEqualToString:@"iPhone9,4"]) - return @"iPhone 7 Plus"; - if ([platform isEqualToString:@"iPhone10,1"] || - [platform isEqualToString:@"iPhone10,4"]) - return @"iPhone 8"; - if ([platform isEqualToString:@"iPhone10,2"] || - [platform isEqualToString:@"iPhone10,5"]) - return @"iPhone 8 Plus"; - if ([platform isEqualToString:@"iPhone10,3"] || - [platform isEqualToString:@"iPhone10,6"]) - return @"iPhone X"; - if ([platform isEqualToString:@"iPhone11,2"]) - return @"iPhone XS"; - if ([platform isEqualToString:@"iPhone11,4"] || - [platform isEqualToString:@"iPhone11,6"]) - return @"iPhone XS Max"; - if ([platform isEqualToString:@"iPhone11,8"]) - return @"iPhone XR"; - if ([platform isEqualToString:@"iPhone12,1"]) - return @"iPhone 11"; - if ([platform isEqualToString:@"iPhone12,3"]) - return @"iPhone 11 Pro"; - if ([platform isEqualToString:@"iPhone12,5"]) - return @"iPhone 11 Pro Max"; - if ([platform isEqualToString:@"iPhone12,8"]) - return @"iPhone SE (2nd gen)"; - if ([platform isEqualToString:@"iPhone13,1"]) - return @"iPhone 12 mini"; - if ([platform isEqualToString:@"iPhone13,2"]) - return @"iPhone 12"; - if ([platform isEqualToString:@"iPhone13,3"]) - return @"iPhone 12 Pro"; - if ([platform isEqualToString:@"iPhone13,4"]) - return @"iPhone 12 Pro Max"; - if ([platform isEqualToString:@"iPhone14,2"]) - return @"iPhone 13 Pro"; - if ([platform isEqualToString:@"iPhone14,3"]) - return @"iPhone 13 Pro Max"; - if ([platform isEqualToString:@"iPhone14,4"]) - return @"iPhone 13 Mini"; - if ([platform isEqualToString:@"iPhone14,5"]) - return @"iPhone 13"; - if ([platform isEqualToString:@"iPhone14,6"]) - return @"iPhone SE (3rd gen)"; - if ([platform isEqualToString:@"iPhone14,7"]) - return @"iPhone 14"; - if ([platform isEqualToString:@"iPhone14,8"]) - return @"iPhone 14 Plus"; - if ([platform isEqualToString:@"iPhone15,2"]) - return @"iPhone 14 Pro"; - if ([platform isEqualToString:@"iPhone15,3"]) - return @"iPhone 14 Pro Max"; - if ([platform isEqualToString:@"iPhone15,4"]) - return @"iPhone 15"; - if ([platform isEqualToString:@"iPhone15,5"]) - return @"iPhone 15 Plus"; - if ([platform isEqualToString:@"iPhone16,1"]) - return @"iPhone 15 Pro"; - if ([platform isEqualToString:@"iPhone16,2"]) - return @"iPhone 15 Pro Max"; - if ([platform isEqualToString:@"iPhone17,3"]) - return @"iPhone 16"; - if ([platform isEqualToString:@"iPhone17,4"]) - return @"iPhone 16 Plus"; - if ([platform isEqualToString:@"iPhone17,1"]) - return @"iPhone 16 Pro"; - if ([platform isEqualToString:@"iPhone17,2"]) - return @"iPhone 16 Pro Max"; - if ([platform isEqualToString:@"iPhone17,5"]) - return @"iPhone 16e"; - if ([platform isEqualToString:@"iPhone18,3"]) - return @"iPhone 17"; - if ([platform isEqualToString:@"iPhone18,1"]) - return @"iPhone 17 Pro"; - if ([platform isEqualToString:@"iPhone18,2"]) - return @"iPhone 17 Pro Max"; - if ([platform isEqualToString:@"iPhone18,4"]) - return @"iPhone Air"; - - if ([platform hasPrefix:@"iPod1"]) - return @"iPod touch 1G"; - if ([platform hasPrefix:@"iPod2"]) - return @"iPod touch 2G"; - if ([platform hasPrefix:@"iPod3"]) - return @"iPod touch 3G"; - if ([platform hasPrefix:@"iPod4"]) - return @"iPod touch 4G"; - if ([platform hasPrefix:@"iPod5"]) - return @"iPod touch 5G"; - if ([platform hasPrefix:@"iPod7"]) - return @"iPod touch 6G"; - if ([platform hasPrefix:@"iPod9"]) - return @"iPod touch 7G"; - - if ([platform isEqualToString:@"iPad2,5"] || - [platform isEqualToString:@"iPad2,6"] || - [platform isEqualToString:@"iPad2,7"]) - return @"iPad mini"; - - if ([platform hasPrefix:@"iPad2"]) - return @"iPad 2G"; - - if ([platform isEqualToString:@"iPad3,1"] || - [platform isEqualToString:@"iPad3,2"] || - [platform isEqualToString:@"iPad3,3"]) - return @"iPad 3G"; - - if ([platform isEqualToString:@"iPad3,4"] || - [platform isEqualToString:@"iPad3,5"] || - [platform isEqualToString:@"iPad3,6"]) - return @"iPad 3G"; - - if ([platform isEqualToString:@"iPad4,1"] || - [platform isEqualToString:@"iPad4,2"]) - return @"iPad Air"; - - if ([platform isEqualToString:@"iPad4,4"] || - [platform isEqualToString:@"iPad4,5"] || - [platform isEqualToString:@"iPad4,6"]) - return @"iPad mini Retina"; - - if ([platform isEqualToString:@"iPad4,7"] || - [platform isEqualToString:@"iPad4,8"] || - [platform isEqualToString:@"iPad4,9"]) - return @"iPad mini 3"; - - if ([platform isEqualToString:@"iPad5,1"] || - [platform isEqualToString:@"iPad5,2"]) - return @"iPad mini 4"; - - if ([platform isEqualToString:@"iPad5,3"] || - [platform isEqualToString:@"iPad5,4"]) - return @"iPad Air 2"; - - if ([platform isEqualToString:@"iPad6,3"] || - [platform isEqualToString:@"iPad6,4"]) - return @"iPad Pro 9.7 inch"; - - if ([platform isEqualToString:@"iPad6,7"] || - [platform isEqualToString:@"iPad6,8"]) - return @"iPad Pro 12.9 inch"; - - if ([platform isEqualToString:@"iPad6,11"] || - [platform isEqualToString:@"iPad6,12"]) - return @"iPad (2017)"; - - if ([platform isEqualToString:@"iPad7,1"] || - [platform isEqualToString:@"iPad7,2"]) - return @"iPad Pro (2nd gen)"; - - if ([platform isEqualToString:@"iPad7,3"] || - [platform isEqualToString:@"iPad7,4"]) - return @"iPad Pro 10.5 inch"; - - if ([platform isEqualToString:@"iPad7,5"] || - [platform isEqualToString:@"iPad7,6"]) - return @"iPad (6th gen)"; - - if ([platform isEqualToString:@"iPad7,11"] || - [platform isEqualToString:@"iPad7,12"]) - return @"iPad 10.2 inch (7th gen)"; - - if ([platform isEqualToString:@"iPad8,1"] || - [platform isEqualToString:@"iPad8,2"] || - [platform isEqualToString:@"iPad8,3"] || - [platform isEqualToString:@"iPad8,4"]) - return @"iPad Pro 11 inch"; - - if ([platform isEqualToString:@"iPad8,5"] || - [platform isEqualToString:@"iPad8,6"] || - [platform isEqualToString:@"iPad8,7"] || - [platform isEqualToString:@"iPad8,8"]) - return @"iPad Pro 12.9 inch (3rd gen)"; - - if ([platform isEqualToString:@"iPad8,9"] || - [platform isEqualToString:@"iPad8,10"]) - return @"iPad Pro 11 inch (2th gen)"; - - if ([platform isEqualToString:@"iPad8,11"] || - [platform isEqualToString:@"iPad8,12"]) - return @"iPad Pro 12.9 inch (4th gen)"; - - if ([platform isEqualToString:@"iPad11,1"] || - [platform isEqualToString:@"iPad11,2"]) - return @"iPad mini (5th gen)"; - - if ([platform isEqualToString:@"iPad11,3"] || - [platform isEqualToString:@"iPad11,4"]) - return @"iPad Air (3rd gen)"; - - if ([platform isEqualToString:@"iPad11,6"] || - [platform isEqualToString:@"iPad11,7"]) - return @"iPad (8th gen)"; - - if ([platform isEqualToString:@"iPad12,1"] || - [platform isEqualToString:@"iPad12,2"]) - return @"iPad (9th gen)"; - - if ([platform isEqualToString:@"iPad13,1"] || - [platform isEqualToString:@"iPad13,2"]) - return @"iPad Air (4th gen)"; - - if ([platform isEqualToString:@"iPad13,4"] || - [platform isEqualToString:@"iPad13,5"] || - [platform isEqualToString:@"iPad13,6"] || - [platform isEqualToString:@"iPad13,7"]) - return @"iPad Pro 11 inch (3th gen)"; - - if ([platform isEqualToString:@"iPad13,8"] || - [platform isEqualToString:@"iPad13,9"] || - [platform isEqualToString:@"iPad13,10"] || - [platform isEqualToString:@"iPad13,11"]) - return @"iPad Pro 12.9 inch (5th gen)"; - - if ([platform isEqualToString:@"iPad13,16"] || - [platform isEqualToString:@"iPad13,17"]) - return @"iPad Air (5th gen)"; - - if ([platform isEqualToString:@"iPad13,18"] || - [platform isEqualToString:@"iPad13,19"]) - return @"iPad (10th gen)"; - - if ([platform isEqualToString:@"iPad14,1"] || - [platform isEqualToString:@"iPad14,2"]) - return @"iPad mini (6th gen)"; - - if ([platform isEqualToString:@"iPad14,3"] || - [platform isEqualToString:@"iPad14,4"]) - return @"iPad Pro 11 inch (4th gen)"; - - if ([platform isEqualToString:@"iPad14,5"] || - [platform isEqualToString:@"iPad14,6"]) - return @"iPad Pro 12.9 inch (6th gen)"; - - if ([platform isEqualToString:@"iPad14,8"] || - [platform isEqualToString:@"iPad14,9"]) - return @"iPad Air (6th gen)"; - - if ([platform isEqualToString:@"iPad14,10"] || - [platform isEqualToString:@"iPad14,11"]) - return @"iPad Air (7th gen)"; - - if ([platform isEqualToString:@"iPad16,3"] || - [platform isEqualToString:@"iPad16,4"]) - return @"iPad Pro 11 inch (5th gen)"; - - if ([platform isEqualToString:@"iPad16,5"] || - [platform isEqualToString:@"iPad16,6"]) - return @"iPad Pro 12.9 inch (7th gen)"; - - if ([platform hasPrefix:@"iPhone"]) - return @"Unknown iPhone"; - if ([platform hasPrefix:@"iPod"]) - return @"Unknown iPod"; - if ([platform hasPrefix:@"iPad"]) - return @"Unknown iPad"; - - if ([platform hasSuffix:@"86"] || [platform isEqual:@"x86_64"] || [platform isEqual:@"arm64"]) { - return @"iPhone Simulator"; - } + NSString *platform = [self platform]; + + if ([platform isEqualToString:@"iPhone1,1"]) + return @"iPhone"; + if ([platform isEqualToString:@"iPhone1,2"]) + return @"iPhone 3G"; + if ([platform isEqualToString:@"iPhone2,1"]) + return @"iPhone 3GS"; + if ([platform hasPrefix:@"iPhone3"]) + return @"iPhone 4"; + if ([platform hasPrefix:@"iPhone4"]) + return @"iPhone 4S"; + if ([platform isEqualToString:@"iPhone5,1"] || + [platform isEqualToString:@"iPhone5,2"]) + return @"iPhone 5"; + if ([platform isEqualToString:@"iPhone5,3"] || + [platform isEqualToString:@"iPhone5,4"]) + return @"iPhone 5C"; + if ([platform hasPrefix:@"iPhone6"]) + return @"iPhone 5S"; + if ([platform isEqualToString:@"iPhone7,1"]) + return @"iPhone 6 Plus"; + if ([platform isEqualToString:@"iPhone7,2"]) + return @"iPhone 6"; + if ([platform isEqualToString:@"iPhone8,1"]) + return @"iPhone 6S"; + if ([platform isEqualToString:@"iPhone8,2"]) + return @"iPhone 6S Plus"; + if ([platform isEqualToString:@"iPhone8,4"]) + return @"iPhone SE"; + if ([platform isEqualToString:@"iPhone9,1"] || + [platform isEqualToString:@"iPhone9,3"]) + return @"iPhone 7"; + if ([platform isEqualToString:@"iPhone9,2"] || + [platform isEqualToString:@"iPhone9,4"]) + return @"iPhone 7 Plus"; + if ([platform isEqualToString:@"iPhone10,1"] || + [platform isEqualToString:@"iPhone10,4"]) + return @"iPhone 8"; + if ([platform isEqualToString:@"iPhone10,2"] || + [platform isEqualToString:@"iPhone10,5"]) + return @"iPhone 8 Plus"; + if ([platform isEqualToString:@"iPhone10,3"] || + [platform isEqualToString:@"iPhone10,6"]) + return @"iPhone X"; + if ([platform isEqualToString:@"iPhone11,2"]) + return @"iPhone XS"; + if ([platform isEqualToString:@"iPhone11,4"] || + [platform isEqualToString:@"iPhone11,6"]) + return @"iPhone XS Max"; + if ([platform isEqualToString:@"iPhone11,8"]) + return @"iPhone XR"; + if ([platform isEqualToString:@"iPhone12,1"]) + return @"iPhone 11"; + if ([platform isEqualToString:@"iPhone12,3"]) + return @"iPhone 11 Pro"; + if ([platform isEqualToString:@"iPhone12,5"]) + return @"iPhone 11 Pro Max"; + if ([platform isEqualToString:@"iPhone12,8"]) + return @"iPhone SE (2nd gen)"; + if ([platform isEqualToString:@"iPhone13,1"]) + return @"iPhone 12 mini"; + if ([platform isEqualToString:@"iPhone13,2"]) + return @"iPhone 12"; + if ([platform isEqualToString:@"iPhone13,3"]) + return @"iPhone 12 Pro"; + if ([platform isEqualToString:@"iPhone13,4"]) + return @"iPhone 12 Pro Max"; + if ([platform isEqualToString:@"iPhone14,2"]) + return @"iPhone 13 Pro"; + if ([platform isEqualToString:@"iPhone14,3"]) + return @"iPhone 13 Pro Max"; + if ([platform isEqualToString:@"iPhone14,4"]) + return @"iPhone 13 Mini"; + if ([platform isEqualToString:@"iPhone14,5"]) + return @"iPhone 13"; + if ([platform isEqualToString:@"iPhone14,6"]) + return @"iPhone SE (3rd gen)"; + if ([platform isEqualToString:@"iPhone14,7"]) + return @"iPhone 14"; + if ([platform isEqualToString:@"iPhone14,8"]) + return @"iPhone 14 Plus"; + if ([platform isEqualToString:@"iPhone15,2"]) + return @"iPhone 14 Pro"; + if ([platform isEqualToString:@"iPhone15,3"]) + return @"iPhone 14 Pro Max"; + if ([platform isEqualToString:@"iPhone15,4"]) + return @"iPhone 15"; + if ([platform isEqualToString:@"iPhone15,5"]) + return @"iPhone 15 Plus"; + if ([platform isEqualToString:@"iPhone16,1"]) + return @"iPhone 15 Pro"; + if ([platform isEqualToString:@"iPhone16,2"]) + return @"iPhone 15 Pro Max"; + if ([platform isEqualToString:@"iPhone17,3"]) + return @"iPhone 16"; + if ([platform isEqualToString:@"iPhone17,4"]) + return @"iPhone 16 Plus"; + if ([platform isEqualToString:@"iPhone17,1"]) + return @"iPhone 16 Pro"; + if ([platform isEqualToString:@"iPhone17,2"]) + return @"iPhone 16 Pro Max"; + if ([platform isEqualToString:@"iPhone17,5"]) + return @"iPhone 16e"; + if ([platform isEqualToString:@"iPhone18,3"]) + return @"iPhone 17"; + if ([platform isEqualToString:@"iPhone18,1"]) + return @"iPhone 17 Pro"; + if ([platform isEqualToString:@"iPhone18,2"]) + return @"iPhone 17 Pro Max"; + if ([platform isEqualToString:@"iPhone18,4"]) + return @"iPhone Air"; + + if ([platform hasPrefix:@"iPod1"]) + return @"iPod touch 1G"; + if ([platform hasPrefix:@"iPod2"]) + return @"iPod touch 2G"; + if ([platform hasPrefix:@"iPod3"]) + return @"iPod touch 3G"; + if ([platform hasPrefix:@"iPod4"]) + return @"iPod touch 4G"; + if ([platform hasPrefix:@"iPod5"]) + return @"iPod touch 5G"; + if ([platform hasPrefix:@"iPod7"]) + return @"iPod touch 6G"; + if ([platform hasPrefix:@"iPod9"]) + return @"iPod touch 7G"; + + if ([platform isEqualToString:@"iPad2,5"] || + [platform isEqualToString:@"iPad2,6"] || + [platform isEqualToString:@"iPad2,7"]) + return @"iPad mini"; + + if ([platform hasPrefix:@"iPad2"]) + return @"iPad 2G"; + + if ([platform isEqualToString:@"iPad3,1"] || + [platform isEqualToString:@"iPad3,2"] || + [platform isEqualToString:@"iPad3,3"]) + return @"iPad 3G"; + + if ([platform isEqualToString:@"iPad3,4"] || + [platform isEqualToString:@"iPad3,5"] || + [platform isEqualToString:@"iPad3,6"]) + return @"iPad 3G"; + + if ([platform isEqualToString:@"iPad4,1"] || + [platform isEqualToString:@"iPad4,2"]) + return @"iPad Air"; + + if ([platform isEqualToString:@"iPad4,4"] || + [platform isEqualToString:@"iPad4,5"] || + [platform isEqualToString:@"iPad4,6"]) + return @"iPad mini Retina"; + + if ([platform isEqualToString:@"iPad4,7"] || + [platform isEqualToString:@"iPad4,8"] || + [platform isEqualToString:@"iPad4,9"]) + return @"iPad mini 3"; + + if ([platform isEqualToString:@"iPad5,1"] || + [platform isEqualToString:@"iPad5,2"]) + return @"iPad mini 4"; + + if ([platform isEqualToString:@"iPad5,3"] || + [platform isEqualToString:@"iPad5,4"]) + return @"iPad Air 2"; + + if ([platform isEqualToString:@"iPad6,3"] || + [platform isEqualToString:@"iPad6,4"]) + return @"iPad Pro 9.7 inch"; + + if ([platform isEqualToString:@"iPad6,7"] || + [platform isEqualToString:@"iPad6,8"]) + return @"iPad Pro 12.9 inch"; + + if ([platform isEqualToString:@"iPad6,11"] || + [platform isEqualToString:@"iPad6,12"]) + return @"iPad (2017)"; + + if ([platform isEqualToString:@"iPad7,1"] || + [platform isEqualToString:@"iPad7,2"]) + return @"iPad Pro (2nd gen)"; + + if ([platform isEqualToString:@"iPad7,3"] || + [platform isEqualToString:@"iPad7,4"]) + return @"iPad Pro 10.5 inch"; + + if ([platform isEqualToString:@"iPad7,5"] || + [platform isEqualToString:@"iPad7,6"]) + return @"iPad (6th gen)"; + + if ([platform isEqualToString:@"iPad7,11"] || + [platform isEqualToString:@"iPad7,12"]) + return @"iPad 10.2 inch (7th gen)"; + + if ([platform isEqualToString:@"iPad8,1"] || + [platform isEqualToString:@"iPad8,2"] || + [platform isEqualToString:@"iPad8,3"] || + [platform isEqualToString:@"iPad8,4"]) + return @"iPad Pro 11 inch"; + + if ([platform isEqualToString:@"iPad8,5"] || + [platform isEqualToString:@"iPad8,6"] || + [platform isEqualToString:@"iPad8,7"] || + [platform isEqualToString:@"iPad8,8"]) + return @"iPad Pro 12.9 inch (3rd gen)"; + + if ([platform isEqualToString:@"iPad8,9"] || + [platform isEqualToString:@"iPad8,10"]) + return @"iPad Pro 11 inch (2th gen)"; + + if ([platform isEqualToString:@"iPad8,11"] || + [platform isEqualToString:@"iPad8,12"]) + return @"iPad Pro 12.9 inch (4th gen)"; + + if ([platform isEqualToString:@"iPad11,1"] || + [platform isEqualToString:@"iPad11,2"]) + return @"iPad mini (5th gen)"; + + if ([platform isEqualToString:@"iPad11,3"] || + [platform isEqualToString:@"iPad11,4"]) + return @"iPad Air (3rd gen)"; + + if ([platform isEqualToString:@"iPad11,6"] || + [platform isEqualToString:@"iPad11,7"]) + return @"iPad (8th gen)"; + + if ([platform isEqualToString:@"iPad12,1"] || + [platform isEqualToString:@"iPad12,2"]) + return @"iPad (9th gen)"; + + if ([platform isEqualToString:@"iPad13,1"] || + [platform isEqualToString:@"iPad13,2"]) + return @"iPad Air (4th gen)"; + + if ([platform isEqualToString:@"iPad13,4"] || + [platform isEqualToString:@"iPad13,5"] || + [platform isEqualToString:@"iPad13,6"] || + [platform isEqualToString:@"iPad13,7"]) + return @"iPad Pro 11 inch (3th gen)"; + + if ([platform isEqualToString:@"iPad13,8"] || + [platform isEqualToString:@"iPad13,9"] || + [platform isEqualToString:@"iPad13,10"] || + [platform isEqualToString:@"iPad13,11"]) + return @"iPad Pro 12.9 inch (5th gen)"; + + if ([platform isEqualToString:@"iPad13,16"] || + [platform isEqualToString:@"iPad13,17"]) + return @"iPad Air (5th gen)"; + + if ([platform isEqualToString:@"iPad13,18"] || + [platform isEqualToString:@"iPad13,19"]) + return @"iPad (10th gen)"; + + if ([platform isEqualToString:@"iPad14,1"] || + [platform isEqualToString:@"iPad14,2"]) + return @"iPad mini (6th gen)"; + + if ([platform isEqualToString:@"iPad14,3"] || + [platform isEqualToString:@"iPad14,4"]) + return @"iPad Pro 11 inch (4th gen)"; + + if ([platform isEqualToString:@"iPad14,5"] || + [platform isEqualToString:@"iPad14,6"]) + return @"iPad Pro 12.9 inch (6th gen)"; + + if ([platform isEqualToString:@"iPad14,8"] || + [platform isEqualToString:@"iPad14,9"]) + return @"iPad Air (6th gen)"; + + if ([platform isEqualToString:@"iPad14,10"] || + [platform isEqualToString:@"iPad14,11"]) + return @"iPad Air (7th gen)"; + + if ([platform isEqualToString:@"iPad16,3"] || + [platform isEqualToString:@"iPad16,4"]) + return @"iPad Pro 11 inch (5th gen)"; + + if ([platform isEqualToString:@"iPad16,5"] || + [platform isEqualToString:@"iPad16,6"]) + return @"iPad Pro 12.9 inch (7th gen)"; + + if ([platform hasPrefix:@"iPhone"]) + return @"Unknown iPhone"; + if ([platform hasPrefix:@"iPod"]) + return @"Unknown iPod"; + if ([platform hasPrefix:@"iPad"]) + return @"Unknown iPad"; + + if ([platform hasSuffix:@"86"] || [platform isEqual:@"x86_64"] || + [platform isEqual:@"arm64"]) { + return @"iPhone Simulator"; + } #else - return [self macHWName]; + return [self macHWName]; #endif - - return @"Unknown iOS device"; + + return @"Unknown iOS device"; } - + - (NSString *)macHWName { - size_t len = 0; - sysctlbyname("hw.model", NULL, &len, NULL, 0); - if (len) { - char *model = malloc(len*sizeof(char)); - sysctlbyname("hw.model", model, &len, NULL, 0); - NSString *name = [[NSString alloc] initWithUTF8String:model]; - free(model); - return name; - }; - return @"macOS"; + size_t len = 0; + sysctlbyname("hw.model", NULL, &len, NULL, 0); + if (len) { + char *model = malloc(len * sizeof(char)); + sysctlbyname("hw.model", model, &len, NULL, 0); + NSString *name = [[NSString alloc] initWithUTF8String:model]; + free(model); + return name; + }; + return @"macOS"; } -- (NSString *)getSysInfoByName:(char *)typeSpecifier -{ - size_t size; - sysctlbyname(typeSpecifier, NULL, &size, NULL, 0); - - char *answer = malloc(size); - sysctlbyname(typeSpecifier, answer, &size, NULL, 0); - - NSString *results = [NSString stringWithCString:answer encoding: NSUTF8StringEncoding]; - - free(answer); - return results; +- (NSString *)getSysInfoByName:(char *)typeSpecifier { + size_t size; + sysctlbyname(typeSpecifier, NULL, &size, NULL, 0); + + char *answer = malloc(size); + sysctlbyname(typeSpecifier, answer, &size, NULL, 0); + + NSString *results = [NSString stringWithCString:answer + encoding:NSUTF8StringEncoding]; + + free(answer); + return results; } -- (NSString *)platform -{ - return [self getSysInfoByName:"hw.machine"]; +- (NSString *)platform { + return [self getSysInfoByName:"hw.machine"]; } - (MTApiEnvironment *)withUpdatedLangPackCode:(NSString *)langPackCode { - MTApiEnvironment *result = [[MTApiEnvironment alloc] initWithDeviceModelName:_deviceModelName]; - - result.apiId = self.apiId; - result.appVersion = self.appVersion; - result.layer = self.layer; - - result.langPack = self.langPack; - - result->_langPackCode = langPackCode; - - result.disableUpdates = self.disableUpdates; - result.tcpPayloadPrefix = self.tcpPayloadPrefix; - result.datacenterAddressOverrides = self.datacenterAddressOverrides; - result.accessHostOverride = self.accessHostOverride; - result->_socksProxySettings = self.socksProxySettings; - result->_networkSettings = self.networkSettings; - result->_systemCode = self.systemCode; - - [result _updateApiInitializationHash]; - - return result; + MTApiEnvironment *result = + [[MTApiEnvironment alloc] initWithDeviceModelName:_deviceModelName]; + + result.apiId = self.apiId; + result.appVersion = self.appVersion; + result.layer = self.layer; + + result.langPack = self.langPack; + + result->_langPackCode = langPackCode; + + result.disableUpdates = self.disableUpdates; + result.tcpPayloadPrefix = self.tcpPayloadPrefix; + result.datacenterAddressOverrides = self.datacenterAddressOverrides; + result.accessHostOverride = self.accessHostOverride; + result->_socksProxySettings = self.socksProxySettings; + result->_networkSettings = self.networkSettings; + result->_systemCode = self.systemCode; + + [result _updateApiInitializationHash]; + + return result; } - (instancetype)copyWithZone:(NSZone *)__unused zone { - MTApiEnvironment *result = [[MTApiEnvironment alloc] initWithDeviceModelName:_deviceModelName]; - - result.apiId = self.apiId; - result.appVersion = self.appVersion; - result.layer = self.layer; - - result.langPack = self.langPack; - - result->_langPackCode = self.langPackCode; - result->_socksProxySettings = self.socksProxySettings; - result->_networkSettings = self.networkSettings; - result->_systemCode = self.systemCode; - - result.disableUpdates = self.disableUpdates; - result.tcpPayloadPrefix = self.tcpPayloadPrefix; - result.datacenterAddressOverrides = self.datacenterAddressOverrides; - result.accessHostOverride = self.accessHostOverride; - - [result _updateApiInitializationHash]; - - return result; + MTApiEnvironment *result = + [[MTApiEnvironment alloc] initWithDeviceModelName:_deviceModelName]; + + result.apiId = self.apiId; + result.appVersion = self.appVersion; + result.layer = self.layer; + + result.langPack = self.langPack; + + result->_langPackCode = self.langPackCode; + result->_socksProxySettings = self.socksProxySettings; + result->_networkSettings = self.networkSettings; + result->_systemCode = self.systemCode; + + result.disableUpdates = self.disableUpdates; + result.tcpPayloadPrefix = self.tcpPayloadPrefix; + result.datacenterAddressOverrides = self.datacenterAddressOverrides; + result.accessHostOverride = self.accessHostOverride; + + [result _updateApiInitializationHash]; + + return result; } -- (MTApiEnvironment *)withUpdatedSocksProxySettings:(MTSocksProxySettings *)socksProxySettings { - MTApiEnvironment *result = [[MTApiEnvironment alloc] initWithDeviceModelName:_deviceModelName]; - - result.apiId = self.apiId; - result.appVersion = self.appVersion; - result.layer = self.layer; - - result.langPack = self.langPack; - - result->_langPackCode = self.langPackCode; - result->_socksProxySettings = socksProxySettings; - result->_networkSettings = self.networkSettings; - result->_systemCode = self.systemCode; - - result.disableUpdates = self.disableUpdates; - result.tcpPayloadPrefix = self.tcpPayloadPrefix; - result.datacenterAddressOverrides = self.datacenterAddressOverrides; - result.accessHostOverride = self.accessHostOverride; - - [result _updateApiInitializationHash]; - - return result; +- (MTApiEnvironment *)withUpdatedSocksProxySettings: + (MTSocksProxySettings *)socksProxySettings { + MTApiEnvironment *result = + [[MTApiEnvironment alloc] initWithDeviceModelName:_deviceModelName]; + + result.apiId = self.apiId; + result.appVersion = self.appVersion; + result.layer = self.layer; + + result.langPack = self.langPack; + + result->_langPackCode = self.langPackCode; + result->_socksProxySettings = socksProxySettings; + result->_networkSettings = self.networkSettings; + result->_systemCode = self.systemCode; + + result.disableUpdates = self.disableUpdates; + result.tcpPayloadPrefix = self.tcpPayloadPrefix; + result.datacenterAddressOverrides = self.datacenterAddressOverrides; + result.accessHostOverride = self.accessHostOverride; + + [result _updateApiInitializationHash]; + + return result; } -- (MTApiEnvironment *)withUpdatedNetworkSettings:(MTNetworkSettings *)networkSettings { - MTApiEnvironment *result = [[MTApiEnvironment alloc] initWithDeviceModelName:_deviceModelName]; - - result.apiId = self.apiId; - result.appVersion = self.appVersion; - result.layer = self.layer; - - result.langPack = self.langPack; - - result->_langPackCode = self.langPackCode; - result->_socksProxySettings = self.socksProxySettings; - result->_networkSettings = networkSettings; - result->_systemCode = self.systemCode; - - result.disableUpdates = self.disableUpdates; - result.tcpPayloadPrefix = self.tcpPayloadPrefix; - result.datacenterAddressOverrides = self.datacenterAddressOverrides; - result.accessHostOverride = self.accessHostOverride; - - [result _updateApiInitializationHash]; - - return result; +- (MTApiEnvironment *)withUpdatedNetworkSettings: + (MTNetworkSettings *)networkSettings { + MTApiEnvironment *result = + [[MTApiEnvironment alloc] initWithDeviceModelName:_deviceModelName]; + + result.apiId = self.apiId; + result.appVersion = self.appVersion; + result.layer = self.layer; + + result.langPack = self.langPack; + + result->_langPackCode = self.langPackCode; + result->_socksProxySettings = self.socksProxySettings; + result->_networkSettings = networkSettings; + result->_systemCode = self.systemCode; + + result.disableUpdates = self.disableUpdates; + result.tcpPayloadPrefix = self.tcpPayloadPrefix; + result.datacenterAddressOverrides = self.datacenterAddressOverrides; + result.accessHostOverride = self.accessHostOverride; + + [result _updateApiInitializationHash]; + + return result; } - (MTApiEnvironment *)withUpdatedSystemCode:(NSData *)systemCode { - MTApiEnvironment *result = [[MTApiEnvironment alloc] initWithDeviceModelName:_deviceModelName]; - - result.apiId = self.apiId; - result.appVersion = self.appVersion; - result.layer = self.layer; - - result.langPack = self.langPack; - - result->_langPackCode = self.langPackCode; - result->_socksProxySettings = self.socksProxySettings; - result->_networkSettings = self.networkSettings; - result->_systemCode = systemCode; - - result.disableUpdates = self.disableUpdates; - result.tcpPayloadPrefix = self.tcpPayloadPrefix; - result.datacenterAddressOverrides = self.datacenterAddressOverrides; - result.accessHostOverride = self.accessHostOverride; - - [result _updateApiInitializationHash]; - - return result; + MTApiEnvironment *result = + [[MTApiEnvironment alloc] initWithDeviceModelName:_deviceModelName]; + + result.apiId = self.apiId; + result.appVersion = self.appVersion; + result.layer = self.layer; + + result.langPack = self.langPack; + + result->_langPackCode = self.langPackCode; + result->_socksProxySettings = self.socksProxySettings; + result->_networkSettings = self.networkSettings; + result->_systemCode = systemCode; + + result.disableUpdates = self.disableUpdates; + result.tcpPayloadPrefix = self.tcpPayloadPrefix; + result.datacenterAddressOverrides = self.datacenterAddressOverrides; + result.accessHostOverride = self.accessHostOverride; + + [result _updateApiInitializationHash]; + + return result; } @end - diff --git a/submodules/MtProtoKit/Sources/MTNetworkAvailability.m b/submodules/MtProtoKit/Sources/MTNetworkAvailability.m index 47f45844..3cdbfc6b 100644 --- a/submodules/MtProtoKit/Sources/MTNetworkAvailability.m +++ b/submodules/MtProtoKit/Sources/MTNetworkAvailability.m @@ -62,6 +62,8 @@ static void MTNetworkAvailabilityContextRelease(const void *info) zeroAddress.sin_len = sizeof(zeroAddress); zeroAddress.sin_family = AF_INET; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" _reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)&zeroAddress); if (_reachability != NULL) { @@ -84,6 +86,7 @@ static void MTNetworkAvailabilityContextRelease(const void *info) if (SCNetworkReachabilitySetCallback(_reachability, &MTAvailabilityCallback, &context)) SCNetworkReachabilitySetDispatchQueue(_reachability, [MTNetworkAvailability networkAvailabilityQueue].nativeQueue); +#pragma clang diagnostic pop } }]; } @@ -101,8 +104,11 @@ static void MTNetworkAvailabilityContextRelease(const void *info) [[MTNetworkAvailability networkAvailabilityQueue] dispatchOnQueue:^{ [timer invalidate]; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" SCNetworkReachabilitySetCallback(reachability, NULL, NULL); SCNetworkReachabilitySetDispatchQueue(reachability, NULL); +#pragma clang diagnostic pop CFRelease(reachability); }]; } @@ -126,7 +132,10 @@ static void MTNetworkAvailabilityContextRelease(const void *info) if (_reachability != nil) { SCNetworkReachabilityFlags currentFlags = 0; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" if (SCNetworkReachabilityGetFlags(_reachability, ¤tFlags)) +#pragma clang diagnostic pop [self updateReachability:currentFlags notify:notify]; } }]; diff --git a/submodules/NotificationSoundSelectionUI/Sources/NotificationSoundSelection.swift b/submodules/NotificationSoundSelectionUI/Sources/NotificationSoundSelection.swift index 4e9432cc..a744c229 100644 --- a/submodules/NotificationSoundSelectionUI/Sources/NotificationSoundSelection.swift +++ b/submodules/NotificationSoundSelectionUI/Sources/NotificationSoundSelection.swift @@ -455,7 +455,7 @@ public func notificationSoundSelectionController(context: AccountContext, update let presentationData = context.sharedContext.currentPresentationData.with { $0 } - controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.PeerInfo_DeleteToneTitle, text: presentationData.strings.PeerInfo_DeleteToneText(title).string, actions: [ + controller.present(textAlertController(context: context, title: presentationData.strings.PeerInfo_DeleteToneTitle, text: presentationData.strings.PeerInfo_DeleteToneText(title).string, actions: [ TextAlertAction(type: .destructiveAction, title: presentationData.strings.Common_Delete, action: { updateState { state in var state = state diff --git a/submodules/OpusBinding/BUILD b/submodules/OpusBinding/BUILD index f65be5a8..78d27acd 100644 --- a/submodules/OpusBinding/BUILD +++ b/submodules/OpusBinding/BUILD @@ -24,6 +24,8 @@ objc_library( ], sdk_frameworks = [ "Foundation", + "AVFoundation", + "AudioToolbox", ], visibility = [ "//visibility:public", diff --git a/submodules/OpusBinding/PublicHeaders/OpusBinding/OpusBinding.h b/submodules/OpusBinding/PublicHeaders/OpusBinding/OpusBinding.h index 30524dbc..88139968 100644 --- a/submodules/OpusBinding/PublicHeaders/OpusBinding/OpusBinding.h +++ b/submodules/OpusBinding/PublicHeaders/OpusBinding/OpusBinding.h @@ -2,4 +2,5 @@ #import #import -#import \ No newline at end of file +#import +#import \ No newline at end of file diff --git a/submodules/PasscodeUI/Sources/PasscodeSetupController.swift b/submodules/PasscodeUI/Sources/PasscodeSetupController.swift index a4592b80..0c62555e 100644 --- a/submodules/PasscodeUI/Sources/PasscodeSetupController.swift +++ b/submodules/PasscodeUI/Sources/PasscodeSetupController.swift @@ -34,7 +34,7 @@ public final class PasscodeSetupController: ViewController { self.mode = mode self.presentationData = context.sharedContext.currentPresentationData.with { $0 } - super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData)) + super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData, style: .glass)) self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style diff --git a/submodules/PasswordSetupUI/Sources/SetupTwoStepVerificationController.swift b/submodules/PasswordSetupUI/Sources/SetupTwoStepVerificationController.swift index 9427aa0c..e6c93894 100644 --- a/submodules/PasswordSetupUI/Sources/SetupTwoStepVerificationController.swift +++ b/submodules/PasswordSetupUI/Sources/SetupTwoStepVerificationController.swift @@ -39,7 +39,7 @@ public class SetupTwoStepVerificationController: ViewController { self.presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(buttonColor: self.presentationData.theme.rootController.navigationBar.accentTextColor, disabledButtonColor: self.presentationData.theme.rootController.navigationBar.disabledButtonColor, primaryTextColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings))) + super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(overallDarkAppearance: self.presentationData.theme.overallDarkAppearance, buttonColor: self.presentationData.theme.rootController.navigationBar.accentTextColor, disabledButtonColor: self.presentationData.theme.rootController.navigationBar.disabledButtonColor, primaryTextColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings))) self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style @@ -89,7 +89,7 @@ public class SetupTwoStepVerificationController: ViewController { private func updateThemeAndStrings() { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData), transition: .immediate) self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) self.controllerNode.updatePresentationData(self.presentationData) } diff --git a/submodules/PasswordSetupUI/Sources/TwoFactorAuthDataInputScreen.swift b/submodules/PasswordSetupUI/Sources/TwoFactorAuthDataInputScreen.swift index eaa84497..54843c67 100644 --- a/submodules/PasswordSetupUI/Sources/TwoFactorAuthDataInputScreen.swift +++ b/submodules/PasswordSetupUI/Sources/TwoFactorAuthDataInputScreen.swift @@ -71,7 +71,7 @@ public final class TwoFactorDataInputScreen: ViewController { self.presentationData = self.sharedContext.currentPresentationData.with { $0 } let defaultTheme = NavigationBarTheme(rootControllerTheme: self.presentationData.theme) - let navigationBarTheme = NavigationBarTheme(buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor) + let navigationBarTheme = NavigationBarTheme(overallDarkAppearance: defaultTheme.overallDarkAppearance, buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor) super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close))) @@ -119,7 +119,7 @@ public final class TwoFactorDataInputScreen: ViewController { return } if values[0] != values[1] { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.TwoStepAuth_SetupPasswordConfirmFailed, actions: [ + strongSelf.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: strongSelf.presentationData.strings.TwoStepAuth_SetupPasswordConfirmFailed, actions: [ TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {}) ]), in: .window(.root)) return @@ -167,7 +167,7 @@ public final class TwoFactorDataInputScreen: ViewController { text = strongSelf.presentationData.strings.Login_UnknownError } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + strongSelf.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) }, completed: { [weak statusController] in statusController?.dismiss() @@ -196,7 +196,7 @@ public final class TwoFactorDataInputScreen: ViewController { return } if values[0] != values[1] { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.TwoStepAuth_SetupPasswordConfirmFailed, actions: [ + strongSelf.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: strongSelf.presentationData.strings.TwoStepAuth_SetupPasswordConfirmFailed, actions: [ TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {}) ]), in: .window(.root)) return @@ -481,7 +481,7 @@ public final class TwoFactorDataInputScreen: ViewController { } switch strongSelf.mode { case let .emailAddress(password, hint, doneText): - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.TwoFactorSetup_Email_SkipConfirmationTitle, text: strongSelf.presentationData.strings.TwoFactorSetup_Email_SkipConfirmationText, actions: [ + strongSelf.present(textAlertController(sharedContext: strongSelf.sharedContext, title: strongSelf.presentationData.strings.TwoFactorSetup_Email_SkipConfirmationTitle, text: strongSelf.presentationData.strings.TwoFactorSetup_Email_SkipConfirmationText, actions: [ TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.TwoFactorSetup_Email_SkipConfirmationSkip, action: { guard let strongSelf = self else { return @@ -543,7 +543,7 @@ public final class TwoFactorDataInputScreen: ViewController { strongSelf.push(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .emailAddress(password: password, hint: "", doneText: doneText), stateUpdated: strongSelf.stateUpdated, presentation: strongSelf.navigationPresentation)) } case let .passwordRecovery(recovery, _): - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.TwoFactorSetup_PasswordRecovery_SkipAlertTitle, text: strongSelf.presentationData.strings.TwoFactorSetup_PasswordRecovery_SkipAlertText, actions: [ + strongSelf.present(textAlertController(sharedContext: strongSelf.sharedContext, title: strongSelf.presentationData.strings.TwoFactorSetup_PasswordRecovery_SkipAlertTitle, text: strongSelf.presentationData.strings.TwoFactorSetup_PasswordRecovery_SkipAlertText, actions: [ TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.TwoFactorSetup_PasswordRecovery_SkipAlertAction, action: { guard let strongSelf = self else { return diff --git a/submodules/PasswordSetupUI/Sources/TwoFactorAuthSplashScreen.swift b/submodules/PasswordSetupUI/Sources/TwoFactorAuthSplashScreen.swift index 66e73dba..6b0209ed 100644 --- a/submodules/PasswordSetupUI/Sources/TwoFactorAuthSplashScreen.swift +++ b/submodules/PasswordSetupUI/Sources/TwoFactorAuthSplashScreen.swift @@ -57,7 +57,7 @@ public final class TwoFactorAuthSplashScreen: ViewController { self.presentationData = self.sharedContext.currentPresentationData.with { $0 } let defaultTheme = NavigationBarTheme(rootControllerTheme: self.presentationData.theme) - let navigationBarTheme = NavigationBarTheme(buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor) + let navigationBarTheme = NavigationBarTheme(overallDarkAppearance: defaultTheme.overallDarkAppearance, buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor) super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close))) diff --git a/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryController.swift b/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryController.swift index 9cf2bfb8..72931c65 100644 --- a/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryController.swift +++ b/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryController.swift @@ -754,7 +754,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr self.galleryNode.setControlsHidden(true, animated: false) if let centralItemNode = self.galleryNode.pager.centralItemNode(), let itemSize = centralItemNode.contentSize() { self.preferredContentSize = itemSize.aspectFitted(self.view.bounds.size) - self.containerLayoutUpdated(ContainerViewLayout(size: self.preferredContentSize, metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), additionalInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate) + self.containerLayoutUpdated(ContainerViewLayout(size: self.preferredContentSize, metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), additionalInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate) centralItemNode.activateAsInitial() } } diff --git a/submodules/PeerInfoUI/BUILD b/submodules/PeerInfoUI/BUILD index 7c57b565..756a3d95 100644 --- a/submodules/PeerInfoUI/BUILD +++ b/submodules/PeerInfoUI/BUILD @@ -80,6 +80,9 @@ swift_library( "//submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController", "//submodules/TelegramUI/Components/PeerManagement/OldChannelsController", "//submodules/TelegramUI/Components/PeerInfo/MessagePriceItem", + "//submodules/TelegramUI/Components/GlassBackgroundComponent", + "//submodules/ComponentFlow", + "//submodules/Components/ComponentDisplayAdapters", ], visibility = [ "//visibility:public", diff --git a/submodules/PeerInfoUI/CreateExternalMediaStreamScreen/Sources/CreateExternalMediaStreamScreen.swift b/submodules/PeerInfoUI/CreateExternalMediaStreamScreen/Sources/CreateExternalMediaStreamScreen.swift index 999385a0..10bf5156 100644 --- a/submodules/PeerInfoUI/CreateExternalMediaStreamScreen/Sources/CreateExternalMediaStreamScreen.swift +++ b/submodules/PeerInfoUI/CreateExternalMediaStreamScreen/Sources/CreateExternalMediaStreamScreen.swift @@ -242,7 +242,7 @@ private final class CreateExternalMediaStreamScreenComponent: CombinedComponent component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: theme.chat.inputPanel.panelControlColor ) )), action: { _ in diff --git a/submodules/PeerInfoUI/Sources/ChannelAdminController.swift b/submodules/PeerInfoUI/Sources/ChannelAdminController.swift index deb1d436..57a6e9a3 100644 --- a/submodules/PeerInfoUI/Sources/ChannelAdminController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelAdminController.swift @@ -1091,7 +1091,7 @@ public func channelAdminController(context: AccountContext, updatedPresentationD text = presentationData.strings.Channel_EditAdmin_CannotEdit } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) }) }, transferOwnership: { let _ = (context.engine.data.get( @@ -1104,19 +1104,30 @@ public func channelAdminController(context: AccountContext, updatedPresentationD } transferOwnershipDisposable.set((context.engine.peers.checkOwnershipTranfserAvailability(memberId: adminId) |> deliverOnMainQueue).start(error: { error in - let controller = channelOwnershipTransferController(context: context, updatedPresentationData: updatedPresentationData, peer: peer, member: member, initialError: error, present: { c, a in - presentControllerImpl?(c, a) - }, completion: { upgradedPeerId in - if let upgradedPeerId = upgradedPeerId { - upgradedToSupergroupImpl(upgradedPeerId, { + let controller = channelOwnershipTransferController( + context: context, + updatedPresentationData: updatedPresentationData, + peer: peer, + member: member, + initialError: error, + present: { c, a in + presentControllerImpl?(c, a) + }, + push: { c in + pushControllerImpl?(c) + }, + completion: { upgradedPeerId in + if let upgradedPeerId = upgradedPeerId { + upgradedToSupergroupImpl(upgradedPeerId, { + dismissImpl?() + transferedOwnership(member.id) + }) + } else { dismissImpl?() transferedOwnership(member.id) - }) - } else { - dismissImpl?() - transferedOwnership(member.id) + } } - }) + ) presentControllerImpl?(controller, nil) })) }) @@ -1617,20 +1628,21 @@ public func channelAdminController(context: AccountContext, updatedPresentationD rightNavigationButton = nil footerItem = ChannelAdminAddBotFooterItem(theme: presentationData.theme, title: state.adminRights ? presentationData.strings.Bot_AddToChat_Add_AddAsAdmin : presentationData.strings.Bot_AddToChat_Add_AddAsMember, action: { if state.adminRights { - let theme = AlertControllerTheme(presentationData: presentationData) - let attributedTitle = NSAttributedString(string: presentationData.strings.Bot_AddToChat_Add_AdminAlertTitle, font: Font.semibold(presentationData.listsFontSize.baseDisplaySize), textColor: theme.primaryColor, paragraphAlignment: .center) - let text = isGroup ? presentationData.strings.Bot_AddToChat_Add_AdminAlertTextGroup(peerTitle).string : presentationData.strings.Bot_AddToChat_Add_AdminAlertTextChannel(peerTitle).string - - let body = MarkdownAttributeSet(font: Font.regular(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: theme.primaryColor) - let bold = MarkdownAttributeSet(font: Font.semibold(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: theme.primaryColor) - let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .center) - - let controller = richTextAlertController(context: context, title: attributedTitle, text: attributedText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Bot_AddToChat_Add_AdminAlertAdd, action: { - rightButtonActionImpl() - }), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - })], actionLayout: .vertical) - presentControllerImpl?(controller, nil) + + let alertController = textAlertController( + context: context, + updatedPresentationData: updatedPresentationData, + title: presentationData.strings.Bot_AddToChat_Add_AdminAlertTitle, + text: text, + actions: [ + TextAlertAction(type: .defaultAction, title: presentationData.strings.Bot_AddToChat_Add_AdminAlertAdd, action: { rightButtonActionImpl() + }), + TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), + ], + actionLayout: .vertical + ) + presentControllerImpl?(alertController, nil) } else { rightButtonActionImpl() } diff --git a/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupSearchContainerNode.swift b/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupSearchContainerNode.swift index 52ff8fb0..c661eab9 100644 --- a/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupSearchContainerNode.swift +++ b/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupSearchContainerNode.swift @@ -135,7 +135,7 @@ final class ChannelDiscussionGroupSearchContainerNode: SearchDisplayControllerCo super.init() - self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5) + self.dimNode.backgroundColor = .clear self.listNode.backgroundColor = self.presentationData.theme.chatList.backgroundColor self.listNode.isHidden = true diff --git a/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupSetupHeaderItem.swift b/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupSetupHeaderItem.swift index 9a87b600..e98240c8 100644 --- a/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupSetupHeaderItem.swift +++ b/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupSetupHeaderItem.swift @@ -90,7 +90,7 @@ class ChannelDiscussionGroupSetupHeaderItemNode: ListViewItemNode { self.labelNode.contentMode = .left self.labelNode.contentsScale = UIScreen.main.scale - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.imageNode) self.addSubnode(self.titleNode) diff --git a/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupSetupSearchItem.swift b/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupSetupSearchItem.swift index d0e05756..b95ba046 100644 --- a/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupSetupSearchItem.swift +++ b/submodules/PeerInfoUI/Sources/ChannelDiscussionGroupSetupSearchItem.swift @@ -9,6 +9,11 @@ import ItemListUI import PresentationDataUtils import AccountContext import SearchBarNode +import GlassBackgroundComponent +import ComponentFlow +import ComponentDisplayAdapters +import AppBundle +import ActivityIndicator final class ChannelDiscussionGroupSetupSearchItem: ItemListControllerSearch { let context: AccountContext @@ -84,8 +89,8 @@ private final class ChannelDiscussionGroupSetupSearchItemNode: ItemListControlle } override func updateLayout(layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { - transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight), size: CGSize(width: layout.size.width, height: layout.size.height - navigationBarHeight))) - self.containerNode.containerLayoutUpdated(layout.withUpdatedSize(CGSize(width: layout.size.width, height: layout.size.height - navigationBarHeight)), navigationBarHeight: 0.0, transition: transition) + transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height))) + self.containerNode.containerLayoutUpdated(layout.withUpdatedSize(CGSize(width: layout.size.width, height: layout.size.height)), navigationBarHeight: navigationBarHeight, transition: transition) } override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { @@ -100,17 +105,40 @@ private final class ChannelDiscussionGroupSetupSearchItemNode: ItemListControlle private let searchBarFont = Font.regular(17.0) private final class ChannelDiscussionSearchNavigationContentNode: NavigationBarContentNode, ItemListControllerSearchNavigationContentNode { + private struct Params: Equatable { + let size: CGSize + let leftInset: CGFloat + let rightInset: CGFloat + + init(size: CGSize, leftInset: CGFloat, rightInset: CGFloat) { + self.size = size + self.leftInset = leftInset + self.rightInset = rightInset + } + } + private var theme: PresentationTheme private let strings: PresentationStrings private let cancel: () -> Void + private let backgroundContainer: GlassBackgroundContainerView + private let backgroundView: GlassBackgroundView + private let iconView: UIImageView + private var activityIndicator: ActivityIndicator? private let searchBar: SearchBarNode + private let close: (background: GlassBackgroundView, icon: UIImageView) + + private var params: Params? private var queryUpdated: ((String) -> Void)? var activity: Bool = false { didSet { - searchBar.activity = activity + if self.activity != oldValue { + if let params = self.params { + let _ = self.updateLayout(size: params.size, leftInset: params.leftInset, rightInset: params.rightInset, transition: .immediate) + } + } } } init(theme: PresentationTheme, strings: PresentationStrings, cancel: @escaping () -> Void, updateActivity: @escaping(@escaping(Bool)->Void) -> Void) { @@ -119,11 +147,42 @@ private final class ChannelDiscussionSearchNavigationContentNode: NavigationBarC self.cancel = cancel - self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: theme, hasSeparator: false), strings: strings, fieldStyle: .modern) + self.backgroundContainer = GlassBackgroundContainerView() + self.backgroundView = GlassBackgroundView() + self.backgroundContainer.contentView.addSubview(self.backgroundView) + self.iconView = UIImageView() + self.backgroundView.contentView.addSubview(self.iconView) + + self.close = (GlassBackgroundView(), UIImageView()) + self.close.background.contentView.addSubview(self.close.icon) + + self.searchBar = SearchBarNode( + theme: SearchBarNodeTheme( + background: .clear, + separator: .clear, + inputFill: .clear, + primaryText: theme.chat.inputPanel.panelControlColor, + placeholder: theme.chat.inputPanel.inputPlaceholderColor, + inputIcon: theme.chat.inputPanel.inputControlColor, + inputClear: theme.chat.inputPanel.panelControlColor, + accent: theme.chat.inputPanel.panelControlAccentColor, + keyboard: theme.rootController.keyboardColor + ), + presentationTheme: theme, + strings: strings, + fieldStyle: .inlineNavigation, + forceSeparator: false, + displayBackground: false, + cancelText: nil + ) super.init() - self.addSubnode(self.searchBar) + self.view.addSubview(self.backgroundContainer) + self.backgroundView.contentView.addSubview(self.searchBar.view) + + self.backgroundContainer.contentView.addSubview(self.close.background) + self.close.background.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.onCloseTapGesture(_:)))) self.searchBar.cancel = { [weak self] in self?.searchBar.deactivate(clear: false) @@ -141,13 +200,21 @@ private final class ChannelDiscussionSearchNavigationContentNode: NavigationBarC self.updatePlaceholder() } + @objc private func onCloseTapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + self.searchBar.cancel?() + } + } + func setQueryUpdated(_ f: @escaping (String) -> Void) { self.queryUpdated = f } func updateTheme(_ theme: PresentationTheme) { self.theme = theme - self.searchBar.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: self.theme), strings: self.strings) + if let params = self.params { + let _ = self.updateLayout(size: params.size, leftInset: params.leftInset, rightInset: params.rightInset, transition: .immediate) + } self.updatePlaceholder() } @@ -158,13 +225,87 @@ private final class ChannelDiscussionSearchNavigationContentNode: NavigationBarC } override var nominalHeight: CGFloat { - return 54.0 + return 60.0 } - override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { - let searchBarFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - self.nominalHeight), size: CGSize(width: size.width, height: 54.0)) - self.searchBar.frame = searchBarFrame - self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition) + override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { + self.params = Params(size: size, leftInset: leftInset, rightInset: rightInset) + + let transition = ComponentTransition(transition) + + let backgroundFrame = CGRect(origin: CGPoint(x: leftInset + 16.0, y: 6.0), size: CGSize(width: size.width - 16.0 * 2.0 - leftInset - rightInset - 44.0 - 8.0, height: 44.0)) + let closeFrame = CGRect(origin: CGPoint(x: size.width - 16.0 - rightInset - 44.0, y: backgroundFrame.minY), size: CGSize(width: 44.0, height: 44.0)) + + transition.setFrame(view: self.backgroundContainer, frame: CGRect(origin: CGPoint(), size: size)) + self.backgroundContainer.update(size: size, isDark: self.theme.overallDarkAppearance, transition: transition) + + transition.setFrame(view: self.backgroundView, frame: backgroundFrame) + self.backgroundView.update(size: backgroundFrame.size, cornerRadius: backgroundFrame.height * 0.5, isDark: self.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition) + + if self.iconView.image == nil { + self.iconView.image = UIImage(bundleImageName: "Navigation/Search")?.withRenderingMode(.alwaysTemplate) + } + transition.setTintColor(view: self.iconView, color: self.theme.rootController.navigationSearchBar.inputIconColor) + + if let image = self.iconView.image { + let imageSize: CGSize + let iconFrame: CGRect + let iconFraction: CGFloat = 0.8 + imageSize = CGSize(width: image.size.width * iconFraction, height: image.size.height * iconFraction) + iconFrame = CGRect(origin: CGPoint(x: 12.0, y: floor((backgroundFrame.height - imageSize.height) * 0.5)), size: imageSize) + transition.setPosition(view: self.iconView, position: iconFrame.center) + transition.setBounds(view: self.iconView, bounds: CGRect(origin: CGPoint(), size: iconFrame.size)) + } + + if self.activity { + let activityIndicator: ActivityIndicator + if let current = self.activityIndicator { + activityIndicator = current + } else { + activityIndicator = ActivityIndicator(type: .custom(self.theme.chat.inputPanel.inputControlColor, 14.0, 14.0, false)) + self.activityIndicator = activityIndicator + self.backgroundView.contentView.addSubview(activityIndicator.view) + } + let indicatorSize = activityIndicator.measure(CGSize(width: 32.0, height: 32.0)) + let indicatorFrame = CGRect(origin: CGPoint(x: 15.0, y: floorToScreenPixels((backgroundFrame.height - indicatorSize.height) * 0.5)), size: indicatorSize) + transition.setPosition(view: activityIndicator.view, position: indicatorFrame.center) + transition.setBounds(view: activityIndicator.view, bounds: CGRect(origin: CGPoint(), size: indicatorFrame.size)) + } else if let activityIndicator = self.activityIndicator { + self.activityIndicator = nil + activityIndicator.view.removeFromSuperview() + } + self.iconView.isHidden = self.activity + + let searchBarFrame = CGRect(origin: CGPoint(x: 36.0, y: 0.0), size: CGSize(width: backgroundFrame.width - 36.0 - 4.0, height: 44.0)) + transition.setFrame(view: self.searchBar.view, frame: searchBarFrame) + self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: 0.0, rightInset: 0.0, transition: transition.containedViewLayoutTransition) + + if self.close.icon.image == nil { + self.close.icon.image = generateImage(CGSize(width: 40.0, height: 40.0), contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + context.setLineWidth(2.0) + context.setLineCap(.round) + context.setStrokeColor(UIColor.white.cgColor) + + context.beginPath() + context.move(to: CGPoint(x: 12.0, y: 12.0)) + context.addLine(to: CGPoint(x: size.width - 12.0, y: size.height - 12.0)) + context.move(to: CGPoint(x: size.width - 12.0, y: 12.0)) + context.addLine(to: CGPoint(x: 12.0, y: size.height - 12.0)) + context.strokePath() + })?.withRenderingMode(.alwaysTemplate) + } + + if let image = close.icon.image { + self.close.icon.frame = image.size.centered(in: CGRect(origin: CGPoint(), size: closeFrame.size)) + } + self.close.icon.tintColor = self.theme.chat.inputPanel.panelControlColor + + transition.setFrame(view: self.close.background, frame: closeFrame) + self.close.background.update(size: closeFrame.size, cornerRadius: closeFrame.height * 0.5, isDark: self.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition) + + return size } func activate() { diff --git a/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift b/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift index 0116746e..1aec3ba6 100644 --- a/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift +++ b/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift @@ -1203,7 +1203,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon strongSelf.enqueueEmptyQueryTransition(transition, firstTime: firstTime) if entries == nil { - strongSelf.emptyQueryListNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5) + strongSelf.emptyQueryListNode.backgroundColor = .clear } else { strongSelf.emptyQueryListNode.backgroundColor = presentationData.theme.chatList.backgroundColor } diff --git a/submodules/PeerInfoUI/Sources/ChannelMembersSearchController.swift b/submodules/PeerInfoUI/Sources/ChannelMembersSearchController.swift index bd022fb7..d0135110 100644 --- a/submodules/PeerInfoUI/Sources/ChannelMembersSearchController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelMembersSearchController.swift @@ -42,8 +42,9 @@ public final class ChannelMembersSearchControllerImpl: ViewController, ChannelMe self.presentationData = self.presentationData.withUpdated(theme: forceTheme) } - super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData)) + super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData, style: .glass)) + self._hasGlassStyle = true self.navigationPresentation = .modal self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style diff --git a/submodules/PeerInfoUI/Sources/ChannelMembersSearchControllerNode.swift b/submodules/PeerInfoUI/Sources/ChannelMembersSearchControllerNode.swift index b9f3bf04..af8f4190 100644 --- a/submodules/PeerInfoUI/Sources/ChannelMembersSearchControllerNode.swift +++ b/submodules/PeerInfoUI/Sources/ChannelMembersSearchControllerNode.swift @@ -685,7 +685,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode { if let requestDeactivateSearch = self?.requestDeactivateSearch { requestDeactivateSearch() } - }) + }, fieldStyle: placeholderNode.fieldStyle) self.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: navigationBarHeight, transition: .immediate) self.searchDisplayController?.activate(insertSubnode: { [weak self, weak placeholderNode] subnode, isSearchBar in diff --git a/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift b/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift index 5e130739..701e1c79 100644 --- a/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift @@ -1132,22 +1132,26 @@ public func channelPermissionsController(context: AccountContext, updatedPresent controller.navigationPresentation = .modal controller.setState(.custom(icon: .animation("BroadcastGroup"), title: presentationData.strings.BroadcastGroups_IntroTitle, subtitle: nil, text: presentationData.strings.BroadcastGroups_IntroText, buttonTitle: presentationData.strings.BroadcastGroups_Convert, secondaryButtonTitle: presentationData.strings.BroadcastGroups_Cancel, footerText: nil), animated: false) controller.proceed = { [weak controller] result in - let attributedTitle = NSAttributedString(string: presentationData.strings.BroadcastGroups_ConfirmationAlert_Title, font: Font.semibold(presentationData.listsFontSize.baseDisplaySize), textColor: presentationData.theme.actionSheet.primaryTextColor, paragraphAlignment: .center) - let body = MarkdownAttributeSet(font: Font.regular(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: presentationData.theme.actionSheet.primaryTextColor) - let bold = MarkdownAttributeSet(font: Font.semibold(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: presentationData.theme.actionSheet.primaryTextColor) - let attributedText = parseMarkdownIntoAttributedString(presentationData.strings.BroadcastGroups_ConfirmationAlert_Text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .center) - - let alertController = richTextAlertController(context: context, title: attributedTitle, text: attributedText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.BroadcastGroups_ConfirmationAlert_Convert, action: { [weak controller] in - controller?.dismiss() - - let _ = (convertGroupToGigagroup(account: context.account, peerId: originalPeerId) - |> deliverOnMainQueue).start(completed: { - let participantsLimit = context.currentLimitsConfiguration.with { $0 }.maxSupergroupMemberCount - presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .gigagroupConversion(text: presentationData.strings.BroadcastGroups_Success(presentationStringsFormattedNumber(participantsLimit, presentationData.dateTimeFormat.decimalSeparator)).string), elevatedLayout: true, action: { _ in return false }), nil) - - dismissToChatController?() - }) - })]) + let alertController = textAlertController( + context: context, + updatedPresentationData: updatedPresentationData, + title: presentationData.strings.BroadcastGroups_ConfirmationAlert_Title, + text: presentationData.strings.BroadcastGroups_ConfirmationAlert_Text, + actions: [ + TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), + TextAlertAction(type: .defaultAction, title: presentationData.strings.BroadcastGroups_ConfirmationAlert_Convert, action: { [weak controller] in + controller?.dismiss() + + let _ = (convertGroupToGigagroup(account: context.account, peerId: originalPeerId) + |> deliverOnMainQueue).start(completed: { + let participantsLimit = context.currentLimitsConfiguration.with { $0 }.maxSupergroupMemberCount + presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .gigagroupConversion(text: presentationData.strings.BroadcastGroups_Success(presentationStringsFormattedNumber(participantsLimit, presentationData.dateTimeFormat.decimalSeparator)).string), elevatedLayout: true, action: { _ in return false }), nil) + + dismissToChatController?() + }) + }) + ] + ) controller?.present(alertController, in: .window(.root)) } pushControllerImpl?(controller) diff --git a/submodules/PeerInfoUI/Sources/ChatSlowmodeItem.swift b/submodules/PeerInfoUI/Sources/ChatSlowmodeItem.swift index 781c3eec..f87a8316 100644 --- a/submodules/PeerInfoUI/Sources/ChatSlowmodeItem.swift +++ b/submodules/PeerInfoUI/Sources/ChatSlowmodeItem.swift @@ -95,7 +95,7 @@ class ChatSlowmodeItemNode: ListViewItemNode { return textNode } - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.textNodes.forEach(self.addSubnode) } diff --git a/submodules/PeerInfoUI/Sources/ChatUnrestrictBoostersItem.swift b/submodules/PeerInfoUI/Sources/ChatUnrestrictBoostersItem.swift index baa545c7..4817be5e 100644 --- a/submodules/PeerInfoUI/Sources/ChatUnrestrictBoostersItem.swift +++ b/submodules/PeerInfoUI/Sources/ChatUnrestrictBoostersItem.swift @@ -103,7 +103,7 @@ class ChatUnrestrictBoostersItemNode: ListViewItemNode { return textNode } - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.iconNodes.forEach(self.addSubnode) self.textNodes.forEach(self.addSubnode) diff --git a/submodules/PeerInfoUI/Sources/GroupInfoSearchItem.swift b/submodules/PeerInfoUI/Sources/GroupInfoSearchItem.swift index 6f636531..46469ec4 100644 --- a/submodules/PeerInfoUI/Sources/GroupInfoSearchItem.swift +++ b/submodules/PeerInfoUI/Sources/GroupInfoSearchItem.swift @@ -110,8 +110,8 @@ private final class ChannelMembersSearchItemNode: ItemListControllerSearchNode { } override func updateLayout(layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { - transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight), size: CGSize(width: layout.size.width, height: layout.size.height - navigationBarHeight))) - self.containerNode.containerLayoutUpdated(layout.withUpdatedSize(CGSize(width: layout.size.width, height: layout.size.height - navigationBarHeight)), navigationBarHeight: 0.0, transition: transition) + transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height))) + self.containerNode.containerLayoutUpdated(layout.withUpdatedSize(CGSize(width: layout.size.width, height: layout.size.height)), navigationBarHeight: navigationBarHeight, transition: transition) } override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { diff --git a/submodules/PeerInfoUI/Sources/GroupInfoSearchNavigationContentNode.swift b/submodules/PeerInfoUI/Sources/GroupInfoSearchNavigationContentNode.swift index 16c072a3..a298a6ab 100644 --- a/submodules/PeerInfoUI/Sources/GroupInfoSearchNavigationContentNode.swift +++ b/submodules/PeerInfoUI/Sources/GroupInfoSearchNavigationContentNode.swift @@ -8,24 +8,53 @@ import TelegramPresentationData import ItemListUI import PresentationDataUtils import SearchBarNode +import GlassBackgroundComponent +import ComponentFlow +import ComponentDisplayAdapters +import AppBundle +import ActivityIndicator private let searchBarFont = Font.regular(17.0) final class GroupInfoSearchNavigationContentNode: NavigationBarContentNode, ItemListControllerSearchNavigationContentNode { + private struct Params: Equatable { + let size: CGSize + let leftInset: CGFloat + let rightInset: CGFloat + + init(size: CGSize, leftInset: CGFloat, rightInset: CGFloat) { + self.size = size + self.leftInset = leftInset + self.rightInset = rightInset + } + } + private var theme: PresentationTheme private let strings: PresentationStrings private let searchMode: ChannelMembersSearchMode private let cancel: () -> Void + private let backgroundContainer: GlassBackgroundContainerView + private let backgroundView: GlassBackgroundView + private let iconView: UIImageView + private var activityIndicator: ActivityIndicator? private let searchBar: SearchBarNode + private let close: (background: GlassBackgroundView, icon: UIImageView) + + private var params: Params? private var queryUpdated: ((String) -> Void)? var activity: Bool = false { didSet { - searchBar.activity = activity + if self.activity != oldValue { + if let params = self.params { + let _ = self.updateLayout(size: params.size, leftInset: params.leftInset, rightInset: params.rightInset, transition: .immediate) + } + } } } + init(theme: PresentationTheme, strings: PresentationStrings, mode: ChannelMembersSearchMode, cancel: @escaping () -> Void, updateActivity: @escaping(@escaping(Bool)->Void) -> Void) { self.theme = theme self.strings = strings @@ -33,11 +62,42 @@ final class GroupInfoSearchNavigationContentNode: NavigationBarContentNode, Item self.cancel = cancel - self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: theme, hasSeparator: false), strings: strings, fieldStyle: .modern, displayBackground: false) + self.backgroundContainer = GlassBackgroundContainerView() + self.backgroundView = GlassBackgroundView() + self.backgroundContainer.contentView.addSubview(self.backgroundView) + self.iconView = UIImageView() + self.backgroundView.contentView.addSubview(self.iconView) + + self.close = (GlassBackgroundView(), UIImageView()) + self.close.background.contentView.addSubview(self.close.icon) + + self.searchBar = SearchBarNode( + theme: SearchBarNodeTheme( + background: .clear, + separator: .clear, + inputFill: .clear, + primaryText: theme.chat.inputPanel.panelControlColor, + placeholder: theme.chat.inputPanel.inputPlaceholderColor, + inputIcon: theme.chat.inputPanel.inputControlColor, + inputClear: theme.chat.inputPanel.panelControlColor, + accent: theme.chat.inputPanel.panelControlAccentColor, + keyboard: theme.rootController.keyboardColor + ), + presentationTheme: theme, + strings: strings, + fieldStyle: .inlineNavigation, + forceSeparator: false, + displayBackground: false, + cancelText: nil + ) super.init() - self.addSubnode(self.searchBar) + self.view.addSubview(self.backgroundContainer) + self.backgroundView.contentView.addSubview(self.searchBar.view) + + self.backgroundContainer.contentView.addSubview(self.close.background) + self.close.background.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.onCloseTapGesture(_:)))) self.searchBar.cancel = { [weak self] in self?.searchBar.deactivate(clear: false) @@ -55,13 +115,21 @@ final class GroupInfoSearchNavigationContentNode: NavigationBarContentNode, Item self.updatePlaceholder() } + @objc private func onCloseTapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + self.searchBar.cancel?() + } + } + func setQueryUpdated(_ f: @escaping (String) -> Void) { self.queryUpdated = f } func updateTheme(_ theme: PresentationTheme) { self.theme = theme - self.searchBar.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: self.theme), strings: self.strings) + if let params = self.params { + let _ = self.updateLayout(size: params.size, leftInset: params.leftInset, rightInset: params.rightInset, transition: .immediate) + } self.updatePlaceholder() } @@ -77,13 +145,87 @@ final class GroupInfoSearchNavigationContentNode: NavigationBarContentNode, Item } override var nominalHeight: CGFloat { - return 54.0 + return 60.0 } - override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { - let searchBarFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - self.nominalHeight), size: CGSize(width: size.width, height: 54.0)) - self.searchBar.frame = searchBarFrame - self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition) + override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { + self.params = Params(size: size, leftInset: leftInset, rightInset: rightInset) + + let transition = ComponentTransition(transition) + + let backgroundFrame = CGRect(origin: CGPoint(x: leftInset + 16.0, y: 6.0), size: CGSize(width: size.width - 16.0 * 2.0 - leftInset - rightInset - 44.0 - 8.0, height: 44.0)) + let closeFrame = CGRect(origin: CGPoint(x: size.width - 16.0 - rightInset - 44.0, y: backgroundFrame.minY), size: CGSize(width: 44.0, height: 44.0)) + + transition.setFrame(view: self.backgroundContainer, frame: CGRect(origin: CGPoint(), size: size)) + self.backgroundContainer.update(size: size, isDark: self.theme.overallDarkAppearance, transition: transition) + + transition.setFrame(view: self.backgroundView, frame: backgroundFrame) + self.backgroundView.update(size: backgroundFrame.size, cornerRadius: backgroundFrame.height * 0.5, isDark: self.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition) + + if self.iconView.image == nil { + self.iconView.image = UIImage(bundleImageName: "Navigation/Search")?.withRenderingMode(.alwaysTemplate) + } + transition.setTintColor(view: self.iconView, color: self.theme.rootController.navigationSearchBar.inputIconColor) + + if let image = self.iconView.image { + let imageSize: CGSize + let iconFrame: CGRect + let iconFraction: CGFloat = 0.8 + imageSize = CGSize(width: image.size.width * iconFraction, height: image.size.height * iconFraction) + iconFrame = CGRect(origin: CGPoint(x: 12.0, y: floor((backgroundFrame.height - imageSize.height) * 0.5)), size: imageSize) + transition.setPosition(view: self.iconView, position: iconFrame.center) + transition.setBounds(view: self.iconView, bounds: CGRect(origin: CGPoint(), size: iconFrame.size)) + } + + if self.activity { + let activityIndicator: ActivityIndicator + if let current = self.activityIndicator { + activityIndicator = current + } else { + activityIndicator = ActivityIndicator(type: .custom(self.theme.chat.inputPanel.inputControlColor, 14.0, 14.0, false)) + self.activityIndicator = activityIndicator + self.backgroundView.contentView.addSubview(activityIndicator.view) + } + let indicatorSize = activityIndicator.measure(CGSize(width: 32.0, height: 32.0)) + let indicatorFrame = CGRect(origin: CGPoint(x: 15.0, y: floorToScreenPixels((backgroundFrame.height - indicatorSize.height) * 0.5)), size: indicatorSize) + transition.setPosition(view: activityIndicator.view, position: indicatorFrame.center) + transition.setBounds(view: activityIndicator.view, bounds: CGRect(origin: CGPoint(), size: indicatorFrame.size)) + } else if let activityIndicator = self.activityIndicator { + self.activityIndicator = nil + activityIndicator.view.removeFromSuperview() + } + self.iconView.isHidden = self.activity + + let searchBarFrame = CGRect(origin: CGPoint(x: 36.0, y: 0.0), size: CGSize(width: backgroundFrame.width - 36.0 - 4.0, height: 44.0)) + transition.setFrame(view: self.searchBar.view, frame: searchBarFrame) + self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: 0.0, rightInset: 0.0, transition: transition.containedViewLayoutTransition) + + if self.close.icon.image == nil { + self.close.icon.image = generateImage(CGSize(width: 40.0, height: 40.0), contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + context.setLineWidth(2.0) + context.setLineCap(.round) + context.setStrokeColor(UIColor.white.cgColor) + + context.beginPath() + context.move(to: CGPoint(x: 12.0, y: 12.0)) + context.addLine(to: CGPoint(x: size.width - 12.0, y: size.height - 12.0)) + context.move(to: CGPoint(x: size.width - 12.0, y: 12.0)) + context.addLine(to: CGPoint(x: 12.0, y: size.height - 12.0)) + context.strokePath() + })?.withRenderingMode(.alwaysTemplate) + } + + if let image = close.icon.image { + self.close.icon.frame = image.size.centered(in: CGRect(origin: CGPoint(), size: closeFrame.size)) + } + self.close.icon.tintColor = self.theme.chat.inputPanel.panelControlColor + + transition.setFrame(view: self.close.background, frame: closeFrame) + self.close.background.update(size: closeFrame.size, cornerRadius: closeFrame.height * 0.5, isDark: self.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition) + + return size } func activate() { @@ -94,4 +236,3 @@ final class GroupInfoSearchNavigationContentNode: NavigationBarContentNode, Item self.searchBar.deactivate(clear: false) } } - diff --git a/submodules/PeerInfoUI/Sources/ItemListCallListItem.swift b/submodules/PeerInfoUI/Sources/ItemListCallListItem.swift index 23bb4f30..cfd32b4a 100644 --- a/submodules/PeerInfoUI/Sources/ItemListCallListItem.swift +++ b/submodules/PeerInfoUI/Sources/ItemListCallListItem.swift @@ -174,7 +174,7 @@ public class ItemListCallListItemNode: ListViewItemNode { self.accessibilityArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.titleNode) self.addSubnode(self.accessibilityArea) diff --git a/submodules/PeerInfoUI/Sources/ItemListReactionItem.swift b/submodules/PeerInfoUI/Sources/ItemListReactionItem.swift index e1e2d04c..64133463 100644 --- a/submodules/PeerInfoUI/Sources/ItemListReactionItem.swift +++ b/submodules/PeerInfoUI/Sources/ItemListReactionItem.swift @@ -168,7 +168,7 @@ public class ItemListReactionItemNode: ListViewItemNode, ItemListItemNode { self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.titleNode) self.addSubnode(self.switchNode) diff --git a/submodules/PeerInfoUI/Sources/ItemListSecretChatKeyItem.swift b/submodules/PeerInfoUI/Sources/ItemListSecretChatKeyItem.swift index a9824f78..4d3b3634 100644 --- a/submodules/PeerInfoUI/Sources/ItemListSecretChatKeyItem.swift +++ b/submodules/PeerInfoUI/Sources/ItemListSecretChatKeyItem.swift @@ -123,7 +123,7 @@ class ItemListSecretChatKeyItemNode: ListViewItemNode { self.highlightedBackgroundNode = ASDisplayNode() self.highlightedBackgroundNode.isLayerBacked = true - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.titleNode) self.addSubnode(self.keyNode) diff --git a/submodules/PeerInfoUI/Sources/PeerAutoremoveTimeoutItem.swift b/submodules/PeerInfoUI/Sources/PeerAutoremoveTimeoutItem.swift index f45479be..b0a2827b 100644 --- a/submodules/PeerInfoUI/Sources/PeerAutoremoveTimeoutItem.swift +++ b/submodules/PeerInfoUI/Sources/PeerAutoremoveTimeoutItem.swift @@ -125,7 +125,7 @@ class PeerRemoveTimeoutItemNode: ListViewItemNode, ItemListItemNode { return TextNode() } - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.titleNodes.forEach(self.addSubnode) diff --git a/submodules/PeerInfoUI/Sources/UserInfoEditingPhoneActionItem.swift b/submodules/PeerInfoUI/Sources/UserInfoEditingPhoneActionItem.swift index 4fe64adb..ba292243 100644 --- a/submodules/PeerInfoUI/Sources/UserInfoEditingPhoneActionItem.swift +++ b/submodules/PeerInfoUI/Sources/UserInfoEditingPhoneActionItem.swift @@ -100,7 +100,7 @@ class UserInfoEditingPhoneActionItemNode: ListViewItemNode { self.highlightedBackgroundNode = ASDisplayNode() self.highlightedBackgroundNode.isLayerBacked = true - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.iconNode) self.addSubnode(self.titleNode) diff --git a/submodules/PeerInfoUI/Sources/UserInfoEditingPhoneItem.swift b/submodules/PeerInfoUI/Sources/UserInfoEditingPhoneItem.swift index 409db1eb..c3ff6f4c 100644 --- a/submodules/PeerInfoUI/Sources/UserInfoEditingPhoneItem.swift +++ b/submodules/PeerInfoUI/Sources/UserInfoEditingPhoneItem.swift @@ -131,7 +131,7 @@ class UserInfoEditingPhoneItemNode: ItemListRevealOptionsItemNode, ItemListItemN self.clearButton.displaysAsynchronously = false self.clearButton.isHidden = true - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.editableControlNode) self.addSubnode(self.labelNode) diff --git a/submodules/PeersNearbyUI/Sources/PeersNearbyHeaderItem.swift b/submodules/PeersNearbyUI/Sources/PeersNearbyHeaderItem.swift index 57f756bf..e1b70cb3 100644 --- a/submodules/PeersNearbyUI/Sources/PeersNearbyHeaderItem.swift +++ b/submodules/PeersNearbyUI/Sources/PeersNearbyHeaderItem.swift @@ -76,7 +76,7 @@ class PeersNearbyHeaderItemNode: ListViewItemNode { self.animationNode = DefaultAnimatedStickerNodeImpl() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.titleNode) self.addSubnode(self.animationNode) diff --git a/submodules/Postbox/Sources/DeletedMessagesView.swift b/submodules/Postbox/Sources/DeletedMessagesView.swift index 7f3b4eda..b1ab425f 100644 --- a/submodules/Postbox/Sources/DeletedMessagesView.swift +++ b/submodules/Postbox/Sources/DeletedMessagesView.swift @@ -15,7 +15,7 @@ final class MutableDeletedMessagesView: MutablePostboxView { for operation in operations { switch operation { case let .Remove(indices): - for (index, _) in indices { + for (index, _, _) in indices { testMessageIds.append(index.id) } default: diff --git a/submodules/Postbox/Sources/HistoryTagInfoView.swift b/submodules/Postbox/Sources/HistoryTagInfoView.swift index 912b4a86..9b9e0adc 100644 --- a/submodules/Postbox/Sources/HistoryTagInfoView.swift +++ b/submodules/Postbox/Sources/HistoryTagInfoView.swift @@ -32,7 +32,7 @@ final class MutableHistoryTagInfoView: MutablePostboxView { } case let .Remove(indicesAndTags): if self.currentIndex != nil { - for (index, tags) in indicesAndTags { + for (index, tags, _) in indicesAndTags { if tags.contains(self.tag) { if index == self.currentIndex { self.currentIndex = nil diff --git a/submodules/Postbox/Sources/MessageHistoryOperation.swift b/submodules/Postbox/Sources/MessageHistoryOperation.swift index 71e7e84d..c4537b58 100644 --- a/submodules/Postbox/Sources/MessageHistoryOperation.swift +++ b/submodules/Postbox/Sources/MessageHistoryOperation.swift @@ -2,7 +2,7 @@ import Foundation enum MessageHistoryOperation { case InsertMessage(IntermediateMessage) - case Remove([(MessageIndex, MessageTags)]) + case Remove([(MessageIndex, MessageTags, Int64?)]) case UpdateReadState(PeerId, CombinedPeerReadState) case UpdateEmbeddedMedia(MessageIndex, ReadBuffer) case UpdateTimestamp(MessageIndex, Int32) diff --git a/submodules/Postbox/Sources/MessageHistoryTable.swift b/submodules/Postbox/Sources/MessageHistoryTable.swift index 2d290a75..cd004d82 100644 --- a/submodules/Postbox/Sources/MessageHistoryTable.swift +++ b/submodules/Postbox/Sources/MessageHistoryTable.swift @@ -197,19 +197,19 @@ final class MessageHistoryTable: Table { let buckets = self.continuousIndexIntervalsForRemoving(accumulatedRemoveIndices) for bucket in buckets { - var indicesWithMetadata: [(MessageIndex, MessageTags)] = [] + var indicesWithMetadata: [(MessageIndex, MessageTags, Int64?)] = [] var globalIndicesWithMetadata: [(GlobalMessageTags, MessageIndex)] = [] for index in bucket { let tagsAndGlobalTags = self.justRemove(index, unsentMessageOperations: &unsentMessageOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations) - if let (tags, globalTags) = tagsAndGlobalTags { - indicesWithMetadata.append((index, tags)) + if let (tags, globalTags, threadId) = tagsAndGlobalTags { + indicesWithMetadata.append((index, tags, threadId)) if !globalTags.isEmpty { globalIndicesWithMetadata.append((globalTags, index)) } } else { - indicesWithMetadata.append((index, MessageTags())) + indicesWithMetadata.append((index, MessageTags(), nil)) } } assert(bucket.count == indicesWithMetadata.count) @@ -352,8 +352,8 @@ final class MessageHistoryTable: Table { processIndexOperationsCommitAccumulatedRemoveIndices(peerId: peerId, accumulatedRemoveIndices: &accumulatedRemoveIndices, updatedCombinedState: &updatedCombinedState, invalidateReadState: &invalidateReadState, unsentMessageOperations: &unsentMessageOperations, outputOperations: &outputOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations) var updatedGroupInfos: [MessageId: MessageGroupInfo] = [:] - if let (message, previousTags) = self.justUpdate(storeMessage.index, message: storeMessage, keepLocalTags: true, sharedKey: sharedKey, sharedBuffer: sharedBuffer, sharedEncoder: sharedEncoder, unsentMessageOperations: &unsentMessageOperations, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, updatedGroupInfos: &updatedGroupInfos, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations, updatedMedia: &updatedMedia) { - outputOperations.append(.Remove([(storeMessage.index, previousTags)])) + if let (message, previousTags, previousThreadId) = self.justUpdate(storeMessage.index, message: storeMessage, keepLocalTags: true, sharedKey: sharedKey, sharedBuffer: sharedBuffer, sharedEncoder: sharedEncoder, unsentMessageOperations: &unsentMessageOperations, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, updatedGroupInfos: &updatedGroupInfos, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations, updatedMedia: &updatedMedia) { + outputOperations.append(.Remove([(storeMessage.index, previousTags, previousThreadId)])) outputOperations.append(.InsertMessage(message)) if !updatedGroupInfos.isEmpty { outputOperations.append(.UpdateGroupInfos(updatedGroupInfos)) @@ -367,8 +367,8 @@ final class MessageHistoryTable: Table { processIndexOperationsCommitAccumulatedRemoveIndices(peerId: peerId, accumulatedRemoveIndices: &accumulatedRemoveIndices, updatedCombinedState: &updatedCombinedState, invalidateReadState: &invalidateReadState, unsentMessageOperations: &unsentMessageOperations, outputOperations: &outputOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations) var updatedGroupInfos: [MessageId: MessageGroupInfo] = [:] - if let (message, previousTags) = self.justUpdate(index, message: storeMessage, keepLocalTags: false, sharedKey: sharedKey, sharedBuffer: sharedBuffer, sharedEncoder: sharedEncoder, unsentMessageOperations: &unsentMessageOperations, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, updatedGroupInfos: &updatedGroupInfos, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations, updatedMedia: &updatedMedia) { - outputOperations.append(.Remove([(index, previousTags)])) + if let (message, previousTags, previousThreadId) = self.justUpdate(index, message: storeMessage, keepLocalTags: false, sharedKey: sharedKey, sharedBuffer: sharedBuffer, sharedEncoder: sharedEncoder, unsentMessageOperations: &unsentMessageOperations, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, updatedGroupInfos: &updatedGroupInfos, localTagsOperations: &localTagsOperations, timestampBasedMessageAttributesOperations: ×tampBasedMessageAttributesOperations, updatedMedia: &updatedMedia) { + outputOperations.append(.Remove([(index, previousTags, previousThreadId)])) outputOperations.append(.InsertMessage(message)) if !updatedGroupInfos.isEmpty { outputOperations.append(.UpdateGroupInfos(updatedGroupInfos)) @@ -944,7 +944,7 @@ final class MessageHistoryTable: Table { self.storeIntermediateMessage(updatedMessage, sharedKey: self.key(MessageIndex.absoluteLowerBound())) let operations: [MessageHistoryOperation] = [ - .Remove([(index, message.tags)]), + .Remove([(index, message.tags, message.threadId)]), .InsertMessage(updatedMessage) ] if operationsByPeerId[message.id.peerId] == nil { @@ -1352,7 +1352,7 @@ final class MessageHistoryTable: Table { return result } - private func justRemove(_ index: MessageIndex, unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation]) -> (MessageTags, GlobalMessageTags)? { + private func justRemove(_ index: MessageIndex, unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation]) -> (MessageTags, GlobalMessageTags, Int64?)? { let key = self.key(index) if let value = self.valueBox.get(self.table, key: key) { let resultTags: MessageTags @@ -1440,7 +1440,7 @@ final class MessageHistoryTable: Table { resultGlobalTags = message.globalTags self.valueBox.remove(self.table, key: key, secure: true) - return (resultTags, resultGlobalTags) + return (resultTags, resultGlobalTags, message.threadId) } else { return nil } @@ -1544,7 +1544,7 @@ final class MessageHistoryTable: Table { }) } - private func justUpdate(_ index: MessageIndex, message: InternalStoreMessage, keepLocalTags: Bool, sharedKey: ValueBoxKey, sharedBuffer: WriteBuffer, sharedEncoder: PostboxEncoder, unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], updatedGroupInfos: inout [MessageId: MessageGroupInfo], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], updatedMedia: inout [MediaId: Media?]) -> (IntermediateMessage, MessageTags)? { + private func justUpdate(_ index: MessageIndex, message: InternalStoreMessage, keepLocalTags: Bool, sharedKey: ValueBoxKey, sharedBuffer: WriteBuffer, sharedEncoder: PostboxEncoder, unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], updatedGroupInfos: inout [MessageId: MessageGroupInfo], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation], timestampBasedMessageAttributesOperations: inout [TimestampBasedMessageAttributesOperation], updatedMedia: inout [MediaId: Media?]) -> (IntermediateMessage, MessageTags, Int64?)? { if let previousMessage = self.getMessage(index) { var mediaToUpdate: [Media] = [] @@ -2037,7 +2037,7 @@ final class MessageHistoryTable: Table { self.valueBox.set(self.table, key: self.key(message.index, key: sharedKey), value: sharedBuffer) - let result = (IntermediateMessage(stableId: stableId, stableVersion: stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: groupInfo, threadId: message.threadId, timestamp: message.timestamp, flags: flags, tags: tags, globalTags: message.globalTags, localTags: updatedLocalTags, customTags: message.customTags, forwardInfo: intermediateForwardInfo, authorId: message.authorId, text: message.text, attributesData: attributesBuffer.makeReadBufferAndReset(), embeddedMediaData: embeddedMediaBuffer.makeReadBufferAndReset(), referencedMedia: referencedMedia), previousMessage.tags) + let result = (IntermediateMessage(stableId: stableId, stableVersion: stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: groupInfo, threadId: message.threadId, timestamp: message.timestamp, flags: flags, tags: tags, globalTags: message.globalTags, localTags: updatedLocalTags, customTags: message.customTags, forwardInfo: intermediateForwardInfo, authorId: message.authorId, text: message.text, attributesData: attributesBuffer.makeReadBufferAndReset(), embeddedMediaData: embeddedMediaBuffer.makeReadBufferAndReset(), referencedMedia: referencedMedia), previousMessage.tags, previousMessage.threadId) for media in mediaToUpdate { if let id = media.id { diff --git a/submodules/Postbox/Sources/MessageHistoryView.swift b/submodules/Postbox/Sources/MessageHistoryView.swift index a8b000f9..eb259183 100644 --- a/submodules/Postbox/Sources/MessageHistoryView.swift +++ b/submodules/Postbox/Sources/MessageHistoryView.swift @@ -676,7 +676,7 @@ final class MutableMessageHistoryView: MutablePostboxView { } } case let .Remove(indicesAndTags): - for (index, _) in indicesAndTags { + for (index, _, _) in indicesAndTags { if self.namespaces.contains(index.id.namespace) { if loadedState.remove(index: index) { hasChanges = true @@ -821,7 +821,7 @@ final class MutableMessageHistoryView: MutablePostboxView { } case let .Remove(indices): if !self.topTaggedMessages.isEmpty { - for (index, _) in indices { + for (index, _, _) in indices { if let maybeCurrentTopMessage = self.topTaggedMessages[index.id.namespace], let currentTopMessage = maybeCurrentTopMessage, index.id == currentTopMessage.id { let item: MessageHistoryTopTaggedMessage? = nil self.topTaggedMessages[index.id.namespace] = item @@ -873,7 +873,7 @@ final class MutableMessageHistoryView: MutablePostboxView { break findOperation } case let .Remove(indices): - for (index, _) in indices { + for (index, _, _) in indices { if currentIds.contains(index.id) { updateMessage = true break findOperation @@ -956,7 +956,7 @@ final class MutableMessageHistoryView: MutablePostboxView { break outer } case let .Remove(indicesWithTags): - for (index, _) in indicesWithTags { + for (index, _, _) in indicesWithTags { if cachedData.messageIds.contains(index.id) { updatedCachedPeerDataMessages = true break outer diff --git a/submodules/Postbox/Sources/MessageView.swift b/submodules/Postbox/Sources/MessageView.swift index 1ae3ed29..48312584 100644 --- a/submodules/Postbox/Sources/MessageView.swift +++ b/submodules/Postbox/Sources/MessageView.swift @@ -18,7 +18,7 @@ final class MutableMessageView { case let .Remove(indices): if let message = self.message { let messageIndex = message.index - for (index, _) in indices { + for (index, _, _) in indices { if index == messageIndex { self.message = nil updated = true diff --git a/submodules/Postbox/Sources/PeerChatTopIndexableMessageIds.swift b/submodules/Postbox/Sources/PeerChatTopIndexableMessageIds.swift index ed898666..84e6c996 100644 --- a/submodules/Postbox/Sources/PeerChatTopIndexableMessageIds.swift +++ b/submodules/Postbox/Sources/PeerChatTopIndexableMessageIds.swift @@ -1,7 +1,7 @@ import Foundation private struct PeerChatTopTaggedUpdateRecord: Equatable, Hashable { - let peerId: PeerId + let peerAndThreadId: PeerAndThreadId let namespace: MessageId.Namespace } @@ -10,71 +10,85 @@ final class PeerChatTopTaggedMessageIdsTable: Table { return ValueBoxTable(id: id, keyType: .binary, compactValuesOnCreation: true) } - private var cachedTopIds: [PeerId: [MessageId.Namespace: MessageId?]] = [:] + private var cachedTopIds: [PeerAndThreadId: [MessageId.Namespace: MessageId?]] = [:] private var updatedPeerIds = Set() - private let sharedKey = ValueBoxKey(length: 8 + 4) + private let sharedKeyNoThreadId = ValueBoxKey(length: 8 + 4) + private let sharedKeyWithThreadId = ValueBoxKey(length: 8 + 4 + 8) - private func key(peerId: PeerId, namespace: MessageId.Namespace) -> ValueBoxKey { - self.sharedKey.setInt64(0, value: peerId.toInt64()) - self.sharedKey.setInt32(8, value: namespace) - return self.sharedKey + private func key(combinedId: PeerAndThreadId, namespace: MessageId.Namespace) -> ValueBoxKey { + if let threadId = combinedId.threadId { + self.sharedKeyWithThreadId.setInt64(0, value: combinedId.peerId.toInt64()) + self.sharedKeyWithThreadId.setInt32(8, value: namespace) + self.sharedKeyWithThreadId.setInt64(8 + 4, value: threadId) + + return self.sharedKeyWithThreadId + } else { + self.sharedKeyNoThreadId.setInt64(0, value: combinedId.peerId.toInt64()) + self.sharedKeyNoThreadId.setInt32(8, value: namespace) + + return self.sharedKeyNoThreadId + } } - func get(peerId: PeerId, namespace: MessageId.Namespace) -> MessageId? { - if let cachedDict = self.cachedTopIds[peerId] { + func get(peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace) -> MessageId? { + let combinedId = PeerAndThreadId(peerId: peerId, threadId: threadId) + + if let cachedDict = self.cachedTopIds[combinedId] { if let maybeCachedId = cachedDict[namespace] { return maybeCachedId } else { - if let value = self.valueBox.get(self.table, key: self.key(peerId: peerId, namespace: namespace)) { + if let value = self.valueBox.get(self.table, key: self.key(combinedId: combinedId, namespace: namespace)) { var messageIdId: Int32 = 0 value.read(&messageIdId, offset: 0, length: 4) - self.cachedTopIds[peerId]![namespace] = MessageId(peerId: peerId, namespace: namespace, id: messageIdId) + self.cachedTopIds[combinedId]![namespace] = MessageId(peerId: peerId, namespace: namespace, id: messageIdId) return MessageId(peerId: peerId, namespace: namespace, id: messageIdId) } else { let item: MessageId? = nil - self.cachedTopIds[peerId]![namespace] = item + self.cachedTopIds[combinedId]![namespace] = item return nil } } } else { - if let value = self.valueBox.get(self.table, key: self.key(peerId: peerId, namespace: namespace)) { + if let value = self.valueBox.get(self.table, key: self.key(combinedId: combinedId, namespace: namespace)) { var messageIdId: Int32 = 0 value.read(&messageIdId, offset: 0, length: 4) - self.cachedTopIds[peerId] = [namespace: MessageId(peerId: peerId, namespace: namespace, id: messageIdId)] + self.cachedTopIds[combinedId] = [namespace: MessageId(peerId: peerId, namespace: namespace, id: messageIdId)] return MessageId(peerId: peerId, namespace: namespace, id: messageIdId) } else { let item: MessageId? = nil - self.cachedTopIds[peerId] = [namespace: item] + self.cachedTopIds[combinedId] = [namespace: item] return nil } } } - private func set(peerId: PeerId, namespace: MessageId.Namespace, id: MessageId?) { - if let _ = self.cachedTopIds[peerId] { - self.cachedTopIds[peerId]![namespace] = id + private func set(peerId: PeerId, threadId: Int64?, namespace: MessageId.Namespace, id: MessageId?) { + let combinedId = PeerAndThreadId(peerId: peerId, threadId: threadId) + + if let _ = self.cachedTopIds[combinedId] { + self.cachedTopIds[combinedId]![namespace] = id } else { - self.cachedTopIds[peerId] = [namespace: id] + self.cachedTopIds[combinedId] = [namespace: id] } - self.updatedPeerIds.insert(PeerChatTopTaggedUpdateRecord(peerId: peerId, namespace: namespace)) + self.updatedPeerIds.insert(PeerChatTopTaggedUpdateRecord(peerAndThreadId: combinedId, namespace: namespace)) } - func replay(historyOperationsByPeerId: [PeerId : [MessageHistoryOperation]]) { + func replay(historyOperationsByPeerId: [PeerId: [MessageHistoryOperation]]) { for (_, operations) in historyOperationsByPeerId { for operation in operations { switch operation { case let .InsertMessage(message): if message.flags.contains(.TopIndexable) { - let currentTopMessageId = self.get(peerId: message.id.peerId, namespace: message.id.namespace) + let currentTopMessageId = self.get(peerId: message.id.peerId, threadId: message.threadId, namespace: message.id.namespace) if currentTopMessageId == nil || currentTopMessageId! < message.id { - self.set(peerId: message.id.peerId, namespace: message.id.namespace, id: message.id) + self.set(peerId: message.id.peerId, threadId: message.threadId, namespace: message.id.namespace, id: message.id) } } case let .Remove(indices): - for (index, _) in indices { - if let messageId = self.get(peerId: index.id.peerId, namespace: index.id.namespace), index.id == messageId { - self.set(peerId: index.id.peerId, namespace: index.id.namespace, id: nil) + for (index, _, threadId) in indices { + if let messageId = self.get(peerId: index.id.peerId, threadId: threadId, namespace: index.id.namespace), index.id == messageId { + self.set(peerId: index.id.peerId, threadId: threadId, namespace: index.id.namespace, id: nil) } } default: @@ -92,12 +106,12 @@ final class PeerChatTopTaggedMessageIdsTable: Table { override func beforeCommit() { if !self.updatedPeerIds.isEmpty { for record in self.updatedPeerIds { - if let cachedDict = self.cachedTopIds[record.peerId], let maybeMessageId = cachedDict[record.namespace] { + if let cachedDict = self.cachedTopIds[record.peerAndThreadId], let maybeMessageId = cachedDict[record.namespace] { if let maybeMessageId = maybeMessageId { var messageIdId: Int32 = maybeMessageId.id - self.valueBox.set(self.table, key: self.key(peerId: record.peerId, namespace: record.namespace), value: MemoryBuffer(memory: &messageIdId, capacity: 4, length: 4, freeWhenDone: false)) + self.valueBox.set(self.table, key: self.key(combinedId: record.peerAndThreadId, namespace: record.namespace), value: MemoryBuffer(memory: &messageIdId, capacity: 4, length: 4, freeWhenDone: false)) } else { - self.valueBox.remove(self.table, key: self.key(peerId: record.peerId, namespace: record.namespace), secure: false) + self.valueBox.remove(self.table, key: self.key(combinedId: record.peerAndThreadId, namespace: record.namespace), secure: false) } } } diff --git a/submodules/Postbox/Sources/PeerView.swift b/submodules/Postbox/Sources/PeerView.swift index f7ff4c11..d0fa0b4f 100644 --- a/submodules/Postbox/Sources/PeerView.swift +++ b/submodules/Postbox/Sources/PeerView.swift @@ -247,7 +247,7 @@ final class MutablePeerView: MutablePostboxView { break outer } case let .Remove(indicesWithTags): - for (index, _) in indicesWithTags { + for (index, _, _) in indicesWithTags { if cachedData.messageIds.contains(index.id) { updateMessages = true break outer diff --git a/submodules/Postbox/Sources/Postbox.swift b/submodules/Postbox/Sources/Postbox.swift index 7f8e92ac..396fe12a 100644 --- a/submodules/Postbox/Sources/Postbox.swift +++ b/submodules/Postbox/Sources/Postbox.swift @@ -3494,20 +3494,20 @@ final class PostboxImpl { useRootInterfaceStateForThread: Bool ) -> Disposable { var topTaggedMessages: [MessageId.Namespace: MessageHistoryTopTaggedMessage?] = [:] - var mainPeerIdForTopTaggedMessages: PeerId? - switch peerIds { + var mainPeerIdForTopTaggedMessages: (peerId: PeerId, threadId: Int64?)? + if tag == nil { + switch peerIds { case let .single(id, threadId): - if threadId == nil { - mainPeerIdForTopTaggedMessages = id - } + mainPeerIdForTopTaggedMessages = (id, threadId) case let .associated(id, _): - mainPeerIdForTopTaggedMessages = id + mainPeerIdForTopTaggedMessages = (id, nil) case .external: mainPeerIdForTopTaggedMessages = nil + } } if let peerId = mainPeerIdForTopTaggedMessages { for namespace in topTaggedMessageIdNamespaces { - if let messageId = self.peerChatTopTaggedMessageIdsTable.get(peerId: peerId, namespace: namespace) { + if let messageId = self.peerChatTopTaggedMessageIdsTable.get(peerId: peerId.peerId, threadId: peerId.threadId, namespace: namespace) { if let index = self.messageHistoryIndexTable.getIndex(messageId) { if let message = self.messageHistoryTable.getMessage(index) { topTaggedMessages[namespace] = MessageHistoryTopTaggedMessage.intermediate(message) diff --git a/submodules/PremiumUI/BUILD b/submodules/PremiumUI/BUILD index ac8046b6..f2a16847 100644 --- a/submodules/PremiumUI/BUILD +++ b/submodules/PremiumUI/BUILD @@ -123,6 +123,9 @@ swift_library( "//submodules/TelegramUI/Components/Premium/PremiumCoinComponent", "//submodules/TelegramUI/Components/GlassBarButtonComponent", "//submodules/TelegramUI/Components/EdgeEffect", + "//submodules/TelegramUI/Components/Gifts/TableComponent", + "//submodules/TelegramUI/Components/Gifts/PeerTableCellComponent", + "//submodules/TelegramUI/Components/AlertComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/PremiumUI/Sources/CreateGiveawayController.swift b/submodules/PremiumUI/Sources/CreateGiveawayController.swift index 52330915..810c269d 100644 --- a/submodules/PremiumUI/Sources/CreateGiveawayController.swift +++ b/submodules/PremiumUI/Sources/CreateGiveawayController.swift @@ -493,7 +493,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry { } }) case let .starsMore(theme, title): - return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: { + return ItemListPeerActionItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesItemList.downArrowImage(theme), title: title, sectionId: self.section, editing: false, action: { arguments.expandStars() }) case let .starsInfo(_, text): @@ -521,14 +521,14 @@ private enum CreateGiveawayEntry: ItemListNodeEntry { if case let .channel(channel) = peer, case .group = channel.info { isGroup = true } - return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: PresentationDateTimeFormat(), nameDisplayOrder: presentationData.nameDisplayOrder, context: arguments.context, peer: peer, presence: nil, text: boosts.flatMap { .text(isGroup ? presentationData.strings.BoostGift_GroupBoosts($0) : presentationData.strings.BoostGift_ChannelsBoosts($0), .secondary) } ?? .none, label: .none, editing: ItemListPeerItemEditing(editable: boosts == nil, editing: false, revealed: isRevealed), switchValue: nil, enabled: true, selectable: peer.id != arguments.context.account.peerId, sectionId: self.section, action: { + return ItemListPeerItem(presentationData: presentationData, systemStyle: .glass, dateTimeFormat: PresentationDateTimeFormat(), nameDisplayOrder: presentationData.nameDisplayOrder, context: arguments.context, peer: peer, presence: nil, text: boosts.flatMap { .text(isGroup ? presentationData.strings.BoostGift_GroupBoosts($0) : presentationData.strings.BoostGift_ChannelsBoosts($0), .secondary) } ?? .none, label: .none, editing: ItemListPeerItemEditing(editable: boosts == nil, editing: false, revealed: isRevealed), switchValue: nil, enabled: true, selectable: peer.id != arguments.context.account.peerId, sectionId: self.section, action: { }, setPeerIdWithRevealedOptions: { lhs, rhs in arguments.setItemIdWithRevealedOptions(lhs, rhs) }, removePeer: { id in arguments.removeChannel(id) }) case let .channelAdd(theme, text): - return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.roundPlusIconImage(theme), title: text, alwaysPlain: false, hasSeparator: true, sectionId: self.section, height: .compactPeerList, color: .accent, editing: false, action: { + return ItemListPeerActionItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesItemList.roundPlusIconImage(theme), title: text, alwaysPlain: false, hasSeparator: true, sectionId: self.section, height: .compactPeerList, color: .accent, editing: false, action: { arguments.openChannelsSelection() }) case let .channelsInfo(_, text): @@ -582,7 +582,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry { arguments.openPremiumIntro() }) case let .prizeDescription(_, text, value): - return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in + return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in arguments.updateState { state in var updatedState = state updatedState.showPrizeDescription = value @@ -590,7 +590,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry { } }) case let .prizeDescriptionText(_, placeholder, value, count): - return ItemListSingleLineInputItem(presentationData: presentationData, title: NSAttributedString(string: "\(count)"), text: value, placeholder: placeholder, returnKeyType: .done, spacing: 24.0, maxLength: 128, tag: CreateGiveawayEntryTag.description, sectionId: self.section, textUpdated: { value in + return ItemListSingleLineInputItem(presentationData: presentationData, systemStyle: .glass, title: NSAttributedString(string: "\(count)"), text: value, placeholder: placeholder, returnKeyType: .done, spacing: 24.0, maxLength: 128, tag: CreateGiveawayEntryTag.description, sectionId: self.section, textUpdated: { value in arguments.updateState { state in var updatedState = state updatedState.prizeDescription = value @@ -616,7 +616,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry { } else { text = presentationData.strings.InviteLink_Create_TimeLimitExpiryDateNever } - return ItemListDisclosureItem(presentationData: presentationData, title: presentationData.strings.BoostGift_DateEnds, label: text, labelStyle: active ? .coloredText(theme.list.itemAccentColor) : .text, sectionId: self.section, style: .blocks, disclosureStyle: .none, action: { + return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, title: presentationData.strings.BoostGift_DateEnds, label: text, labelStyle: active ? .coloredText(theme.list.itemAccentColor) : .text, sectionId: self.section, style: .blocks, disclosureStyle: .none, action: { arguments.dismissInput() var focus = false arguments.updateState { state in @@ -677,7 +677,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry { case let .timeInfo(_, text): return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) case let .winners(_, text, value): - return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in + return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in arguments.updateState { state in var updatedState = state updatedState.showWinners = value diff --git a/submodules/PremiumUI/Sources/GiftOptionItem.swift b/submodules/PremiumUI/Sources/GiftOptionItem.swift index 12166ff4..eac888a1 100644 --- a/submodules/PremiumUI/Sources/GiftOptionItem.swift +++ b/submodules/PremiumUI/Sources/GiftOptionItem.swift @@ -212,7 +212,7 @@ class GiftOptionItemNode: ItemListRevealOptionsItemNode { self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.containerNode) diff --git a/submodules/PremiumUI/Sources/GiveawayInfoController.swift b/submodules/PremiumUI/Sources/GiveawayInfoController.swift index b50ada30..2db71bc9 100644 --- a/submodules/PremiumUI/Sources/GiveawayInfoController.swift +++ b/submodules/PremiumUI/Sources/GiveawayInfoController.swift @@ -7,8 +7,8 @@ import TelegramCore import AccountContext import TelegramStringFormatting import TelegramPresentationData -import Markdown -import AlertUI +import ComponentFlow +import AlertComponent public func presentGiveawayInfoController( context: AccountContext, @@ -23,6 +23,8 @@ public func presentGiveawayInfoController( peerIds.append(adminId) } + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let _ = (context.engine.data.get( TelegramEngine.EngineData.Item.Messages.Message(id: messageId), EngineDataMap(peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init)) @@ -121,22 +123,16 @@ public func presentGiveawayInfoController( channelsCount = 1 + giveawayResults.additionalChannelsCount } - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - - - let timeZone = TimeZone.current let untilDate = stringForDate(timestamp: untilDateValue, timeZone: timeZone, strings: presentationData.strings) let title: String - let text: String + var text: String var warning: String? - - var dismissImpl: (() -> Void)? - - var actions: [TextAlertAction] = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { - dismissImpl?() - })] + + var actions: [AlertScreen.Action] = [ + .init(title: presentationData.strings.Common_OK, type: .default) + ] var additionalPrizes = "" if let prizeDescription, !prizeDescription.isEmpty { @@ -322,278 +318,65 @@ public func presentGiveawayInfoController( case .refunded: result = "" warning = presentationData.strings.Chat_Giveaway_Info_Refunded - actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Close, action: { - dismissImpl?() - })] case .notWon: result = "**\(presentationData.strings.Chat_Giveaway_Info_DidntWin)**\n\n" case let .wonPremium(slug): result = "**\(presentationData.strings.Chat_Giveaway_Info_Won("").string)**\n\n" - actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Chat_Giveaway_Info_ViewPrize, action: { - dismissImpl?() - openLink(slug) - }), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - dismissImpl?() - })] + actions = [ + .init(title: presentationData.strings.Chat_Giveaway_Info_ViewPrize, type: .default, action: { + openLink(slug) + }), + .init(title: presentationData.strings.Common_Cancel) + ] case let .wonStars(stars): let _ = stars result = "**\(presentationData.strings.Chat_Giveaway_Info_Won("").string)**\n\n" - actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Chat_Giveaway_Info_ViewPrize, action: { - dismissImpl?() - openLink("") - }), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - dismissImpl?() - })] + actions = [ + .init(title: presentationData.strings.Chat_Giveaway_Info_ViewPrize, type: .default, action: { + openLink("") + }), + .init(title: presentationData.strings.Common_Cancel) + ] } text = "\(result)\(intro)\(additionalPrizes)\n\n\(ending)" } - let alertController = giveawayInfoAlertController( - context: context, - updatedPresentationData: updatedPresentationData, - title: title, - text: text, - warning: warning, - actions: actions - ) - dismissImpl = { [weak alertController] in - alertController?.dismissAnimated() + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: title) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(text)) + ) + )) + if let warning { + content.append(AnyComponentWithIdentity( + id: "warning", + component: AnyComponent( + AlertTextComponent(content: .plain(warning), color: .destructive, style: .background(.bold)) + ) + )) } + + var effectiveUpdatedPresentationData: (PresentationData, Signal) + if let updatedPresentationData { + effectiveUpdatedPresentationData = updatedPresentationData + } else { + effectiveUpdatedPresentationData = (presentationData, context.sharedContext.presentationData) + } + + let alertController = AlertScreen( + configuration: AlertScreen.Configuration(actionAlignment: .vertical), + content: content, + actions: actions, + updatedPresentationData: effectiveUpdatedPresentationData + ) present(alertController) }) } - -private final class GiveawayInfoAlertContentNode: AlertContentNode { - private let title: String - private let text: String - private let warning: String? - - private let titleNode: ASTextNode - private let textNode: ASTextNode - fileprivate let warningBackgroundNode: ASImageNode - fileprivate let warningTextNode: ImmediateTextNode - - private let actionNodesSeparator: ASDisplayNode - private let actionNodes: [TextAlertContentActionNode] - private let actionVerticalSeparators: [ASDisplayNode] - - private var validLayout: CGSize? - - public var theme: PresentationTheme - - public override var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled - } - - public init(theme: AlertControllerTheme, ptheme: PresentationTheme, title: String, text: String, warning: String?, actions: [TextAlertAction]) { - self.theme = ptheme - self.title = title - self.text = text - self.warning = warning - - self.titleNode = ASTextNode() - self.titleNode.maximumNumberOfLines = 0 - self.textNode = ASTextNode() - self.textNode.maximumNumberOfLines = 0 - - self.warningBackgroundNode = ASImageNode() - self.warningBackgroundNode.displaysAsynchronously = false - - self.warningTextNode = ImmediateTextNode() - self.warningTextNode.maximumNumberOfLines = 0 - self.warningTextNode.lineSpacing = 0.1 - - self.actionNodesSeparator = ASDisplayNode() - self.actionNodesSeparator.isLayerBacked = true - - self.actionNodes = actions.map { action -> TextAlertContentActionNode in - return TextAlertContentActionNode(theme: theme, action: action) - } - - var actionVerticalSeparators: [ASDisplayNode] = [] - if actions.count > 1 { - for _ in 0 ..< actions.count - 1 { - let separatorNode = ASDisplayNode() - separatorNode.isLayerBacked = true - actionVerticalSeparators.append(separatorNode) - } - } - self.actionVerticalSeparators = actionVerticalSeparators - - super.init() - - self.addSubnode(self.titleNode) - self.addSubnode(self.textNode) - - self.addSubnode(self.warningBackgroundNode) - self.addSubnode(self.warningTextNode) - - self.addSubnode(self.actionNodesSeparator) - - for actionNode in self.actionNodes { - self.addSubnode(actionNode) - } - - for separatorNode in self.actionVerticalSeparators { - self.addSubnode(separatorNode) - } - - self.updateTheme(theme) - } - - public override func updateTheme(_ theme: AlertControllerTheme) { - self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.bold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center) - - let body = MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor) - let bold = MarkdownAttributeSet(font: Font.semibold(13.0), textColor: theme.primaryColor) - let attributedText = parseMarkdownIntoAttributedString(self.text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .center) - - self.textNode.attributedText = attributedText - - self.warningTextNode.attributedText = NSAttributedString(string: self.warning ?? "", font: Font.semibold(13.0), textColor: theme.destructiveColor, paragraphAlignment: .center) - self.warningBackgroundNode.image = generateStretchableFilledCircleImage(radius: 5.0, color: theme.destructiveColor.withAlphaComponent(0.1)) - - self.actionNodesSeparator.backgroundColor = theme.separatorColor - for actionNode in self.actionNodes { - actionNode.updateTheme(theme) - } - for separatorNode in self.actionVerticalSeparators { - separatorNode.backgroundColor = theme.separatorColor - } - - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) - } - } - - public override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - var size = size - size.width = min(size.width, 270.0) - let measureSize = CGSize(width: size.width - 16.0 * 2.0, height: CGFloat.greatestFiniteMagnitude) - - self.validLayout = size - - var origin: CGPoint = CGPoint(x: 0.0, y: 20.0) - - let titleSize = self.titleNode.measure(measureSize) - transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize)) - origin.y += titleSize.height + 4.0 - - let textSize = self.textNode.measure(measureSize) - transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize)) - origin.y += textSize.height + 6.0 - - let actionButtonHeight: CGFloat = 44.0 - var minActionsWidth: CGFloat = 0.0 - let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) - let actionTitleInsets: CGFloat = 8.0 - - var effectiveActionLayout = TextAlertContentActionLayout.horizontal - for actionNode in self.actionNodes { - let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) - if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 { - effectiveActionLayout = .vertical - } - switch effectiveActionLayout { - case .horizontal: - minActionsWidth += actionTitleSize.width + actionTitleInsets - case .vertical: - minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) - } - } - if "".isEmpty { - effectiveActionLayout = .vertical - } - let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0) - - var contentWidth = max(titleSize.width, minActionsWidth) - contentWidth = max(contentWidth, 234.0) - - var actionsHeight: CGFloat = 0.0 - switch effectiveActionLayout { - case .horizontal: - actionsHeight = actionButtonHeight - case .vertical: - actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) - } - - let resultWidth = contentWidth + insets.left + insets.right - - var warningHeight: CGFloat = 0.0 - if let _ = self.warning { - let warningSize = self.warningTextNode.updateLayout(measureSize) - let warningFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - warningSize.width) / 2.0), y: origin.y + 20.0), size: warningSize) - transition.updateFrame(node: self.warningTextNode, frame: warningFrame) - - transition.updateFrame(node: self.warningBackgroundNode, frame: warningFrame.insetBy(dx: -8.0, dy: -8.0)) - - warningHeight += warningSize.height + 26.0 - } - - let resultSize = CGSize(width: resultWidth, height: titleSize.height + textSize.height + 8.0 + actionsHeight + warningHeight + insets.top + insets.bottom) - - transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - - var actionOffset: CGFloat = 0.0 - let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) - var separatorIndex = -1 - var nodeIndex = 0 - for actionNode in self.actionNodes { - if separatorIndex >= 0 { - let separatorNode = self.actionVerticalSeparators[separatorIndex] - switch effectiveActionLayout { - case .horizontal: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) - case .vertical: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - } - } - separatorIndex += 1 - - let currentActionWidth: CGFloat - switch effectiveActionLayout { - case .horizontal: - if nodeIndex == self.actionNodes.count - 1 { - currentActionWidth = resultSize.width - actionOffset - } else { - currentActionWidth = actionWidth - } - case .vertical: - currentActionWidth = resultSize.width - } - - let actionNodeFrame: CGRect - switch effectiveActionLayout { - case .horizontal: - actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += currentActionWidth - case .vertical: - actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += actionButtonHeight - } - - transition.updateFrame(node: actionNode, frame: actionNodeFrame) - - nodeIndex += 1 - } - - return resultSize - } -} - -private func giveawayInfoAlertController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, title: String, text: String, warning: String?, actions: [TextAlertAction]) -> AlertController { - let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } - - let contentNode = GiveawayInfoAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, title: title, text: text, warning: warning, actions: actions) - - let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode) - let presentationDataDisposable = (updatedPresentationData?.signal ?? context.sharedContext.presentationData).start(next: { [weak controller] presentationData in - controller?.theme = AlertControllerTheme(presentationData: presentationData) - }) - controller.dismissed = { _ in - presentationDataDisposable.dispose() - } - - return controller -} diff --git a/submodules/PremiumUI/Sources/IncreaseLimitHeaderItem.swift b/submodules/PremiumUI/Sources/IncreaseLimitHeaderItem.swift index f203973b..14600adf 100644 --- a/submodules/PremiumUI/Sources/IncreaseLimitHeaderItem.swift +++ b/submodules/PremiumUI/Sources/IncreaseLimitHeaderItem.swift @@ -99,7 +99,7 @@ class IncreaseLimitHeaderItemNode: ListViewItemNode { self.textNode.contentMode = .left self.textNode.contentsScale = UIScreen.main.scale - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.titleNode) self.addSubnode(self.textNode) diff --git a/submodules/PremiumUI/Sources/PremiumDemoScreen.swift b/submodules/PremiumUI/Sources/PremiumDemoScreen.swift index e531a8b1..2decd5b6 100644 --- a/submodules/PremiumUI/Sources/PremiumDemoScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumDemoScreen.swift @@ -1133,7 +1133,7 @@ private final class DemoSheetContent: CombinedComponent { let closeButton = closeButton.update( component: GlassBarButtonComponent( - size: CGSize(width: 40.0, height: 40.0), + size: CGSize(width: 44.0, height: 44.0), backgroundColor: UIColor(rgb: 0x7f76f4), isDark: false, state: .tintedGlass, @@ -1147,7 +1147,7 @@ private final class DemoSheetContent: CombinedComponent { component.dismiss() } ), - availableSize: CGSize(width: 40.0, height: 40.0), + availableSize: CGSize(width: 44.0, height: 44.0), transition: .immediate ) context.add(closeButton diff --git a/submodules/PremiumUI/Sources/PremiumGiftCodeScreen.swift b/submodules/PremiumUI/Sources/PremiumGiftCodeScreen.swift index be059544..a17a1396 100644 --- a/submodules/PremiumUI/Sources/PremiumGiftCodeScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumGiftCodeScreen.swift @@ -23,6 +23,8 @@ import InvisibleInkDustNode import PremiumStarComponent import GlassBarButtonComponent import ButtonComponent +import TableComponent +import PeerTableCellComponent private final class PremiumGiftCodeSheetContent: CombinedComponent { typealias EnvironmentType = ViewControllerComponentContainer.Environment @@ -163,7 +165,7 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent { component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: theme.chat.inputPanel.panelControlColor ) )), action: { _ in @@ -326,7 +328,7 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent { title: strings.GiftLink_From, component: AnyComponent( Button( - content: AnyComponent(PeerCellComponent(context: context.component.context, textColor: tableLinkColor, peer: fromPeer)), + content: AnyComponent(PeerTableCellComponent(context: context.component.context, theme: theme, strings: strings, peer: fromPeer)), action: { if let peer = fromPeer, peer.id != accountContext.account.peerId { component.openPeer(peer) @@ -344,7 +346,7 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent { title: strings.GiftLink_To, component: AnyComponent( Button( - content: AnyComponent(PeerCellComponent(context: context.component.context, textColor: tableLinkColor, peer: toPeer)), + content: AnyComponent(PeerTableCellComponent(context: context.component.context, theme: theme, strings: strings, peer: toPeer)), action: { if toPeer.id != accountContext.account.peerId { component.openPeer(toPeer) @@ -857,306 +859,6 @@ final class GiftLinkButtonContentComponent: CombinedComponent { } } -private final class TableComponent: CombinedComponent { - class Item: Equatable { - public let id: AnyHashable - public let title: String - public let component: AnyComponent - - public init(id: IdType, title: String, component: AnyComponent) { - self.id = AnyHashable(id) - self.title = title - self.component = component - } - - public static func == (lhs: Item, rhs: Item) -> Bool { - if lhs.id != rhs.id { - return false - } - if lhs.title != rhs.title { - return false - } - if lhs.component != rhs.component { - return false - } - return true - } - } - - private let theme: PresentationTheme - private let items: [Item] - - public init(theme: PresentationTheme, items: [Item]) { - self.theme = theme - self.items = items - } - - public static func ==(lhs: TableComponent, rhs: TableComponent) -> Bool { - if lhs.theme !== rhs.theme { - return false - } - if lhs.items != rhs.items { - return false - } - return true - } - - final class State: ComponentState { - var cachedBorderImage: (UIImage, PresentationTheme)? - } - - func makeState() -> State { - return State() - } - - public static var body: Body { - let leftColumnBackground = Child(Rectangle.self) - let verticalBorder = Child(Rectangle.self) - let titleChildren = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self) - let valueChildren = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self) - let borderChildren = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self) - let outerBorder = Child(Image.self) - - return { context in - let verticalPadding: CGFloat = 11.0 - let horizontalPadding: CGFloat = 12.0 - let borderWidth: CGFloat = 1.0 - - let backgroundColor = context.component.theme.actionSheet.opaqueItemBackgroundColor - let borderColor = backgroundColor.mixedWith(context.component.theme.list.itemBlocksSeparatorColor, alpha: 0.6) - - var leftColumnWidth: CGFloat = 0.0 - - var updatedTitleChildren: [_UpdatedChildComponent] = [] - var updatedValueChildren: [_UpdatedChildComponent] = [] - var updatedBorderChildren: [_UpdatedChildComponent] = [] - - for item in context.component.items { - let titleChild = titleChildren[item.id].update( - component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: item.title, font: Font.regular(15.0), textColor: context.component.theme.list.itemPrimaryTextColor)) - )), - availableSize: context.availableSize, - transition: context.transition - ) - updatedTitleChildren.append(titleChild) - - if titleChild.size.width > leftColumnWidth { - leftColumnWidth = titleChild.size.width - } - } - - leftColumnWidth = max(100.0, leftColumnWidth + horizontalPadding * 2.0) - let rightColumnWidth = context.availableSize.width - leftColumnWidth - - var i = 0 - var rowHeights: [Int: CGFloat] = [:] - var totalHeight: CGFloat = 0.0 - - for item in context.component.items { - let titleChild = updatedTitleChildren[i] - let valueChild = valueChildren[item.id].update( - component: item.component, - availableSize: CGSize(width: rightColumnWidth - horizontalPadding * 2.0, height: context.availableSize.height), - transition: context.transition - ) - updatedValueChildren.append(valueChild) - - let rowHeight = max(40.0, max(titleChild.size.height, valueChild.size.height) + verticalPadding * 2.0) - rowHeights[i] = rowHeight - totalHeight += rowHeight - - if i < context.component.items.count - 1 { - let borderChild = borderChildren[item.id].update( - component: AnyComponent(Rectangle(color: borderColor)), - availableSize: CGSize(width: context.availableSize.width, height: borderWidth), - transition: context.transition - ) - updatedBorderChildren.append(borderChild) - } - - i += 1 - } - - let leftColumnBackground = leftColumnBackground.update( - component: Rectangle(color: context.component.theme.list.itemInputField.backgroundColor), - availableSize: CGSize(width: leftColumnWidth, height: totalHeight), - transition: context.transition - ) - context.add( - leftColumnBackground - .position(CGPoint(x: leftColumnWidth / 2.0, y: totalHeight / 2.0)) - ) - - let borderImage: UIImage - if let (currentImage, theme) = context.state.cachedBorderImage, theme === context.component.theme { - borderImage = currentImage - } else { - let borderRadius: CGFloat = 14.0 - borderImage = generateImage(CGSize(width: borderRadius * 2.0 + 6.0, height: borderRadius * 2.0 + 6.0), rotatedContext: { size, context in - let bounds = CGRect(origin: .zero, size: size) - context.setFillColor(backgroundColor.cgColor) - context.fill(bounds) - - let path = CGPath(roundedRect: bounds.insetBy(dx: borderWidth / 2.0, dy: borderWidth / 2.0), cornerWidth: borderRadius, cornerHeight: borderRadius, transform: nil) - context.setBlendMode(.clear) - context.addPath(path) - context.fillPath() - - context.setBlendMode(.normal) - context.setStrokeColor(borderColor.cgColor) - context.setLineWidth(borderWidth) - context.addPath(path) - context.strokePath() - })!.stretchableImage(withLeftCapWidth: Int(borderRadius), topCapHeight: Int(borderRadius)) - context.state.cachedBorderImage = (borderImage, context.component.theme) - } - - let outerBorder = outerBorder.update( - component: Image(image: borderImage), - availableSize: CGSize(width: context.availableSize.width, height: totalHeight), - transition: context.transition - ) - context.add(outerBorder - .position(CGPoint(x: context.availableSize.width / 2.0, y: totalHeight / 2.0)) - ) - - let verticalBorder = verticalBorder.update( - component: Rectangle(color: borderColor), - availableSize: CGSize(width: borderWidth, height: totalHeight), - transition: context.transition - ) - context.add( - verticalBorder - .position(CGPoint(x: leftColumnWidth - borderWidth / 2.0, y: totalHeight / 2.0)) - ) - - i = 0 - var originY: CGFloat = 0.0 - for (titleChild, valueChild) in zip(updatedTitleChildren, updatedValueChildren) { - let rowHeight = rowHeights[i] ?? 0.0 - - let titleFrame = CGRect(origin: CGPoint(x: horizontalPadding, y: originY + verticalPadding), size: titleChild.size) - let valueFrame = CGRect(origin: CGPoint(x: leftColumnWidth + horizontalPadding, y: originY + verticalPadding), size: valueChild.size) - - context.add(titleChild - .position(titleFrame.center) - ) - - context.add(valueChild - .position(valueFrame.center) - ) - - if i < updatedBorderChildren.count { - let borderChild = updatedBorderChildren[i] - context.add(borderChild - .position(CGPoint(x: context.availableSize.width / 2.0, y: originY + rowHeight - borderWidth / 2.0)) - ) - } - - originY += rowHeight - i += 1 - } - - return CGSize(width: context.availableSize.width, height: totalHeight) - } - } -} - -private final class PeerCellComponent: Component { - let context: AccountContext - let textColor: UIColor - let peer: EnginePeer? - - init(context: AccountContext, textColor: UIColor, peer: EnginePeer?) { - self.context = context - self.textColor = textColor - self.peer = peer - } - - static func ==(lhs: PeerCellComponent, rhs: PeerCellComponent) -> Bool { - if lhs.context !== rhs.context { - return false - } - if lhs.textColor !== rhs.textColor { - return false - } - if lhs.peer != rhs.peer { - return false - } - return true - } - - final class View: UIView { - private let avatarNode: AvatarNode - private let text = ComponentView() - - private var component: PeerCellComponent? - private weak var state: EmptyComponentState? - - override init(frame: CGRect) { - self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 13.0)) - - super.init(frame: frame) - - self.addSubnode(self.avatarNode) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func update(component: PeerCellComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { - self.component = component - self.state = state - - self.avatarNode.setPeer( - context: component.context, - theme: component.context.sharedContext.currentPresentationData.with({ $0 }).theme, - peer: component.peer, - synchronousLoad: true - ) - - let avatarSize = CGSize(width: 22.0, height: 22.0) - let spacing: CGFloat = 6.0 - - let textSize = self.text.update( - transition: .immediate, - component: AnyComponent( - MultilineTextComponent( - text: .plain(NSAttributedString(string: component.peer?.compactDisplayTitle ?? "", font: Font.regular(15.0), textColor: component.textColor, paragraphAlignment: .left)) - ) - ), - environment: {}, - containerSize: CGSize(width: availableSize.width - avatarSize.width - spacing, height: availableSize.height) - ) - - let size = CGSize(width: avatarSize.width + textSize.width + spacing, height: textSize.height) - - let avatarFrame = CGRect(origin: CGPoint(x: 0.0, y: floorToScreenPixels((size.height - avatarSize.height) / 2.0)), size: avatarSize) - self.avatarNode.frame = avatarFrame - - if let view = self.text.view { - if view.superview == nil { - self.addSubview(view) - } - let textFrame = CGRect(origin: CGPoint(x: avatarSize.width + spacing, y: floorToScreenPixels((size.height - textSize.height) / 2.0)), size: textSize) - transition.setFrame(view: view, frame: textFrame) - } - - return size - } - } - - func makeView() -> View { - return View(frame: CGRect()) - } - - func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { - return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) - } -} - private final class DustComponent: Component { let color: UIColor diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift index 71f3a6e4..b8f679fe 100644 --- a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift @@ -1679,34 +1679,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { } } }) - - self.newPerksDisposable = combineLatest( - queue: Queue.mainQueue(), - ApplicationSpecificNotice.dismissedBusinessBadge(accountManager: context.sharedContext.accountManager), - ApplicationSpecificNotice.dismissedBusinessLinksBadge(accountManager: context.sharedContext.accountManager), - ApplicationSpecificNotice.dismissedBusinessIntroBadge(accountManager: context.sharedContext.accountManager), - ApplicationSpecificNotice.dismissedBusinessChatbotsBadge(accountManager: context.sharedContext.accountManager) - ).startStrict(next: { [weak self] dismissedBusinessBadge, dismissedBusinessLinksBadge, dismissedBusinessIntroBadge, dismissedBusinessChatbotsBadge in - guard let self else { - return - } - var newPerks: [String] = [] - if !dismissedBusinessBadge { - newPerks.append(PremiumPerk.business.identifier) - } - if !dismissedBusinessLinksBadge { - newPerks.append(PremiumPerk.businessLinks.identifier) - } - if !dismissedBusinessIntroBadge { - newPerks.append(PremiumPerk.businessIntro.identifier) - } - if !dismissedBusinessChatbotsBadge { - newPerks.append(PremiumPerk.businessChatBots.identifier) - } - self.newPerks = newPerks - self.updated() - }) - + self.adsEnabledDisposable = (context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.AdsEnabled(id: context.account.peerId)) |> deliverOnMainQueue).start(next: { [weak self] adsEnabled in guard let self else { @@ -2219,7 +2192,6 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { demoSubject = .todo case .business: demoSubject = .business - let _ = ApplicationSpecificNotice.setDismissedBusinessBadge(accountManager: accountContext.sharedContext.accountManager).startStandalone() default: demoSubject = .doubleLimits } @@ -2418,7 +2390,6 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { } push(accountContext.sharedContext.makeChatbotSetupScreen(context: accountContext, initialData: initialData)) }) - let _ = ApplicationSpecificNotice.setDismissedBusinessChatbotsBadge(accountManager: accountContext.sharedContext.accountManager).startStandalone() case .businessIntro: let _ = (accountContext.sharedContext.makeBusinessIntroSetupScreenInitialData(context: accountContext) |> take(1) @@ -2428,7 +2399,6 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { } push(accountContext.sharedContext.makeBusinessIntroSetupScreen(context: accountContext, initialData: initialData)) }) - let _ = ApplicationSpecificNotice.setDismissedBusinessIntroBadge(accountManager: accountContext.sharedContext.accountManager).startStandalone() case .businessLinks: let _ = (accountContext.sharedContext.makeBusinessLinksSetupScreenInitialData(context: accountContext) |> take(1) @@ -2438,7 +2408,6 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { } push(accountContext.sharedContext.makeBusinessLinksSetupScreen(context: accountContext, initialData: initialData)) }) - let _ = ApplicationSpecificNotice.setDismissedBusinessLinksBadge(accountManager: accountContext.sharedContext.accountManager).startStandalone() default: fatalError() } @@ -2457,13 +2426,10 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { demoSubject = .businessAwayMessage case .businessChatBots: demoSubject = .businessChatBots - let _ = ApplicationSpecificNotice.setDismissedBusinessChatbotsBadge(accountManager: accountContext.sharedContext.accountManager).startStandalone() case .businessIntro: demoSubject = .businessIntro - let _ = ApplicationSpecificNotice.setDismissedBusinessIntroBadge(accountManager: accountContext.sharedContext.accountManager).startStandalone() case .businessLinks: demoSubject = .businessLinks - let _ = ApplicationSpecificNotice.setDismissedBusinessLinksBadge(accountManager: accountContext.sharedContext.accountManager).startStandalone() default: fatalError() } @@ -2960,6 +2926,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { private final class PremiumIntroScreenComponent: CombinedComponent { typealias EnvironmentType = ViewControllerComponentContainer.Environment + let overNavigationContainer: UIView let screenContext: PremiumIntroScreen.ScreenContext let mode: PremiumIntroScreen.Mode let source: PremiumSource @@ -2972,7 +2939,8 @@ private final class PremiumIntroScreenComponent: CombinedComponent { let copyLink: (String) -> Void let shareLink: (String) -> Void - init(screenContext: PremiumIntroScreen.ScreenContext, mode: PremiumIntroScreen.Mode, source: PremiumSource, forceDark: Bool, forceHasPremium: Bool, updateInProgress: @escaping (Bool) -> Void, present: @escaping (ViewController) -> Void, push: @escaping (ViewController) -> Void, completion: @escaping () -> Void, copyLink: @escaping (String) -> Void, shareLink: @escaping (String) -> Void) { + init(overNavigationContainer: UIView, screenContext: PremiumIntroScreen.ScreenContext, mode: PremiumIntroScreen.Mode, source: PremiumSource, forceDark: Bool, forceHasPremium: Bool, updateInProgress: @escaping (Bool) -> Void, present: @escaping (ViewController) -> Void, push: @escaping (ViewController) -> Void, completion: @escaping () -> Void, copyLink: @escaping (String) -> Void, shareLink: @escaping (String) -> Void) { + self.overNavigationContainer = overNavigationContainer self.screenContext = screenContext self.mode = mode self.source = source @@ -3415,8 +3383,6 @@ private final class PremiumIntroScreenComponent: CombinedComponent { let star = Child(PremiumStarComponent.self) let emoji = Child(EmojiHeaderComponent.self) let coin = Child(PremiumCoinComponent.self) - let topPanel = Child(BlurredBackgroundComponent.self) - let topSeparator = Child(Rectangle.self) let title = Child(MultilineTextComponent.self) let secondaryTitle = Child(MultilineTextWithEntitiesComponent.self) let bottomEdgeEffect = Child(EdgeEffectComponent.self) @@ -3504,22 +3470,6 @@ private final class PremiumIntroScreenComponent: CombinedComponent { ) } - let topPanel = topPanel.update( - component: BlurredBackgroundComponent( - color: environment.theme.rootController.navigationBar.blurredBackgroundColor - ), - availableSize: CGSize(width: context.availableSize.width, height: environment.navigationHeight), - transition: context.transition - ) - - let topSeparator = topSeparator.update( - component: Rectangle( - color: environment.theme.rootController.navigationBar.separatorColor - ), - availableSize: CGSize(width: context.availableSize.width, height: UIScreenPixel), - transition: context.transition - ) - let titleString: String if case .premiumGift = context.component.source { titleString = environment.strings.Premium_PremiumGift_Title @@ -3743,14 +3693,12 @@ private final class PremiumIntroScreenComponent: CombinedComponent { .position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0)) ) - let topPanelAlpha: CGFloat let titleOffset: CGFloat let titleScale: CGFloat let titleOffsetDelta = (topInset + 160.0) - (environment.statusBarHeight + (environment.navigationHeight - environment.statusBarHeight) / 2.0) let titleAlpha: CGFloat if let topContentOffset = state.topContentOffset { - topPanelAlpha = min(20.0, max(0.0, topContentOffset - 95.0)) / 20.0 let topContentOffset = topContentOffset + max(0.0, min(1.0, topContentOffset / titleOffsetDelta)) * 10.0 titleOffset = topContentOffset let fraction = max(0.0, min(1.0, titleOffset / titleOffsetDelta)) @@ -3762,36 +3710,29 @@ private final class PremiumIntroScreenComponent: CombinedComponent { titleAlpha = 1.0 } } else { - topPanelAlpha = 0.0 titleScale = 1.0 titleOffset = 0.0 titleAlpha = state.otherPeerName != nil ? 0.0 : 1.0 } - context.add(header + context.addWithExternalContainer(header .position(CGPoint(x: context.availableSize.width / 2.0, y: topInset + header.size.height / 2.0 - 30.0 - titleOffset * titleScale)) - .scale(titleScale) + .scale(titleScale), + container: context.component.overNavigationContainer ) - context.add(topPanel - .position(CGPoint(x: context.availableSize.width / 2.0, y: topPanel.size.height / 2.0)) - .opacity(topPanelAlpha) - ) - context.add(topSeparator - .position(CGPoint(x: context.availableSize.width / 2.0, y: topPanel.size.height)) - .opacity(topPanelAlpha) - ) - - context.add(title + context.addWithExternalContainer(title .position(CGPoint(x: context.availableSize.width / 2.0, y: max(topInset + 160.0 - titleOffset, environment.statusBarHeight + (environment.navigationHeight - environment.statusBarHeight) / 2.0))) .scale(titleScale) - .opacity(titleAlpha) + .opacity(titleAlpha), + container: context.component.overNavigationContainer ) - context.add(secondaryTitle + context.addWithExternalContainer(secondaryTitle .position(CGPoint(x: context.availableSize.width / 2.0, y: max(topInset + 160.0 - titleOffset, environment.statusBarHeight + (environment.navigationHeight - environment.statusBarHeight) / 2.0))) .scale(titleScale) - .opacity(max(0.0, 1.0 - titleAlpha * 1.8)) + .opacity(max(0.0, 1.0 - titleAlpha * 1.8)), + container: context.component.overNavigationContainer ) var isUnusedGift = false @@ -3989,6 +3930,8 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer { public weak var containerView: UIView? public var animationColor: UIColor? + private let overNavigationContainer: UIView + public convenience init(context: AccountContext, mode: Mode = .premium, source: PremiumSource, modal: Bool = true, forceDark: Bool = false, forceHasPremium: Bool = false) { self.init(screenContext: .accountContext(context), mode: mode, source: source, modal: modal, forceDark: forceDark, forceHasPremium: forceHasPremium) } @@ -4005,7 +3948,11 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer { var completionImpl: (() -> Void)? var copyLinkImpl: ((String) -> Void)? var shareLinkImpl: ((String) -> Void)? + + self.overNavigationContainer = UIView() + super.init(component: PremiumIntroScreenComponent( + overNavigationContainer: self.overNavigationContainer, screenContext: screenContext, mode: mode, source: source, @@ -4029,11 +3976,11 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer { shareLink: { link in shareLinkImpl?(link) } - ), navigationBarAppearance: .transparent, presentationMode: modal ? .modal : .default, theme: forceDark ? .dark : .default, updatedPresentationData: screenContext.updatedPresentationData) + ), navigationBarAppearance: .default, presentationMode: modal ? .modal : .default, theme: forceDark ? .dark : .default, updatedPresentationData: screenContext.updatedPresentationData) + + self._hasGlassStyle = true if modal { - let cancelItem = UIBarButtonItem(title: presentationData.strings.Common_Close, style: .plain, target: self, action: #selector(self.cancelPressed)) - self.navigationItem.setLeftBarButton(cancelItem, animated: false) self.navigationPresentation = .modal } else { self.navigationPresentation = .modalInLargeLayout @@ -4107,12 +4054,20 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer { context.account.viewTracker.keepQuickRepliesApproximatelyUpdated() context.account.viewTracker.keepBusinessLinksApproximatelyUpdated() } + + if let navigationBar = self.navigationBar { + navigationBar.view.insertSubview(self.overNavigationContainer, aboveSubview: navigationBar.backgroundView) + } } required public init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } + override public func viewDidLoad() { + super.viewDidLoad() + } + public override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) self.dismissAllTooltips() @@ -4145,10 +4100,10 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer { super.containerLayoutUpdated(layout, transition: transition) if !self.didSetReady { - if let view = self.node.hostView.findTaggedView(tag: PremiumCoinComponent.View.Tag()) as? PremiumCoinComponent.View { + if let view = findTaggedComponentViewImpl(view: self.view, tag: PremiumCoinComponent.View.Tag()) as? PremiumCoinComponent.View { self.didSetReady = true self._ready.set(view.ready) - } else if let view = self.node.hostView.findTaggedView(tag: PremiumStarComponent.View.Tag()) as? PremiumStarComponent.View { + } else if let view = findTaggedComponentViewImpl(view: self.view, tag: PremiumStarComponent.View.Tag()) as? PremiumStarComponent.View { self.didSetReady = true self._ready.set(view.ready) @@ -4161,7 +4116,7 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer { self.containerView = nil self.animationColor = nil } - } else if let view = self.node.hostView.findTaggedView(tag: EmojiHeaderComponent.View.Tag()) as? EmojiHeaderComponent.View { + } else if let view = findTaggedComponentViewImpl(view: self.view, tag: EmojiHeaderComponent.View.Tag()) as? EmojiHeaderComponent.View { self.didSetReady = true self._ready.set(view.ready) diff --git a/submodules/PremiumUI/Sources/PremiumLimitScreen.swift b/submodules/PremiumUI/Sources/PremiumLimitScreen.swift index e3ff83a2..447f9b2f 100644 --- a/submodules/PremiumUI/Sources/PremiumLimitScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumLimitScreen.swift @@ -864,7 +864,7 @@ private final class LimitSheetContent: CombinedComponent { component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: theme.chat.inputPanel.panelControlColor ) )), action: { _ in diff --git a/submodules/PremiumUI/Sources/PremiumPrivacyScreen.swift b/submodules/PremiumUI/Sources/PremiumPrivacyScreen.swift index c04daa16..7b357651 100644 --- a/submodules/PremiumUI/Sources/PremiumPrivacyScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumPrivacyScreen.swift @@ -170,7 +170,7 @@ private final class SheetContent: CombinedComponent { component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: theme.chat.inputPanel.panelControlColor ) )), action: { _ in diff --git a/submodules/PremiumUI/Sources/SubscriptionsCountItem.swift b/submodules/PremiumUI/Sources/SubscriptionsCountItem.swift index 6fecfbfb..861c43d7 100644 --- a/submodules/PremiumUI/Sources/SubscriptionsCountItem.swift +++ b/submodules/PremiumUI/Sources/SubscriptionsCountItem.swift @@ -91,7 +91,7 @@ private final class SubscriptionsCountItemNode: ListViewItemNode { return textNode } - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.textNodes.forEach(self.addSubnode) } diff --git a/submodules/PresentationDataUtils/BUILD b/submodules/PresentationDataUtils/BUILD index 66b10496..28dcd03d 100644 --- a/submodules/PresentationDataUtils/BUILD +++ b/submodules/PresentationDataUtils/BUILD @@ -19,6 +19,7 @@ swift_library( "//submodules/SolidRoundedButtonNode:SolidRoundedButtonNode", "//submodules/OverlayStatusController:OverlayStatusController", "//submodules/UrlWhitelist:UrlWhitelist", + "//submodules/TelegramUI/Components/AlertComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/PresentationDataUtils/Sources/AlertTheme.swift b/submodules/PresentationDataUtils/Sources/AlertTheme.swift index 1fa7f48d..8353b3d4 100644 --- a/submodules/PresentationDataUtils/Sources/AlertTheme.swift +++ b/submodules/PresentationDataUtils/Sources/AlertTheme.swift @@ -4,35 +4,107 @@ import AlertUI import AccountContext import SwiftSignalKit import TelegramPresentationData +import AlertComponent -public func textAlertController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, forceTheme: PresentationTheme? = nil, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, parseMarkdown: Bool = false, dismissOnOutsideTap: Bool = true, linkAction: (([NSAttributedString.Key: Any], Int) -> Void)? = nil) -> AlertController { +public func textAlertController( + context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, + forceTheme: PresentationTheme? = nil, + title: String?, + text: String, + actions: [TextAlertAction], + actionLayout: TextAlertContentActionLayout = .horizontal, + allowInputInset: Bool = true, + parseMarkdown: Bool = false, + dismissOnOutsideTap: Bool = true, + linkAction: (([NSAttributedString.Key: Any], Int) -> Void)? = nil +) -> ViewController { return textAlertController(sharedContext: context.sharedContext, updatedPresentationData: updatedPresentationData, forceTheme: forceTheme, title: title, text: text, actions: actions, actionLayout: actionLayout, allowInputInset: allowInputInset, parseMarkdown: parseMarkdown, dismissOnOutsideTap: dismissOnOutsideTap, linkAction: linkAction) } -public func textAlertController(sharedContext: SharedAccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, forceTheme: PresentationTheme? = nil, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, parseMarkdown: Bool = false, dismissOnOutsideTap: Bool = true, linkAction: (([NSAttributedString.Key: Any], Int) -> Void)? = nil) -> AlertController { +public func textAlertController( + sharedContext: SharedAccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, + forceTheme: PresentationTheme? = nil, + title: String?, + text: String, + actions: [TextAlertAction], + actionLayout: TextAlertContentActionLayout = .horizontal, + allowInputInset: Bool = true, + parseMarkdown: Bool = false, + dismissOnOutsideTap: Bool = true, + linkAction: (([NSAttributedString.Key: Any], Int) -> Void)? = nil +) -> ViewController { var presentationData = updatedPresentationData?.initial ?? sharedContext.currentPresentationData.with { $0 } - if let forceTheme = forceTheme { + if let forceTheme { presentationData = presentationData.withUpdated(theme: forceTheme) } - return textAlertController(alertContext: AlertControllerContext(theme: AlertControllerTheme(presentationData: presentationData), themeSignal: (updatedPresentationData?.signal ?? sharedContext.presentationData) |> map { + let updatedPresentationDataSignal = (updatedPresentationData?.signal ?? sharedContext.presentationData) |> map { presentationData in var presentationData = presentationData if let forceTheme = forceTheme { presentationData = presentationData.withUpdated(theme: forceTheme) } - return AlertControllerTheme(presentationData: presentationData) - }), title: title, text: text, actions: actions, actionLayout: actionLayout, allowInputInset: allowInputInset, parseMarkdown: parseMarkdown, dismissOnOutsideTap: dismissOnOutsideTap, linkAction: linkAction) + return presentationData + } + + let mappedActions: [AlertScreen.Action] = actions.map { action in + let mappedType: AlertScreen.Action.ActionType + switch action.type { + case .genericAction: + mappedType = .generic + case .defaultAction: + mappedType = .default + case .destructiveAction: + mappedType = .destructive + case .defaultDestructiveAction: + mappedType = .defaultDestructive + } + return AlertScreen.Action( + title: action.title, + type: mappedType, + action: action.action + ) + } + + let controller = AlertScreen( + configuration: AlertScreen.Configuration( + actionAlignment: actionLayout == .vertical ? .vertical : .default, + dismissOnOutsideTap: dismissOnOutsideTap, + allowInputInset: allowInputInset + ), + title: title, + text: text, + textAction: { attributes in + linkAction?(attributes, 0) + }, + actions: mappedActions, + updatedPresentationData: (initial: presentationData, signal: updatedPresentationDataSignal) + ) + return controller } -public func textAlertController(sharedContext: SharedAccountContext, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, dismissOnOutsideTap: Bool = true) -> AlertController { - return textAlertController(alertContext: AlertControllerContext(theme: AlertControllerTheme(presentationData: sharedContext.currentPresentationData.with { $0 }), themeSignal: sharedContext.presentationData |> map { presentationData in AlertControllerTheme(presentationData: presentationData) }), title: title, text: text, actions: actions, actionLayout: actionLayout, allowInputInset: allowInputInset, dismissOnOutsideTap: dismissOnOutsideTap) -} - -public func richTextAlertController(context: AccountContext, title: NSAttributedString?, text: NSAttributedString, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, dismissAutomatically: Bool = true) -> AlertController { +public func richTextAlertController( + context: AccountContext, + title: NSAttributedString?, + text: NSAttributedString, + actions: [TextAlertAction], + actionLayout: TextAlertContentActionLayout = .horizontal, + allowInputInset: Bool = true, + dismissAutomatically: Bool = true +) -> AlertController { return richTextAlertController(alertContext: AlertControllerContext(theme: AlertControllerTheme(presentationData: context.sharedContext.currentPresentationData.with { $0 }), themeSignal: context.sharedContext.presentationData |> map { presentationData in AlertControllerTheme(presentationData: presentationData) }), title: title, text: text, actions: actions, actionLayout: actionLayout, allowInputInset: allowInputInset, dismissAutomatically: dismissAutomatically) } -public func textWithEntitiesAlertController(context: AccountContext, title: NSAttributedString?, text: NSAttributedString, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, dismissAutomatically: Bool = true) -> AlertController { +public func textWithEntitiesAlertController( + context: AccountContext, + title: NSAttributedString?, + text: NSAttributedString, + actions: [TextAlertAction], + actionLayout: TextAlertContentActionLayout = .horizontal, + allowInputInset: Bool = true, + dismissAutomatically: Bool = true +) -> AlertController { return textWithEntitiesAlertController( alertContext: AlertControllerContext( theme: AlertControllerTheme(presentationData: context.sharedContext.currentPresentationData.with { $0 }), diff --git a/submodules/PresentationDataUtils/Sources/OpenUrl.swift b/submodules/PresentationDataUtils/Sources/OpenUrl.swift index 31f70d75..1bb12626 100644 --- a/submodules/PresentationDataUtils/Sources/OpenUrl.swift +++ b/submodules/PresentationDataUtils/Sources/OpenUrl.swift @@ -6,6 +6,7 @@ import AccountContext import OverlayStatusController import UrlWhitelist import TelegramPresentationData +import AlertComponent public func openUserGeneratedUrl(context: AccountContext, peerId: PeerId?, url: String, concealed: Bool, skipUrlAuth: Bool = false, skipConcealedAlert: Bool = false, forceDark: Bool = false, present: @escaping (ViewController) -> Void, openResolved: @escaping (ResolvedUrl) -> Void, progress: Promise? = nil, alertDisplayUpdated: ((ViewController?) -> Void)? = nil) -> Disposable { var concealed = concealed @@ -95,8 +96,10 @@ public func openUserGeneratedUrl(context: AccountContext, peerId: PeerId?, url: let alertController = textAlertController(context: context, forceTheme: forceDark ? presentationData.theme : nil, title: nil, text: presentationData.strings.Generic_OpenHiddenLinkAlert(displayUrl).string, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_No, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Yes, action: { disposable.set(openImpl()) })]) - alertController.dismissed = { _ in - alertDisplayUpdated?(nil) + if let alertController = alertController as? AlertScreen { + alertController.dismissed = { _ in + alertDisplayUpdated?(nil) + } } present(alertController) alertDisplayUpdated?(alertController) diff --git a/submodules/PromptUI/BUILD b/submodules/PromptUI/BUILD index ae10576e..80d83f29 100644 --- a/submodules/PromptUI/BUILD +++ b/submodules/PromptUI/BUILD @@ -10,14 +10,17 @@ swift_library( "-warnings-as-errors", ], deps = [ - "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", - "//submodules/AsyncDisplayKit:AsyncDisplayKit", - "//submodules/Display:Display", - "//submodules/Postbox:Postbox", - "//submodules/TelegramCore:TelegramCore", - "//submodules/AccountContext:AccountContext", - "//submodules/TelegramPresentationData:TelegramPresentationData", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/Postbox", + "//submodules/TelegramCore", + "//submodules/AccountContext", + "//submodules/TelegramPresentationData", "//submodules/TelegramStringFormatting", + "//submodules/ComponentFlow", + "//submodules/TelegramUI/Components/AlertComponent", + "//submodules/TelegramUI/Components/AlertComponent/AlertMultilineInputFieldComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/PromptUI/Sources/PromptController.swift b/submodules/PromptUI/Sources/PromptController.swift index 2581f538..d359be83 100644 --- a/submodules/PromptUI/Sources/PromptController.swift +++ b/submodules/PromptUI/Sources/PromptController.swift @@ -8,6 +8,9 @@ import TelegramCore import TelegramPresentationData import AccountContext import TelegramStringFormatting +import ComponentFlow +import AlertComponent +import AlertMultilineInputFieldComponent private final class PromptInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegate { private var theme: PresentationTheme @@ -194,327 +197,95 @@ private final class PromptInputFieldNode: ASDisplayNode, ASEditableTextNodeDeleg } } -private final class PromptAlertContentNode: AlertContentNode { - private let strings: PresentationStrings - private let text: String - private let titleFont: PromptControllerTitleFont - private let subtitle: String? - - private let textNode: ASTextNode - private let subtitleNode: ASTextNode? - let inputFieldNode: PromptInputFieldNode - - private let actionNodesSeparator: ASDisplayNode - private let actionNodes: [TextAlertContentActionNode] - private let actionVerticalSeparators: [ASDisplayNode] - - private let disposable = MetaDisposable() - - private var validLayout: CGSize? - - private let hapticFeedback = HapticFeedback() - - var complete: (() -> Void)? { - didSet { - self.inputFieldNode.complete = self.complete - } - } - - override var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled - } - - init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction], text: String, titleFont: PromptControllerTitleFont, subtitle: String?, value: String?, placeholder: String?, characterLimit: Int, displayCharacterLimit: Bool) { - self.strings = strings - self.text = text - self.titleFont = titleFont - self.subtitle = subtitle - - self.textNode = ASTextNode() - self.textNode.maximumNumberOfLines = 2 - - if subtitle != nil { - let subtitleNode = ASTextNode() - subtitleNode.maximumNumberOfLines = 0 - self.subtitleNode = subtitleNode - } else { - self.subtitleNode = nil - } - - self.inputFieldNode = PromptInputFieldNode(theme: ptheme, placeholder: placeholder ?? "", characterLimit: characterLimit, displayCharacterLimit: displayCharacterLimit) - self.inputFieldNode.text = value ?? "" - - self.actionNodesSeparator = ASDisplayNode() - self.actionNodesSeparator.isLayerBacked = true - - self.actionNodes = actions.map { action -> TextAlertContentActionNode in - return TextAlertContentActionNode(theme: theme, action: action) - } - - var actionVerticalSeparators: [ASDisplayNode] = [] - if actions.count > 1 { - for _ in 0 ..< actions.count - 1 { - let separatorNode = ASDisplayNode() - separatorNode.isLayerBacked = true - actionVerticalSeparators.append(separatorNode) - } - } - self.actionVerticalSeparators = actionVerticalSeparators - - super.init() - - self.addSubnode(self.textNode) - if let subtitleNode = self.subtitleNode { - self.addSubnode(subtitleNode) - } - - self.addSubnode(self.inputFieldNode) - - self.addSubnode(self.actionNodesSeparator) - - for actionNode in self.actionNodes { - self.addSubnode(actionNode) - } - self.actionNodes.last?.actionEnabled = true - - for separatorNode in self.actionVerticalSeparators { - self.addSubnode(separatorNode) - } - - self.inputFieldNode.updateHeight = { [weak self] in - if let strongSelf = self { - if let _ = strongSelf.validLayout { - strongSelf.requestLayout?(.animated(duration: 0.15, curve: .spring)) - } - } - } - -// self.inputFieldNode.textChanged = { [weak self] text in -// if let strongSelf = self, let lastNode = strongSelf.actionNodes.last { -// lastNode.actionEnabled = !text.isEmpty -// } -// } - - self.updateTheme(theme) - } - - deinit { - self.disposable.dispose() - } - - var value: String { - return self.inputFieldNode.text - } - - override func updateTheme(_ theme: AlertControllerTheme) { - let titleFontValue: UIFont - switch self.titleFont { - case .regular: - titleFontValue = Font.regular(13.0) - case .bold: - titleFontValue = Font.semibold(17.0) - } - self.textNode.attributedText = NSAttributedString(string: self.text, font: titleFontValue, textColor: theme.primaryColor, paragraphAlignment: .center) - - if let subtitle = self.subtitle, let subtitleNode = self.subtitleNode { - subtitleNode.attributedText = NSAttributedString(string: subtitle, font: Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center) - } - - self.actionNodesSeparator.backgroundColor = theme.separatorColor - for actionNode in self.actionNodes { - actionNode.updateTheme(theme) - } - for separatorNode in self.actionVerticalSeparators { - separatorNode.backgroundColor = theme.separatorColor - } - - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) - } - } - - override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - var size = size - size.width = min(size.width, 270.0) - let measureSize = CGSize(width: size.width - 16.0 * 2.0, height: CGFloat.greatestFiniteMagnitude) - - let hadValidLayout = self.validLayout != nil - - self.validLayout = size - - var origin: CGPoint = CGPoint(x: 0.0, y: 20.0) - let spacing: CGFloat = 5.0 - - let titleSize = CGSize() -// transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize)) -// origin.y += titleSize.height + 4.0 - - let textSize = self.textNode.measure(measureSize) - transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize)) - origin.y += textSize.height + 6.0 + spacing - - var subtitleSize: CGSize? - if let subtitleNode { - let subtitleSizeValue = subtitleNode.measure(measureSize) - subtitleSize = subtitleSizeValue - transition.updateFrame(node: subtitleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - subtitleSizeValue.width) / 2.0), y: origin.y), size: subtitleSizeValue)) - origin.y += subtitleSizeValue.height + 6.0 + spacing - } - - let actionButtonHeight: CGFloat = 44.0 - var minActionsWidth: CGFloat = 0.0 - let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) - let actionTitleInsets: CGFloat = 8.0 - - var effectiveActionLayout = TextAlertContentActionLayout.horizontal - for actionNode in self.actionNodes { - let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) - if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 { - effectiveActionLayout = .vertical - } - switch effectiveActionLayout { - case .horizontal: - minActionsWidth += actionTitleSize.width + actionTitleInsets - case .vertical: - minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) - } - } - - let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 9.0, right: 18.0) - - var contentWidth = max(titleSize.width, minActionsWidth) - if let subtitleSize { - contentWidth = max(contentWidth, subtitleSize.width) - } - contentWidth = max(contentWidth, 234.0) - - var actionsHeight: CGFloat = 0.0 - switch effectiveActionLayout { - case .horizontal: - actionsHeight = actionButtonHeight - case .vertical: - actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) - } - - let resultWidth = contentWidth + insets.left + insets.right - - let inputFieldWidth = resultWidth - let inputFieldHeight = self.inputFieldNode.updateLayout(width: inputFieldWidth, transition: transition) - let inputHeight = inputFieldHeight - transition.updateFrame(node: self.inputFieldNode, frame: CGRect(x: 0.0, y: origin.y, width: resultWidth, height: inputFieldHeight)) - transition.updateAlpha(node: self.inputFieldNode, alpha: inputHeight > 0.0 ? 1.0 : 0.0) - - var resultSize = CGSize(width: resultWidth, height: titleSize.height + textSize.height + spacing + inputHeight + actionsHeight + insets.top + insets.bottom) - if let subtitleSize { - resultSize.height += subtitleSize.height + spacing - } - - transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - - var actionOffset: CGFloat = 0.0 - let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) - var separatorIndex = -1 - var nodeIndex = 0 - for actionNode in self.actionNodes { - if separatorIndex >= 0 { - let separatorNode = self.actionVerticalSeparators[separatorIndex] - switch effectiveActionLayout { - case .horizontal: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) - case .vertical: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - } - } - separatorIndex += 1 - - let currentActionWidth: CGFloat - switch effectiveActionLayout { - case .horizontal: - if nodeIndex == self.actionNodes.count - 1 { - currentActionWidth = resultSize.width - actionOffset - } else { - currentActionWidth = actionWidth - } - case .vertical: - currentActionWidth = resultSize.width - } - - let actionNodeFrame: CGRect - switch effectiveActionLayout { - case .horizontal: - actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += currentActionWidth - case .vertical: - actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += actionButtonHeight - } - - transition.updateFrame(node: actionNode, frame: actionNodeFrame) - - nodeIndex += 1 - } - - if !hadValidLayout { - self.inputFieldNode.activateInput() - } - - return resultSize - } - - func animateError() { - self.inputFieldNode.layer.addShakeAnimation() - self.hapticFeedback.error() - } -} - public enum PromptControllerTitleFont { case regular case bold } -public func promptController(sharedContext: SharedAccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, text: String, titleFont: PromptControllerTitleFont = .regular, subtitle: String? = nil, value: String?, placeholder: String? = nil, characterLimit: Int = 1000, displayCharacterLimit: Bool = false, apply: @escaping (String?) -> Void) -> AlertController { - let presentationData = updatedPresentationData?.initial ?? sharedContext.currentPresentationData.with { $0 } +public func promptController( + context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, + text: String, + titleFont: PromptControllerTitleFont = .regular, + subtitle: String? = nil, + value: String?, + placeholder: String? = nil, + characterLimit: Int = 1000, + displayCharacterLimit: Bool = false, + apply: @escaping (String?) -> Void, + dismissed: @escaping () -> Void = {} +) -> ViewController { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let strings = presentationData.strings - var dismissImpl: ((Bool) -> Void)? - var applyImpl: (() -> Void)? - - let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - dismissImpl?(true) - apply(nil) - }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Done, action: { - dismissImpl?(true) - applyImpl?() - })] - - let contentNode = PromptAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: actions, text: text, titleFont: titleFont, subtitle: subtitle, value: value, placeholder: placeholder, characterLimit: characterLimit, displayCharacterLimit: displayCharacterLimit) - contentNode.complete = { - dismissImpl?(true) - applyImpl?() + let inputState = AlertMultilineInputFieldComponent.ExternalState() + + var content: [AnyComponentWithIdentity] = [] + if subtitle == nil && titleFont == .regular { + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTextComponent(content: .plain(text)) + ) + )) + } else { + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: text) + ) + )) } - applyImpl = { [weak contentNode] in - guard let contentNode = contentNode else { - return - } - apply(contentNode.value) + if let subtitle { + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(subtitle)) + ) + )) + } + content.append(AnyComponentWithIdentity( + id: "input", + component: AnyComponent( + AlertMultilineInputFieldComponent( + context: context, + initialValue: value.flatMap { NSAttributedString(string: $0) }, + placeholder: placeholder ?? "", + characterLimit: characterLimit, + formatMenuAvailability: .none, + emptyLineHandling: .notAllowed, + isInitiallyFocused: true, + externalState: inputState + ) + ) + )) + + var effectiveUpdatedPresentationData: (PresentationData, Signal) + if let updatedPresentationData { + effectiveUpdatedPresentationData = updatedPresentationData + } else { + effectiveUpdatedPresentationData = (presentationData, context.sharedContext.presentationData) } - let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode) - let presentationDataDisposable = (updatedPresentationData?.signal ?? sharedContext.presentationData).start(next: { [weak controller, weak contentNode] presentationData in - controller?.theme = AlertControllerTheme(presentationData: presentationData) - contentNode?.inputFieldNode.updateTheme(presentationData.theme) - }) - controller.dismissed = { _ in - presentationDataDisposable.dispose() - } - dismissImpl = { [weak controller] animated in - contentNode.inputFieldNode.deactivateInput() - if animated { - controller?.dismissAnimated() - } else { - controller?.dismiss() + let alertController = AlertScreen( + configuration: AlertScreen.Configuration(allowInputInset: true), + content: content, + actions: [ + .init(title: strings.Common_Cancel, action: { + apply(nil) + }), + .init(title: strings.Common_Done, type: .default, action: { + apply(inputState.value.string) + }) + ], + updatedPresentationData: effectiveUpdatedPresentationData + ) + alertController.dismissed = { byOutsideTap in + if byOutsideTap { + dismissed() } } - return controller + return alertController } private final class AuthAlertContentNode: AlertContentNode { diff --git a/submodules/QrCodeUI/BUILD b/submodules/QrCodeUI/BUILD index 0308b422..e7eb7e22 100644 --- a/submodules/QrCodeUI/BUILD +++ b/submodules/QrCodeUI/BUILD @@ -31,6 +31,15 @@ swift_library( "//submodules/LegacyComponents:LegacyComponents", "//submodules/LegacyMediaPickerUI:LegacyMediaPickerUI", "//submodules/ImageContentAnalysis:ImageContentAnalysis", + "//submodules/ComponentFlow", + "//submodules/Components/SheetComponent", + "//submodules/TelegramUI/Components/ButtonComponent", + "//submodules/TelegramUI/Components/GlassBarButtonComponent", + "//submodules/Components/BundleIconComponent", + "//submodules/Components/BalancedTextComponent", + "//submodules/Components/MultilineTextComponent", + "//submodules/TelegramUI/Components/LottieComponent", + "//submodules/TelegramUI/Components/PlainButtonComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift b/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift index 3649da5e..ceab808e 100644 --- a/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift +++ b/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift @@ -77,7 +77,7 @@ public final class QrCodeScanScreen: ViewController { self.presentationData = context.sharedContext.currentPresentationData.with { $0 } - let navigationBarTheme = NavigationBarTheme(buttonColor: .white, disabledButtonColor: .white, primaryTextColor: .white, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) + let navigationBarTheme = NavigationBarTheme(overallDarkAppearance: self.presentationData.theme.overallDarkAppearance, buttonColor: .white, disabledButtonColor: .white, primaryTextColor: .white, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close))) diff --git a/submodules/QrCodeUI/Sources/QrCodeScreen.swift b/submodules/QrCodeUI/Sources/QrCodeScreen.swift index 94a293a0..9c975cc2 100644 --- a/submodules/QrCodeUI/Sources/QrCodeScreen.swift +++ b/submodules/QrCodeUI/Sources/QrCodeScreen.swift @@ -1,17 +1,23 @@ import Foundation import UIKit -import AsyncDisplayKit import Display +import ComponentFlow import SwiftSignalKit import TelegramCore import TelegramPresentationData -import AppBundle -import QrCode +import ViewControllerComponent +import SheetComponent +import BalancedTextComponent +import MultilineTextComponent +import BundleIconComponent +import ButtonComponent +import GlassBarButtonComponent +import PlainButtonComponent import AccountContext -import SolidRoundedButtonNode -import AnimatedStickerNode -import TelegramAnimatedStickerNode -import PresentationDataUtils +import Markdown +import TextFormat +import QrCode +import LottieComponent private func shareQrCode(context: AccountContext, link: String, ecl: String, view: UIView) { let _ = (qrCode(string: link, color: .black, backgroundColor: .white, icon: .custom(UIImage(bundleImageName: "Chat/Links/QrLogo")), ecl: ecl) @@ -24,7 +30,7 @@ private func shareQrCode(context: AccountContext, link: String, ecl: String, vie guard let image = image else { return } - + let activityController = UIActivityViewController(activityItems: [image], applicationActivities: nil) if let window = view.window { activityController.popoverPresentationController?.sourceView = window @@ -34,13 +40,336 @@ private func shareQrCode(context: AccountContext, link: String, ecl: String, vie }) } -public final class QrCodeScreen: ViewController { +private final class SheetContent: CombinedComponent { + typealias EnvironmentType = ViewControllerComponentContainer.Environment + + let context: AccountContext + let subject: QrCodeScreen.Subject + let dismiss: () -> Void + + init( + context: AccountContext, + subject: QrCodeScreen.Subject, + dismiss: @escaping () -> Void + ) { + self.context = context + self.subject = subject + self.dismiss = dismiss + } + + static func ==(lhs: SheetContent, rhs: SheetContent) -> Bool { + if lhs.context !== rhs.context { + return false + } + return true + } + + final class State: ComponentState { + private let idleTimerExtensionDisposable = MetaDisposable() + + private var initialBrightness: CGFloat? + private var brightnessArguments: (Double, Double, CGFloat, CGFloat)? + private var animator: ConstantDisplayLinkAnimator? + + init(context: AccountContext) { + super.init() + + self.idleTimerExtensionDisposable.set(context.sharedContext.applicationBindings.pushIdleTimerExtension()) + + self.animator = ConstantDisplayLinkAnimator(update: { [weak self] in + self?.updateBrightness() + }) + self.animator?.isPaused = true + + self.initialBrightness = UIScreen.main.brightness + self.brightnessArguments = (CACurrentMediaTime(), 0.3, UIScreen.main.brightness, 1.0) + self.updateBrightness() + } + + deinit { + self.idleTimerExtensionDisposable.dispose() + self.animator?.invalidate() + + if UIScreen.main.brightness > 0.99, let initialBrightness = self.initialBrightness { + self.brightnessArguments = (CACurrentMediaTime(), 0.3, UIScreen.main.brightness, initialBrightness) + self.updateBrightness() + } + } + + private func updateBrightness() { + if let (startTime, duration, initial, target) = self.brightnessArguments { + self.animator?.isPaused = false + + let t = CGFloat(max(0.0, min(1.0, (CACurrentMediaTime() - startTime) / duration))) + let value = initial + (target - initial) * t + + UIScreen.main.brightness = value + + if t >= 1.0 { + self.brightnessArguments = nil + self.animator?.isPaused = true + } + } else { + self.animator?.isPaused = true + } + } + } + + func makeState() -> State { + return State(context: self.context) + } + + static var body: Body { + let qrCode = Child(PlainButtonComponent.self) + let closeButton = Child(GlassBarButtonComponent.self) + let title = Child(Text.self) + let text = Child(BalancedTextComponent.self) + + let button = Child(ButtonComponent.self) + + return { context in + let environment = context.environment[EnvironmentType.self] + let component = context.component + let controller = environment.controller() + + let theme = environment.theme + let strings = environment.strings + + let link = component.subject.link + let ecl = component.subject.ecl + + let titleString: String + let textString: String + switch component.subject { + case let .invite(_, type): + titleString = strings.InviteLink_QRCode_Title + switch type { + case .group: + textString = strings.InviteLink_QRCode_Info + case .channel: + textString = strings.InviteLink_QRCode_InfoChannel + case .groupCall: + textString = strings.InviteLink_QRCode_InfoGroupCall + } + case .chatFolder: + titleString = strings.InviteLink_QRCodeFolder_Title + textString = strings.InviteLink_QRCodeFolder_Text + default: + titleString = "" + textString = "" + } + + var contentSize = CGSize(width: context.availableSize.width, height: 36.0) + + let closeButton = closeButton.update( + component: GlassBarButtonComponent( + size: CGSize(width: 40.0, height: 40.0), + backgroundColor: theme.rootController.navigationBar.glassBarButtonBackgroundColor, + isDark: theme.overallDarkAppearance, + state: .generic, + component: AnyComponentWithIdentity(id: "close", component: AnyComponent( + BundleIconComponent( + name: "Navigation/Close", + tintColor: theme.rootController.navigationBar.glassBarButtonForegroundColor + ) + )), + action: { _ in + component.dismiss() + } + ), + availableSize: CGSize(width: 40.0, height: 40.0), + transition: .immediate + ) + context.add(closeButton + .position(CGPoint(x: 16.0 + closeButton.size.width / 2.0, y: 16.0 + closeButton.size.height / 2.0)) + ) + + let constrainedTitleWidth = context.availableSize.width - 16.0 * 2.0 + + let title = title.update( + component: Text(text: titleString, font: Font.semibold(17.0), color: theme.list.itemPrimaryTextColor), + availableSize: CGSize(width: constrainedTitleWidth, height: context.availableSize.height), + transition: .immediate + ) + context.add(title + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height)) + ) + contentSize.height += title.size.height + contentSize.height += 13.0 + + let qrCode = qrCode.update( + component: PlainButtonComponent( + content: AnyComponent(QrCodeComponent(context: component.context, link: link, ecl: ecl)), + action: { [weak controller] in + if let view = controller?.view { + shareQrCode(context: component.context, link: link, ecl: ecl, view: view) + } + }, + animateScale: false + ), + availableSize: CGSize(width: 260.0, height: 260.0), + transition: .immediate + ) + context.add(qrCode + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + qrCode.size.height / 2.0)) + ) + contentSize.height += qrCode.size.height + contentSize.height += 17.0 + + let textFont = Font.regular(15.0) + let boldTextFont = Font.semibold(15.0) + let textColor = theme.actionSheet.primaryTextColor + let linkColor = theme.actionSheet.controlAccentColor + let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in + return (TelegramTextAttributes.URL, contents) + }) + + let text = text.update( + component: BalancedTextComponent( + text: .markdown( + text: textString, + attributes: markdownAttributes + ), + horizontalAlignment: .center, + maximumNumberOfLines: 0, + lineSpacing: 0.2 + ), + availableSize: CGSize(width: constrainedTitleWidth, height: context.availableSize.height), + transition: .immediate + ) + context.add(text + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + text.size.height / 2.0)) + ) + contentSize.height += text.size.height + contentSize.height += 23.0 + + let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: environment.safeInsets.bottom, innerDiameter: 52.0, sideInset: 30.0) + let button = button.update( + component: ButtonComponent( + background: ButtonComponent.Background( + style: .glass, + color: theme.list.itemCheckColors.fillColor, + foreground: theme.list.itemCheckColors.foregroundColor, + pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9), + cornerRadius: 10.0, + ), + content: AnyComponentWithIdentity( + id: AnyHashable(0), + component: AnyComponent(MultilineTextComponent(text: .plain(NSMutableAttributedString(string: strings.InviteLink_QRCode_Share, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)))) + ), + isEnabled: true, + displaysProgress: false, + action: { [weak controller] in + if let view = controller?.view { + shareQrCode(context: component.context, link: link, ecl: ecl, view: view) + } + } + ), + availableSize: CGSize(width: context.availableSize.width - buttonInsets.left - buttonInsets.right, height: 52.0), + transition: .immediate + ) + context.add(button + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + button.size.height / 2.0)) + ) + contentSize.height += button.size.height + contentSize.height += buttonInsets.bottom + + return contentSize + } + } +} + +private final class QrCodeSheetComponent: CombinedComponent { + typealias EnvironmentType = ViewControllerComponentContainer.Environment + + private let context: AccountContext + private let subject: QrCodeScreen.Subject + + init( + context: AccountContext, + subject: QrCodeScreen.Subject + ) { + self.context = context + self.subject = subject + } + + static func ==(lhs: QrCodeSheetComponent, rhs: QrCodeSheetComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + return true + } + + static var body: Body { + let sheet = Child(SheetComponent<(EnvironmentType)>.self) + let animateOut = StoredActionSlot(Action.self) + + return { context in + let environment = context.environment[EnvironmentType.self] + + let controller = environment.controller + + let sheet = sheet.update( + component: SheetComponent( + content: AnyComponent(SheetContent( + context: context.component.context, + subject: context.component.subject, + dismiss: { + animateOut.invoke(Action { _ in + if let controller = controller() as? QrCodeScreen { + controller.dismiss(completion: nil) + } + }) + } + )), + style: .glass, + backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor), + followContentSizeChanges: true, + clipsContent: true, + animateOut: animateOut + ), + environment: { + environment + SheetComponentEnvironment( + isDisplaying: environment.value.isVisible, + isCentered: environment.metrics.widthClass == .regular, + hasInputHeight: !environment.inputHeight.isZero, + regularMetricsSize: CGSize(width: 430.0, height: 900.0), + dismiss: { animated in + if animated { + animateOut.invoke(Action { _ in + if let controller = controller() as? QrCodeScreen { + controller.dismiss(completion: nil) + } + }) + } else { + if let controller = controller() as? QrCodeScreen { + controller.dismiss(completion: nil) + } + } + } + ) + }, + availableSize: context.availableSize, + transition: context.transition + ) + + context.add(sheet + .position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0)) + ) + + return context.availableSize + } + } +} + +public final class QrCodeScreen: ViewControllerComponentContainer { public enum SubjectType { case group case channel case groupCall } - + public enum Subject { case peer(peer: EnginePeer) case invite(invite: ExportedInvitation, type: SubjectType) @@ -48,471 +377,181 @@ public final class QrCodeScreen: ViewController { var link: String { switch self { - case let .peer(peer): - return "https://t.me/\(peer.addressName ?? "")" - case let .invite(invite, _): - return invite.link ?? "" - case let .chatFolder(slug): - if slug.hasPrefix("https://") { - return slug - } else { - return "https://t.me/addlist/\(slug)" - } + case let .peer(peer): + return "https://t.me/\(peer.addressName ?? "")" + case let .invite(invite, _): + return invite.link ?? "" + case let .chatFolder(slug): + if slug.hasPrefix("https://") { + return slug + } else { + return "https://t.me/addlist/\(slug)" + } } } var ecl: String { switch self { - case .peer: - return "Q" - case .invite: - return "Q" - case .chatFolder: - return "Q" + case .peer: + return "Q" + case .invite: + return "Q" + case .chatFolder: + return "Q" } } } - private var controllerNode: Node { - return self.displayNode as! Node - } - - private var animatedIn = false - private let context: AccountContext - private let subject: QrCodeScreen.Subject - private var presentationData: PresentationData - private var presentationDataDisposable: Disposable? - - private var initialBrightness: CGFloat? - private var brightnessArguments: (Double, Double, CGFloat, CGFloat)? - - private var animator: ConstantDisplayLinkAnimator? - - private let idleTimerExtensionDisposable = MetaDisposable() - - public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, subject: QrCodeScreen.Subject) { + public init( + context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, + subject: QrCodeScreen.Subject + ) { self.context = context - self.subject = subject - self.presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } + super.init( + context: context, + component: QrCodeSheetComponent( + context: context, + subject: subject + ), + navigationBarAppearance: .none, + statusBarStyle: .ignore, + theme: .default // + ) - super.init(navigationBarPresentationData: nil) - - self.statusBar.statusBarStyle = .Ignore - - self.blocksBackgroundWhenInOverlay = true - - self.presentationDataDisposable = ((updatedPresentationData?.signal ?? context.sharedContext.presentationData) - |> deliverOnMainQueue).start(next: { [weak self] presentationData in - if let strongSelf = self { - strongSelf.presentationData = presentationData - strongSelf.controllerNode.updatePresentationData(presentationData) - } - }) - - self.idleTimerExtensionDisposable.set(self.context.sharedContext.applicationBindings.pushIdleTimerExtension()) - - self.statusBar.statusBarStyle = .Ignore - - self.animator = ConstantDisplayLinkAnimator(update: { [weak self] in - self?.updateBrightness() - }) - self.animator?.isPaused = true + self.navigationPresentation = .flatModal } - - required init(coder aDecoder: NSCoder) { + + required public init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - deinit { - self.presentationDataDisposable?.dispose() - self.idleTimerExtensionDisposable.dispose() - self.animator?.invalidate() - } - - override public func loadDisplayNode() { - self.displayNode = Node(context: self.context, presentationData: self.presentationData, subject: self.subject) - self.controllerNode.dismiss = { [weak self] in - self?.presentingViewController?.dismiss(animated: false, completion: nil) - } - self.controllerNode.cancel = { [weak self] in - self?.dismiss() - } - } - - override public func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - - if !self.animatedIn { - self.animatedIn = true - self.controllerNode.animateIn() - - self.initialBrightness = UIScreen.main.brightness - self.brightnessArguments = (CACurrentMediaTime(), 0.3, UIScreen.main.brightness, 1.0) - self.updateBrightness() - } - } - - private func updateBrightness() { - if let (startTime, duration, initial, target) = self.brightnessArguments { - self.animator?.isPaused = false - - let t = CGFloat(max(0.0, min(1.0, (CACurrentMediaTime() - startTime) / duration))) - let value = initial + (target - initial) * t - - UIScreen.main.brightness = value - - if t >= 1.0 { - self.brightnessArguments = nil - self.animator?.isPaused = true - } - } else { - self.animator?.isPaused = true - } - } - - override public func dismiss(completion: (() -> Void)? = nil) { - if UIScreen.main.brightness > 0.99, let initialBrightness = self.initialBrightness { - self.brightnessArguments = (CACurrentMediaTime(), 0.3, UIScreen.main.brightness, initialBrightness) - self.updateBrightness() - } - - self.controllerNode.animateOut(completion: completion) - } - - override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { - super.containerLayoutUpdated(layout, transition: transition) - - self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition) - } - - class Node: ViewControllerTracingNode, ASScrollViewDelegate { - private let context: AccountContext - private let subject: QrCodeScreen.Subject - private var presentationData: PresentationData - - private let dimNode: ASDisplayNode - private let wrappingScrollNode: ASScrollNode - private let contentContainerNode: ASDisplayNode - private let backgroundNode: ASDisplayNode - private let contentBackgroundNode: ASDisplayNode - private let titleNode: ASTextNode - private let cancelButton: HighlightableButtonNode - - private let textNode: ImmediateTextNode - private let qrButtonNode: HighlightTrackingButtonNode - private let qrImageNode: TransformImageNode - private let qrIconNode: AnimatedStickerNode - private var qrCodeSize: Int? - private let buttonNode: SolidRoundedButtonNode - - private var containerLayout: (ContainerViewLayout, CGFloat)? - - var completion: ((Int32) -> Void)? - var dismiss: (() -> Void)? - var cancel: (() -> Void)? - - init(context: AccountContext, presentationData: PresentationData, subject: QrCodeScreen.Subject) { - self.context = context - self.subject = subject - self.presentationData = presentationData - - self.wrappingScrollNode = ASScrollNode() - self.wrappingScrollNode.view.alwaysBounceVertical = true - self.wrappingScrollNode.view.delaysContentTouches = false - self.wrappingScrollNode.view.canCancelContentTouches = true - - self.dimNode = ASDisplayNode() - self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5) - - self.contentContainerNode = ASDisplayNode() - self.contentContainerNode.isOpaque = false - - self.backgroundNode = ASDisplayNode() - self.backgroundNode.clipsToBounds = true - self.backgroundNode.cornerRadius = 16.0 - - let backgroundColor = self.presentationData.theme.actionSheet.opaqueItemBackgroundColor - let textColor = self.presentationData.theme.actionSheet.primaryTextColor - let secondaryTextColor = self.presentationData.theme.actionSheet.secondaryTextColor - let accentColor = self.presentationData.theme.actionSheet.controlAccentColor - - self.contentBackgroundNode = ASDisplayNode() - self.contentBackgroundNode.backgroundColor = backgroundColor - - let title: String - let text: String - switch subject { - case let .invite(_, type): - title = self.presentationData.strings.InviteLink_QRCode_Title - switch type { - case .group: - text = self.presentationData.strings.InviteLink_QRCode_Info - case .channel: - text = self.presentationData.strings.InviteLink_QRCode_InfoChannel - case .groupCall: - text = self.presentationData.strings.InviteLink_QRCode_InfoGroupCall - } - case .chatFolder: - title = self.presentationData.strings.InviteLink_QRCodeFolder_Title - text = self.presentationData.strings.InviteLink_QRCodeFolder_Text - default: - title = "" - text = "" - } - - self.titleNode = ASTextNode() - self.titleNode.attributedText = NSAttributedString(string: title, font: Font.bold(17.0), textColor: textColor) - - self.cancelButton = HighlightableButtonNode() - self.cancelButton.setTitle(self.presentationData.strings.Common_Done, with: Font.bold(17.0), with: accentColor, for: .normal) - - self.buttonNode = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(theme: self.presentationData.theme), height: 52.0, cornerRadius: 11.0, isShimmering: false) - - self.textNode = ImmediateTextNode() - self.textNode.maximumNumberOfLines = 3 - self.textNode.textAlignment = .center - - self.qrButtonNode = HighlightTrackingButtonNode() - self.qrImageNode = TransformImageNode() - self.qrImageNode.clipsToBounds = true - self.qrImageNode.cornerRadius = 16.0 - - self.qrIconNode = DefaultAnimatedStickerNodeImpl() - self.qrIconNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "PlaneLogo"), width: 240, height: 240, playbackMode: .loop, mode: .direct(cachePathPrefix: nil)) - self.qrIconNode.visibility = true - - super.init() - - self.backgroundColor = nil - self.isOpaque = false - - self.dimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:)))) - self.addSubnode(self.dimNode) - - self.wrappingScrollNode.view.delegate = self.wrappedScrollViewDelegate - self.addSubnode(self.wrappingScrollNode) - - self.wrappingScrollNode.addSubnode(self.backgroundNode) - self.wrappingScrollNode.addSubnode(self.contentContainerNode) - - self.backgroundNode.addSubnode(self.contentBackgroundNode) - self.contentContainerNode.addSubnode(self.titleNode) - self.contentContainerNode.addSubnode(self.cancelButton) - self.contentContainerNode.addSubnode(self.buttonNode) - - self.contentContainerNode.addSubnode(self.textNode) - self.contentContainerNode.addSubnode(self.qrImageNode) - self.contentContainerNode.addSubnode(self.qrIconNode) - self.contentContainerNode.addSubnode(self.qrButtonNode) - - self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: secondaryTextColor) - self.buttonNode.title = self.presentationData.strings.InviteLink_QRCode_Share - - self.cancelButton.addTarget(self, action: #selector(self.cancelButtonPressed), forControlEvents: .touchUpInside) - self.buttonNode.pressed = { [weak self] in - if let strongSelf = self{ - shareQrCode(context: strongSelf.context, link: subject.link, ecl: subject.ecl, view: strongSelf.view) - } - } - - self.qrImageNode.setSignal(qrCode(string: subject.link, color: .black, backgroundColor: .white, icon: .cutout, ecl: subject.ecl) |> beforeNext { [weak self] size, _ in - guard let strongSelf = self else { - return - } - strongSelf.qrCodeSize = size - if let (layout, navigationHeight) = strongSelf.containerLayout { - strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: .immediate) - } - } |> map { $0.1 }, attemptSynchronously: true) - - self.qrButtonNode.addTarget(self, action: #selector(self.qrPressed), forControlEvents: .touchUpInside) - self.qrButtonNode.highligthedChanged = { [weak self] highlighted in - guard let strongSelf = self else { - return - } - if highlighted { - strongSelf.qrImageNode.alpha = 0.4 - strongSelf.qrIconNode.alpha = 0.4 - } else { - strongSelf.qrImageNode.layer.animateAlpha(from: strongSelf.qrImageNode.alpha, to: 1.0, duration: 0.2) - strongSelf.qrImageNode.alpha = 1.0 - strongSelf.qrIconNode.layer.animateAlpha(from: strongSelf.qrIconNode.alpha, to: 1.0, duration: 0.2) - strongSelf.qrIconNode.alpha = 1.0 - } - } - } - - @objc private func qrPressed() { - self.buttonNode.pressed?() - } - - func updatePresentationData(_ presentationData: PresentationData) { - let previousTheme = self.presentationData.theme - self.presentationData = presentationData - - self.contentBackgroundNode.backgroundColor = self.presentationData.theme.actionSheet.opaqueItemBackgroundColor - self.titleNode.attributedText = NSAttributedString(string: self.titleNode.attributedText?.string ?? "", font: Font.bold(17.0), textColor: self.presentationData.theme.actionSheet.primaryTextColor) - self.textNode.attributedText = NSAttributedString(string: self.textNode.attributedText?.string ?? "", font: Font.regular(13.0), textColor: self.presentationData.theme.actionSheet.secondaryTextColor) - - if previousTheme !== presentationData.theme, let (layout, navigationBarHeight) = self.containerLayout { - self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate) - } - - self.cancelButton.setTitle(self.presentationData.strings.Common_Done, with: Font.bold(17.0), with: self.presentationData.theme.actionSheet.controlAccentColor, for: .normal) - self.buttonNode.updateTheme(SolidRoundedButtonTheme(theme: self.presentationData.theme)) - } - - override func didLoad() { - super.didLoad() - - if #available(iOSApplicationExtension 11.0, iOS 11.0, *) { - self.wrappingScrollNode.view.contentInsetAdjustmentBehavior = .never - } - } - - @objc func cancelButtonPressed() { - self.cancel?() - } - - @objc func dimTapGesture(_ recognizer: UITapGestureRecognizer) { - if case .ended = recognizer.state { - self.cancelButtonPressed() - } - } - - func animateIn() { - self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4) - - let offset = self.bounds.size.height - self.contentBackgroundNode.frame.minY - - let dimPosition = self.dimNode.layer.position - self.dimNode.layer.animatePosition(from: CGPoint(x: dimPosition.x, y: dimPosition.y - offset), to: dimPosition, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) - self.layer.animateBoundsOriginYAdditive(from: -offset, to: 0.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring) - } - - func animateOut(completion: (() -> Void)? = nil) { - var dimCompleted = false - var offsetCompleted = false - - let internalCompletion: () -> Void = { [weak self] in - if let strongSelf = self, dimCompleted && offsetCompleted { - strongSelf.dismiss?() - } - completion?() - } - - self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in - dimCompleted = true - internalCompletion() - }) - - let offset = self.bounds.size.height - self.contentBackgroundNode.frame.minY - let dimPosition = self.dimNode.layer.position - self.dimNode.layer.animatePosition(from: dimPosition, to: CGPoint(x: dimPosition.x, y: dimPosition.y - offset), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) - self.layer.animateBoundsOriginYAdditive(from: 0.0, to: -offset, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in - offsetCompleted = true - internalCompletion() - }) - } - - override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - if self.bounds.contains(point) { - if !self.contentBackgroundNode.bounds.contains(self.convert(point, to: self.contentBackgroundNode)) { - return self.dimNode.view - } - } - return super.hitTest(point, with: event) - } - - func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { - let contentOffset = scrollView.contentOffset - let additionalTopHeight = max(0.0, -contentOffset.y) - - if additionalTopHeight >= 30.0 { - self.cancelButtonPressed() - } - } - - func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { - self.containerLayout = (layout, navigationBarHeight) - - var insets = layout.insets(options: [.statusBar, .input]) - insets.top = 32.0 - - let makeImageLayout = self.qrImageNode.asyncLayout() - let imageSide: CGFloat = 240.0 - let imageSize = CGSize(width: imageSide, height: imageSide) - let imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(), emptyColor: nil)) - let _ = imageApply() - - let width = horizontalContainerFillingSizeForLayout(layout: layout, sideInset: 0.0) - - let imageFrame = CGRect(origin: CGPoint(x: floor((width - imageSize.width) / 2.0), y: insets.top + 16.0), size: imageSize) - transition.updateFrame(node: self.qrImageNode, frame: imageFrame) - transition.updateFrame(node: self.qrButtonNode, frame: imageFrame) - - if let qrCodeSize = self.qrCodeSize { - let (_, cutoutFrame, _) = qrCodeCutout(size: qrCodeSize, dimensions: imageSize, scale: nil) - self.qrIconNode.updateLayout(size: cutoutFrame.size) - transition.updateBounds(node: self.qrIconNode, bounds: CGRect(origin: CGPoint(), size: cutoutFrame.size)) - transition.updatePosition(node: self.qrIconNode, position: imageFrame.center.offsetBy(dx: 0.0, dy: -1.0)) - } - - let inset: CGFloat = 32.0 - var textSize = self.textNode.updateLayout(CGSize(width: width - inset * 3.0, height: CGFloat.greatestFiniteMagnitude)) - let textFrame = CGRect(origin: CGPoint(x: floor((width - textSize.width) / 2.0), y: imageFrame.maxY + 20.0), size: textSize) - transition.updateFrame(node: self.textNode, frame: textFrame) - - var textSpacing: CGFloat = 111.0 - if case .compact = layout.metrics.widthClass, layout.size.width > layout.size.height { - textSize = CGSize() - self.textNode.isHidden = true - textSpacing = 52.0 - } else { - self.textNode.isHidden = false - } - - let buttonSideInset: CGFloat = 16.0 - let bottomInset = insets.bottom + 10.0 - let buttonWidth = layout.size.width - buttonSideInset * 2.0 - let buttonHeight: CGFloat = 50.0 - - let buttonFrame = CGRect(origin: CGPoint(x: floor((width - buttonWidth) / 2.0), y: layout.size.height - bottomInset - buttonHeight), size: CGSize(width: buttonWidth, height: buttonHeight)) - transition.updateFrame(node: self.buttonNode, frame: buttonFrame) - let _ = self.buttonNode.updateLayout(width: buttonFrame.width, transition: transition) - - let titleHeight: CGFloat = 54.0 - let contentHeight = titleHeight + textSize.height + imageSize.height + bottomInset + textSpacing - - let sideInset = floor((layout.size.width - width) / 2.0) - let contentContainerFrame = CGRect(origin: CGPoint(x: sideInset, y: layout.size.height - contentHeight), size: CGSize(width: width, height: contentHeight)) - let contentFrame = contentContainerFrame - - var backgroundFrame = CGRect(origin: CGPoint(x: contentFrame.minX, y: contentFrame.minY), size: CGSize(width: contentFrame.width, height: contentFrame.height + 2000.0)) - if backgroundFrame.minY < contentFrame.minY { - backgroundFrame.origin.y = contentFrame.minY - } - transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame) - transition.updateFrame(node: self.contentBackgroundNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size)) - transition.updateFrame(node: self.wrappingScrollNode, frame: CGRect(origin: CGPoint(), size: layout.size)) - transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: layout.size)) - - let titleSize = self.titleNode.measure(CGSize(width: width, height: titleHeight)) - let titleFrame = CGRect(origin: CGPoint(x: floor((contentFrame.width - titleSize.width) / 2.0), y: 16.0), size: titleSize) - transition.updateFrame(node: self.titleNode, frame: titleFrame) - - let cancelSize = self.cancelButton.measure(CGSize(width: width, height: titleHeight)) - let cancelFrame = CGRect(origin: CGPoint(x: width - cancelSize.width - 16.0, y: 16.0), size: cancelSize) - transition.updateFrame(node: self.cancelButton, frame: cancelFrame) - - let buttonInset: CGFloat = 16.0 - let doneButtonHeight = self.buttonNode.updateLayout(width: contentFrame.width - buttonInset * 2.0, transition: transition) - transition.updateFrame(node: self.buttonNode, frame: CGRect(x: buttonInset, y: contentHeight - doneButtonHeight - insets.bottom - 16.0, width: contentFrame.width, height: doneButtonHeight)) - - transition.updateFrame(node: self.contentContainerNode, frame: contentContainerFrame) + public func dismissAnimated() { + if let view = self.node.hostView.findTaggedView(tag: SheetComponent.View.Tag()) as? SheetComponent.View { + view.dismissAnimated() } } } + +private final class QrCodeComponent: Component { + let context: AccountContext + let link: String + let ecl: String + + init( + context: AccountContext, + link: String, + ecl: String + ) { + self.context = context + self.link = link + self.ecl = ecl + } + + static func ==(lhs: QrCodeComponent, rhs: QrCodeComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.link != rhs.link { + return false + } + if lhs.ecl != rhs.ecl { + return false + } + return true + } + + final class View: UIView { + private var component: QrCodeComponent? + private var state: EmptyComponentState? + + private let imageNode: TransformImageNode + private let icon = ComponentView() + + private var qrCodeSize: Int? + + private var isUpdating = false + + override init(frame: CGRect) { + self.imageNode = TransformImageNode() + + super.init(frame: frame) + + self.backgroundColor = UIColor.white + self.clipsToBounds = true + self.layer.cornerRadius = 24.0 + self.layer.allowsGroupOpacity = true + + self.addSubview(self.imageNode.view) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(component: QrCodeComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.isUpdating = true + defer { + self.isUpdating = false + } + let previousComponent = self.component + self.component = component + self.state = state + + if previousComponent?.link != component.link { + self.imageNode.setSignal(qrCode(string: component.link, color: .black, backgroundColor: .white, icon: .cutout, ecl: component.ecl) |> beforeNext { [weak self] size, _ in + guard let self else { + return + } + self.qrCodeSize = size + if !self.isUpdating { + self.state?.updated() + } + } |> map { $0.1 }, attemptSynchronously: true) + } + + let size = CGSize(width: 256.0, height: 256.0) + let imageSize = CGSize(width: 240.0, height: 240.0) + + let makeImageLayout = self.imageNode.asyncLayout() + let imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(), emptyColor: nil)) + let _ = imageApply() + let imageFrame = CGRect(origin: CGPoint(x: (size.width - imageSize.width) / 2.0, y: (size.height - imageSize.height) / 2.0), size: imageSize) + self.imageNode.frame = imageFrame + + if let qrCodeSize = self.qrCodeSize { + let (_, cutoutFrame, _) = qrCodeCutout(size: qrCodeSize, dimensions: imageSize, scale: nil) + + let _ = self.icon.update( + transition: .immediate, + component: AnyComponent(LottieComponent( + content: LottieComponent.AppBundleContent(name: "PlaneLogo"), + loop: true + )), + environment: {}, + containerSize: cutoutFrame.size + ) + if let iconView = self.icon.view { + if iconView.superview == nil { + self.addSubview(iconView) + } + iconView.bounds = CGRect(origin: CGPoint(), size: cutoutFrame.size) + iconView.center = imageFrame.center.offsetBy(dx: 0.0, dy: -1.0) + } + } + + return size + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/SearchBarNode/BUILD b/submodules/SearchBarNode/BUILD index 1842fa27..343809aa 100644 --- a/submodules/SearchBarNode/BUILD +++ b/submodules/SearchBarNode/BUILD @@ -21,6 +21,8 @@ swift_library( "//submodules/AvatarNode:AvatarNode", "//submodules/AccountContext:AccountContext", "//submodules/TelegramUI/Components/EmojiStatusComponent", + "//submodules/TelegramUI/Components/GlassBackgroundComponent", + "//submodules/Components/ComponentDisplayAdapters", ], visibility = [ "//visibility:public", diff --git a/submodules/SearchBarNode/Sources/SearchBarNode.swift b/submodules/SearchBarNode/Sources/SearchBarNode.swift index 131323d3..849b0919 100644 --- a/submodules/SearchBarNode/Sources/SearchBarNode.swift +++ b/submodules/SearchBarNode/Sources/SearchBarNode.swift @@ -11,6 +11,7 @@ import AvatarNode import AccountContext import ComponentFlow import EmojiStatusComponent +import ComponentDisplayAdapters private func generateLoupeIcon(color: UIColor) -> UIImage? { return generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Loupe"), color: color) @@ -369,6 +370,7 @@ private class SearchBarTextField: UITextField, UIScrollViewDelegate { } var theme: SearchBarNodeTheme + let style: SearchBarStyle fileprivate func layoutTokens(transition: ContainedViewLayoutTransition = .immediate) { var hasSelected = false @@ -508,6 +510,12 @@ private class SearchBarTextField: UITextField, UIScrollViewDelegate { } fileprivate var tokensWidth: CGFloat = 0.0 + var tokensInsetWidth: CGFloat { + if self.tokensWidth == 0.0 { + return 0.0 + } + return self.tokensWidth + 8.0 + } private let measurePrefixLabel: ImmediateTextNode let prefixLabel: ImmediateTextNode @@ -519,8 +527,9 @@ private class SearchBarTextField: UITextField, UIScrollViewDelegate { } } - init(theme: SearchBarNodeTheme) { + init(theme: SearchBarNodeTheme, style: SearchBarStyle) { self.theme = theme + self.style = style self.placeholderLabel = ImmediateTextNode() self.placeholderLabel.isUserInteractionEnabled = false @@ -547,7 +556,10 @@ private class SearchBarTextField: UITextField, UIScrollViewDelegate { super.init(frame: CGRect()) - self.addSubnode(self.placeholderLabel) + if case .glass = style { + } else { + self.addSubnode(self.placeholderLabel) + } self.addSubnode(self.prefixLabel) self.addSubnode(self.clippingNode) self.clippingNode.addSubnode(self.tokenContainerNode) @@ -682,11 +694,19 @@ private class SearchBarTextField: UITextField, UIScrollViewDelegate { let textRect = self.textRect(forBounds: bounds) let labelSize = self.placeholderLabel.updateLayout(textRect.size) - self.placeholderLabel.frame = CGRect(origin: CGPoint(x: textRect.minX + placeholderXOffset, y: textRect.minY + textOffset + placeholderYOffset), size: labelSize) + + switch self.style { + case .glass, .inlineNavigation: + placeholderYOffset += 0.0 + case .legacy, .modern: + break + } + + self.placeholderLabel.frame = CGRect(origin: CGPoint(x: textRect.minX + placeholderXOffset, y: floorToScreenPixels(bounds.height - labelSize.height) * 0.5), size: labelSize) let prefixSize = self.prefixLabel.updateLayout(CGSize(width: floor(bounds.size.width * 0.7), height: bounds.size.height)) let prefixBounds = bounds.insetBy(dx: 4.0, dy: 4.0) - self.prefixLabel.frame = CGRect(origin: CGPoint(x: prefixBounds.minX, y: prefixBounds.minY + textOffset + placeholderYOffset), size: prefixSize) + self.prefixLabel.frame = CGRect(origin: CGPoint(x: prefixBounds.minX, y: floorToScreenPixels(bounds.height - prefixSize.height) * 0.5), size: prefixSize) } override func deleteBackward() { @@ -813,40 +833,52 @@ public final class SearchBarNodeTheme: Equatable { public enum SearchBarStyle { case modern case legacy + case inlineNavigation + case glass var font: UIFont { switch self { - case .modern: - return Font.regular(17.0) - case .legacy: - return Font.regular(14.0) + case .modern, .inlineNavigation, .glass: + return Font.regular(17.0) + case .legacy: + return Font.regular(14.0) } } var cornerDiameter: CGFloat { switch self { - case .modern: - return 21.0 - case .legacy: - return 14.0 + case .modern, .inlineNavigation: + return 21.0 + case .glass: + return 22.0 + case .legacy: + return 14.0 } } var height: CGFloat { switch self { - case .modern: - return 36.0 - case .legacy: - return 28.0 + case .inlineNavigation: + return 48.0 + case .glass: + return 44.0 + case .modern: + return 36.0 + case .legacy: + return 28.0 } } var padding: CGFloat { switch self { - case .modern: - return 10.0 - case .legacy: - return 8.0 + case .inlineNavigation: + return 0.0 + case .glass: + return 20.0 + case .modern: + return 10.0 + case .legacy: + return 8.0 } } } @@ -868,6 +900,9 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { public var tokensUpdated: (([SearchBarToken]) -> Void)? + private let inlineSearchPlaceholder: SearchBarPlaceholderNode + private var inlineSearchPlaceholderContentsView: SearchBarPlaceholderContentView? + private let backgroundNode: NavigationBackgroundNode private let separatorNode: ASDisplayNode private let textBackgroundNode: ASDisplayNode @@ -877,6 +912,8 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { private let clearButton: HighlightableButtonNode private let cancelButton: HighlightableButtonNode + private var takenSearchPlaceholderContentView: SearchBarPlaceholderContentView? + public var placeholderString: NSAttributedString? { get { return self.textField.placeholderString @@ -941,6 +978,12 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { activityIndicator.removeFromSupernode() } self.iconNode.isHidden = self.activity + if let takenSearchPlaceholderContentView = self.takenSearchPlaceholderContentView { + takenSearchPlaceholderContentView.updateSearchIconVisibility(isVisible: !self.activity) + } + if let inlineSearchPlaceholderContentsView = self.inlineSearchPlaceholderContentsView { + inlineSearchPlaceholderContentsView.updateSearchIconVisibility(isVisible: !self.activity) + } } } } @@ -965,17 +1008,24 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { private var validLayout: (CGSize, CGFloat, CGFloat)? - private let fieldStyle: SearchBarStyle + public let fieldStyle: SearchBarStyle private let forceSeparator: Bool private var theme: SearchBarNodeTheme? + private var presentationTheme: PresentationTheme private var strings: PresentationStrings? private let cancelText: String? - public init(theme: SearchBarNodeTheme, strings: PresentationStrings, fieldStyle: SearchBarStyle = .legacy, icon: Icon = .loupe, forceSeparator: Bool = false, displayBackground: Bool = true, cancelText: String? = nil) { + private var isAnimatingOut: Bool = false + + public init(theme: SearchBarNodeTheme, presentationTheme: PresentationTheme, strings: PresentationStrings, fieldStyle: SearchBarStyle = .legacy, icon: Icon = .loupe, forceSeparator: Bool = false, displayBackground: Bool = true, cancelText: String? = nil) { + self.presentationTheme = presentationTheme + self.fieldStyle = fieldStyle self.forceSeparator = forceSeparator self.cancelText = cancelText self.icon = icon + + self.inlineSearchPlaceholder = SearchBarPlaceholderNode(fieldStyle: .glass) self.backgroundNode = NavigationBackgroundNode(color: theme.background) self.backgroundNode.isUserInteractionEnabled = false @@ -994,7 +1044,7 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { self.iconNode.displaysAsynchronously = false self.iconNode.displayWithoutProcessing = true - self.textField = SearchBarTextField(theme: theme) + self.textField = SearchBarTextField(theme: theme, style: fieldStyle) self.textField.accessibilityTraits = .searchField self.textField.autocorrectionType = .no self.textField.returnKeyType = .search @@ -1011,14 +1061,27 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { super.init() - self.addSubnode(self.backgroundNode) - self.addSubnode(self.separatorNode) - - self.addSubnode(self.textBackgroundNode) + switch self.fieldStyle { + case .glass: + break + case .inlineNavigation: + break + case .legacy, .modern: + self.addSubnode(self.backgroundNode) + self.addSubnode(self.separatorNode) + self.addSubnode(self.textBackgroundNode) + } self.view.addSubview(self.textField) - self.addSubnode(self.iconNode) + + switch self.fieldStyle { + case .glass, .inlineNavigation: + break + case .legacy, .modern: + self.addSubnode(self.iconNode) + self.addSubnode(self.cancelButton) + } + self.addSubnode(self.clearButton) - self.addSubnode(self.cancelButton) self.textField.delegate = self self.textField.addTarget(self, action: #selector(self.textFieldDidChange(_:)), for: .editingChanged) @@ -1043,11 +1106,11 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { self.cancelButton.addTarget(self, action: #selector(self.cancelPressed), forControlEvents: .touchUpInside) self.clearButton.addTarget(self, action: #selector(self.clearPressed), forControlEvents: .touchUpInside) - self.updateThemeAndStrings(theme: theme, strings: strings) + self.updateThemeAndStrings(theme: theme, presentationTheme: presentationTheme, strings: strings) self.updateIsEmpty(animated: false) } - public func updateThemeAndStrings(theme: SearchBarNodeTheme, strings: PresentationStrings) { + public func updateThemeAndStrings(theme: SearchBarNodeTheme, presentationTheme: PresentationTheme, strings: PresentationStrings) { if self.theme != theme || self.strings !== strings { self.clearButton.accessibilityLabel = strings.WebSearch_RecentSectionClear self.cancelButton.accessibilityLabel = self.cancelText ?? strings.Common_Cancel @@ -1080,6 +1143,7 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { } self.theme = theme + self.presentationTheme = presentationTheme self.strings = strings if let (boundingSize, leftInset, rightInset) = self.validLayout { self.updateLayout(boundingSize: boundingSize, leftInset: leftInset, rightInset: rightInset, transition: .immediate) @@ -1093,19 +1157,40 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { self.backgroundNode.update(size: self.backgroundNode.bounds.size, transition: .immediate) transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: self.bounds.size.height), size: CGSize(width: self.bounds.size.width, height: UIScreenPixel))) - let verticalOffset: CGFloat = boundingSize.height - 82.0 - let contentFrame = CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: boundingSize.width - leftInset - rightInset, height: boundingSize.height)) - let textBackgroundHeight = self.fieldStyle.height + let textBackgroundHeight: CGFloat + if case .inlineNavigation = self.fieldStyle { + textBackgroundHeight = boundingSize.height + } else { + textBackgroundHeight = self.fieldStyle.height + } + let verticalOffset: CGFloat + switch self.fieldStyle { + case .inlineNavigation, .glass: + verticalOffset = -textBackgroundHeight + case .legacy, .modern: + verticalOffset = boundingSize.height - 82.0 + } let cancelButtonSize = self.cancelButton.measure(CGSize(width: 100.0, height: CGFloat.infinity)) transition.updateFrame(node: self.cancelButton, frame: CGRect(origin: CGPoint(x: contentFrame.maxX - 10.0 - cancelButtonSize.width, y: verticalOffset + textBackgroundHeight + floorToScreenPixels((textBackgroundHeight - cancelButtonSize.height) / 2.0)), size: cancelButtonSize)) let padding = self.fieldStyle.padding - let textBackgroundFrame = CGRect(origin: CGPoint(x: contentFrame.minX + padding, y: verticalOffset + textBackgroundHeight), size: CGSize(width: contentFrame.width - padding * 2.0 - (self.hasCancelButton ? cancelButtonSize.width + 11.0 : 0.0), height: textBackgroundHeight)) + var textBackgroundFrame = CGRect(origin: CGPoint(x: contentFrame.minX + padding, y: verticalOffset + textBackgroundHeight), size: CGSize(width: contentFrame.width - padding - (self.hasCancelButton ? cancelButtonSize.width + 11.0 : 0.0), height: textBackgroundHeight)) + if case .glass = self.fieldStyle { + textBackgroundFrame.size.width -= 8.0 + } else { + textBackgroundFrame.size.width -= padding + } transition.updateFrame(node: self.textBackgroundNode, frame: textBackgroundFrame) - let textFrame = CGRect(origin: CGPoint(x: textBackgroundFrame.minX + 24.0, y: textBackgroundFrame.minY), size: CGSize(width: max(1.0, textBackgroundFrame.size.width - 24.0 - 27.0), height: textBackgroundFrame.size.height)) + var textFrame = CGRect(origin: CGPoint(x: 0.0, y: textBackgroundFrame.minY), size: CGSize(width: max(1.0, textBackgroundFrame.size.width - 24.0 - 27.0), height: textBackgroundFrame.size.height)) + if case .inlineNavigation = self.fieldStyle { + textFrame.size.width = boundingSize.width - 27.0 + textBackgroundFrame.size.width = boundingSize.width + } else { + textFrame.origin.x = textBackgroundFrame.minX + 24.0 + } if let iconImage = self.iconNode.image { let iconSize = iconImage.size @@ -1121,6 +1206,54 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { transition.updateFrame(node: self.clearButton, frame: CGRect(origin: CGPoint(x: textBackgroundFrame.maxX - 6.0 - clearSize.width, y: textBackgroundFrame.minY + floor((textBackgroundFrame.size.height - clearSize.height) / 2.0)), size: clearSize)) self.textField.frame = textFrame + + let additionalPlaceholderInset = self.textField.tokensInsetWidth + + let searchPlaceholderFrame = CGRect(origin: CGPoint(x: leftInset + 16.0, y: 0.0), size: CGSize(width: max(0.0, boundingSize.width - 16.0 * 2.0 - leftInset - rightInset), height: 44.0)) + + if case .glass = self.fieldStyle, self.takenSearchPlaceholderContentView == nil { + transition.updateFrame(node: self.inlineSearchPlaceholder, frame: searchPlaceholderFrame) + var isFirstTime = false + if let theme = self.theme { + let _ = self.inlineSearchPlaceholder.updateLayout( + placeholderString: self.placeholderString, + compactPlaceholderString: self.placeholderString, + constrainedSize: searchPlaceholderFrame.size, + expansionProgress: 1.0, + iconColor: theme.inputIcon, + foregroundColor: self.presentationTheme.chat.inputPanel.panelControlColor, + backgroundColor: self.presentationTheme.rootController.navigationBar.opaqueBackgroundColor, + controlColor: self.presentationTheme.chat.inputPanel.panelControlColor, + transition: transition + ) + if self.inlineSearchPlaceholderContentsView == nil { + isFirstTime = true + let inlineSearchPlaceholderContentsView = self.inlineSearchPlaceholder.takeContents() + inlineSearchPlaceholderContentsView.onCancel = { [weak self] in + guard let self else { + return + } + self.cancel?() + } + self.inlineSearchPlaceholderContentsView = inlineSearchPlaceholderContentsView + self.view.insertSubview(inlineSearchPlaceholderContentsView, at: 0) + } + } + if let inlineSearchPlaceholderContentsView = self.inlineSearchPlaceholderContentsView { + inlineSearchPlaceholderContentsView.update(size: searchPlaceholderFrame.size, isActive: true, additionalPlaceholderInset: additionalPlaceholderInset, transition: transition) + transition.updateFrame(view: inlineSearchPlaceholderContentsView, frame: searchPlaceholderFrame) + + if isFirstTime { + self.updateIsEmpty(animated: false) + inlineSearchPlaceholderContentsView.updateSearchIconVisibility(isVisible: !self.activity) + } + } + } + + if !self.isAnimatingOut, let takenSearchPlaceholderContentView = self.takenSearchPlaceholderContentView { + transition.updateFrame(view: takenSearchPlaceholderContentView, frame: searchPlaceholderFrame) + takenSearchPlaceholderContentView.update(size: searchPlaceholderFrame.size, isActive: true, additionalPlaceholderInset: additionalPlaceholderInset, transition: transition) + } } @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { @@ -1138,7 +1271,33 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { } public func animateIn(from node: SearchBarPlaceholderNode, duration: Double, timingFunction: String) { - let initialTextBackgroundFrame = node.view.convert(node.backgroundNode.frame, to: self.view) + guard let (boundingSize, leftInset, rightInset) = self.validLayout else { + return + } + + self.inlineSearchPlaceholder.isHidden = true + + let takenSearchPlaceholderContentView = node.takeContents() + takenSearchPlaceholderContentView.onCancel = { [weak self] in + guard let self else { + return + } + self.cancel?() + } + self.takenSearchPlaceholderContentView = takenSearchPlaceholderContentView + self.view.insertSubview(takenSearchPlaceholderContentView, at: 0) + if let inlineSearchPlaceholderContentsView = self.inlineSearchPlaceholderContentsView { + inlineSearchPlaceholderContentsView.removeFromSuperview() + } + + let sourceFrame = node.view.convert(node.bounds, to: self.view) + let targetFrame = CGRect(origin: CGPoint(x: leftInset + 16.0, y: 0.0), size: CGSize(width: max(0.0, boundingSize.width - 16.0 * 2.0 - leftInset - rightInset), height: 44.0)) + let transition: ContainedViewLayoutTransition = .animated(duration: duration, curve: timingFunction == kCAMediaTimingFunctionSpring ? .spring : .easeInOut) + takenSearchPlaceholderContentView.frame = sourceFrame + transition.updateFrame(view: takenSearchPlaceholderContentView, frame: targetFrame) + takenSearchPlaceholderContentView.update(size: targetFrame.size, isActive: true, additionalPlaceholderInset: self.textField.tokensInsetWidth, transition: transition) + + /*let initialTextBackgroundFrame = node.view.convert(node.backgroundView.frame, to: self.view) let initialBackgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: self.bounds.size.width, height: max(0.0, initialTextBackgroundFrame.maxY + 8.0))) if let fromBackgroundColor = node.backgroundColor, let toBackgroundColor = self.backgroundNode.backgroundColor { @@ -1152,7 +1311,7 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { self.separatorNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration) self.separatorNode.layer.animateFrame(from: initialSeparatorFrame, to: self.separatorNode.frame, duration: duration, timingFunction: timingFunction) - if let fromTextBackgroundColor = node.backgroundNode.backgroundColor, let toTextBackgroundColor = self.textBackgroundNode.backgroundColor { + if let fromTextBackgroundColor = node.backgroundView.backgroundColor, let toTextBackgroundColor = self.textBackgroundNode.backgroundColor { self.textBackgroundNode.layer.animate(from: fromTextBackgroundColor.cgColor, to: toTextBackgroundColor.cgColor, keyPath: "backgroundColor", timingFunction: timingFunction, duration: duration * 1.0) } self.textBackgroundNode.layer.animateFrame(from: initialTextBackgroundFrame, to: self.textBackgroundNode.frame, duration: duration, timingFunction: timingFunction) @@ -1177,7 +1336,7 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { let cancelButtonFrame = self.cancelButton.frame self.cancelButton.layer.animatePosition(from: CGPoint(x: self.bounds.size.width + cancelButtonFrame.size.width / 2.0, y: initialTextBackgroundFrame.midY), to: self.cancelButton.layer.position, duration: duration, timingFunction: timingFunction) - node.isHidden = true + node.isHidden = true*/ } public func deactivate(clear: Bool = true) { @@ -1191,7 +1350,9 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { } public func transitionOut(to node: SearchBarPlaceholderNode, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) { - let targetTextBackgroundFrame = node.view.convert(node.backgroundNode.frame, to: self.view) + self.isAnimatingOut = true + + /*let targetTextBackgroundFrame = node.view.convert(node.backgroundView.frame, to: self.view) let duration: Double = transition.isAnimated ? 0.5 : 0.0 let timingFunction = kCAMediaTimingFunctionSpring @@ -1307,8 +1468,8 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { let transitionBackgroundNode = ASDisplayNode() transitionBackgroundNode.isLayerBacked = true transitionBackgroundNode.displaysAsynchronously = false - transitionBackgroundNode.backgroundColor = node.backgroundNode.backgroundColor - transitionBackgroundNode.cornerRadius = node.backgroundNode.cornerRadius + transitionBackgroundNode.backgroundColor = node.backgroundView.backgroundColor + transitionBackgroundNode.cornerRadius = node.backgroundView.layer.cornerRadius self.insertSubnode(transitionBackgroundNode, aboveSubnode: self.textBackgroundNode) transitionBackgroundNode.layer.animateFrame(from: self.textBackgroundNode.frame, to: targetTextBackgroundFrame, duration: duration, timingFunction: timingFunction, removeOnCompletion: false) @@ -1331,7 +1492,52 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { self.iconNode.layer.animateFrame(from: self.iconNode.frame, to: targetIconFrame, duration: duration, timingFunction: timingFunction, removeOnCompletion: false) let cancelButtonFrame = self.cancelButton.frame - self.cancelButton.layer.animatePosition(from: self.cancelButton.layer.position, to: CGPoint(x: self.bounds.size.width + cancelButtonFrame.size.width / 2.0, y: targetTextBackgroundFrame.midY), duration: duration, timingFunction: timingFunction, removeOnCompletion: false) + self.cancelButton.layer.animatePosition(from: self.cancelButton.layer.position, to: CGPoint(x: self.bounds.size.width + cancelButtonFrame.size.width / 2.0, y: targetTextBackgroundFrame.midY), duration: duration, timingFunction: timingFunction, removeOnCompletion: false)*/ + + if let takenSearchPlaceholderContentView = self.takenSearchPlaceholderContentView { + let transition = ComponentTransition(transition) + let alphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.2) + + let sourceFrame = node.view.convert(node.bounds, to: self.view) + takenSearchPlaceholderContentView.update(size: sourceFrame.size, isActive: false, additionalPlaceholderInset: 0.0, transition: transition.containedViewLayoutTransition) + takenSearchPlaceholderContentView.updatePlaceholderVisibility(isVisible: true) + takenSearchPlaceholderContentView.updateSearchIconVisibility(isVisible: true) + + transition.setFrame(view: takenSearchPlaceholderContentView, frame: sourceFrame, completion: { [weak node] _ in + node?.putBackContents() + completion() + }) + + let textBackgroundHeight: CGFloat + if case .inlineNavigation = self.fieldStyle { + textBackgroundHeight = sourceFrame.height + } else { + textBackgroundHeight = self.fieldStyle.height + } + + let padding = self.fieldStyle.padding + var textBackgroundFrame = CGRect(origin: CGPoint(x: sourceFrame.minX + padding, y: sourceFrame.minY), size: CGSize(width: sourceFrame.width - padding, height: textBackgroundHeight)) + if case .glass = self.fieldStyle { + textBackgroundFrame.size.width -= 8.0 + } else { + textBackgroundFrame.size.width -= padding + } + + var textFrame = CGRect(origin: CGPoint(x: 0.0, y: textBackgroundFrame.minY), size: CGSize(width: max(1.0, textBackgroundFrame.size.width - 24.0 - 27.0), height: textBackgroundFrame.size.height)) + if case .inlineNavigation = self.fieldStyle { + textFrame.size.width = sourceFrame.width - 27.0 + textBackgroundFrame.size.width = sourceFrame.width + } else { + textFrame.origin.x = textBackgroundFrame.minX + 24.0 + } + transition.setFrame(view: self.textField, frame: textFrame) + //alphaTransition.setAlpha(view: self.textField, alpha: 0.0) + self.textField.isHidden = true + + let clearSize = self.clearButton.bounds.size + alphaTransition.setAlpha(view: self.clearButton.view, alpha: 0.0) + transition.setFrame(view: self.clearButton.view, frame: CGRect(origin: CGPoint(x: textBackgroundFrame.maxX - 6.0 - clearSize.width, y: textBackgroundFrame.minY + floor((textBackgroundFrame.size.height - clearSize.height) / 2.0)), size: clearSize)) + } } public func textFieldDidBeginEditing(_ textField: UITextField) { @@ -1403,6 +1609,10 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate { let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.3, curve: .spring) : .immediate let placeholderTransition = !isEmpty ? .immediate : transition placeholderTransition.updateAlpha(node: self.textField.placeholderLabel, alpha: isEmpty ? 1.0 : 0.0) + if let takenSearchPlaceholderContentView = self.takenSearchPlaceholderContentView { + takenSearchPlaceholderContentView.updatePlaceholderVisibility(isVisible: isEmpty) + } + self.inlineSearchPlaceholderContentsView?.updatePlaceholderVisibility(isVisible: isEmpty) let clearIsHidden = (textIsEmpty && tokensEmpty) && self.prefixString == nil transition.updateAlpha(node: self.clearButton.imageNode, alpha: clearIsHidden ? 0.0 : 1.0) diff --git a/submodules/SearchBarNode/Sources/SearchBarPlaceholderNode.swift b/submodules/SearchBarNode/Sources/SearchBarPlaceholderNode.swift index 25c468e6..f6f4e3ff 100644 --- a/submodules/SearchBarNode/Sources/SearchBarPlaceholderNode.swift +++ b/submodules/SearchBarNode/Sources/SearchBarPlaceholderNode.swift @@ -5,6 +5,8 @@ import AsyncDisplayKit import Display import AppBundle import ComponentFlow +import GlassBackgroundComponent +import ComponentDisplayAdapters private let templateLoupeIcon = UIImage(bundleImageName: "Components/Search Bar/Loupe") @@ -21,44 +23,69 @@ private class SearchBarPlaceholderNodeView: UIView { } } -public class SearchBarPlaceholderNode: ASDisplayNode { - public var activate: (() -> Void)? +public final class SearchBarPlaceholderContentView: UIView { + private struct Params { + var placeholderString: NSAttributedString? + var compactPlaceholderString: NSAttributedString? + var constrainedSize: CGSize + var expansionProgress: CGFloat + var iconColor: UIColor + var foregroundColor: UIColor + var backgroundColor: UIColor + var controlColor: UIColor + var isActive: Bool + var additionalPlaceholderInset: CGFloat + + init(placeholderString: NSAttributedString?, compactPlaceholderString: NSAttributedString?, constrainedSize: CGSize, expansionProgress: CGFloat, iconColor: UIColor, foregroundColor: UIColor, backgroundColor: UIColor, controlColor: UIColor, isActive: Bool, additionalPlaceholderInset: CGFloat) { + self.placeholderString = placeholderString + self.compactPlaceholderString = compactPlaceholderString + self.constrainedSize = constrainedSize + self.expansionProgress = expansionProgress + self.iconColor = iconColor + self.foregroundColor = foregroundColor + self.backgroundColor = backgroundColor + self.controlColor = controlColor + self.isActive = isActive + self.additionalPlaceholderInset = additionalPlaceholderInset + } + } - private let fieldStyle: SearchBarStyle - public let backgroundNode: ASDisplayNode + let fieldStyle: SearchBarStyle + let plainBackgroundView: UIImageView + let glassBackgroundView: GlassBackgroundView? private var fillBackgroundColor: UIColor private var foregroundColor: UIColor private var iconColor: UIColor - public let iconNode: ASImageNode - public let labelNode: TextNode + let iconNode: ASImageNode + let labelNode: TextNode + let plainIconNode: ASImageNode + let plainLabelNode: TextNode - var pointerInteraction: PointerInteraction? + private var close: (background: GlassBackgroundView, icon: UIImageView)? - public private(set) var placeholderString: NSAttributedString? + private(set) var placeholderString: NSAttributedString? - private(set) var accessoryComponentContainer: UIView? - private(set) var accessoryComponentView: ComponentHostView? + private var params: Params? - convenience public override init() { - self.init(fieldStyle: .legacy) - } + public var onCancel: (() -> Void)? - public init(fieldStyle: SearchBarStyle = .legacy) { + init(fieldStyle: SearchBarStyle) { self.fieldStyle = fieldStyle - self.backgroundNode = ASDisplayNode() - self.backgroundNode.isLayerBacked = false - self.backgroundNode.displaysAsynchronously = false - self.fillBackgroundColor = UIColor.white self.foregroundColor = UIColor(rgb: 0xededed) self.iconColor = UIColor(rgb: 0x000000, alpha: 0.0) - self.backgroundNode.backgroundColor = self.foregroundColor - self.backgroundNode.cornerRadius = self.fieldStyle.cornerDiameter / 2.0 + self.plainBackgroundView = UIImageView() + + switch fieldStyle { + case .legacy, .modern: + self.glassBackgroundView = nil + case .inlineNavigation, .glass: + self.glassBackgroundView = GlassBackgroundView() + } self.iconNode = ASImageNode() - self.iconNode.isLayerBacked = true self.iconNode.displaysAsynchronously = false self.iconNode.displayWithoutProcessing = true @@ -66,51 +93,431 @@ public class SearchBarPlaceholderNode: ASDisplayNode { self.labelNode.isOpaque = false self.labelNode.isUserInteractionEnabled = false + self.plainIconNode = ASImageNode() + self.plainIconNode.displaysAsynchronously = false + self.plainIconNode.displayWithoutProcessing = true + + self.plainLabelNode = TextNode() + self.plainLabelNode.isOpaque = false + self.plainLabelNode.isUserInteractionEnabled = false + + super.init(frame: CGRect()) + + self.plainBackgroundView.isUserInteractionEnabled = true + self.addSubview(self.plainBackgroundView) + + self.plainBackgroundView.addSubview(self.plainIconNode.view) + self.plainBackgroundView.addSubview(self.plainLabelNode.view) + + if let glassBackgroundView = self.glassBackgroundView { + self.addSubview(glassBackgroundView) + + glassBackgroundView.contentView.addSubview(self.iconNode.view) + glassBackgroundView.contentView.addSubview(self.labelNode.view) + } + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc private func onCloseTapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + self.onCancel?() + } + } + + func updateLayout( + placeholderString: NSAttributedString?, + compactPlaceholderString: NSAttributedString?, + constrainedSize: CGSize, + expansionProgress: CGFloat, + iconColor: UIColor, + foregroundColor: UIColor, + backgroundColor: UIColor, + controlColor: UIColor, + transition: ContainedViewLayoutTransition + ) -> CGFloat { + let params = Params( + placeholderString: placeholderString, + compactPlaceholderString: compactPlaceholderString, + constrainedSize: constrainedSize, + expansionProgress: expansionProgress, + iconColor: iconColor, + foregroundColor: foregroundColor, + backgroundColor: backgroundColor, + controlColor: controlColor, + isActive: false, + additionalPlaceholderInset: 0.0 + ) + self.params = params + return self.updateLayout(params: params, transition: transition) + } + + public func update(size: CGSize, isActive: Bool, additionalPlaceholderInset: CGFloat, transition: ContainedViewLayoutTransition) { + guard var params = self.params else { + return + } + params.constrainedSize = size + params.expansionProgress = 1.0 + params.isActive = isActive + params.additionalPlaceholderInset = additionalPlaceholderInset + let _ = self.updateLayout(params: params, transition: transition) + } + + private func updateLayout(params: Params, transition: ContainedViewLayoutTransition) -> CGFloat { + let labelLayout = TextNode.asyncLayout(self.labelNode) + let plainLabelLayout = TextNode.asyncLayout(self.plainLabelNode) + let currentForegroundColor = self.foregroundColor + let currentIconColor = self.iconColor + + let placeholderString: NSAttributedString? + if params.constrainedSize.width < 350.0 { + placeholderString = params.compactPlaceholderString + } else { + placeholderString = params.placeholderString + } + + let (labelLayoutResult, labelApply) = labelLayout(TextNodeLayoutArguments(attributedString: placeholderString, backgroundColor: .clear, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: params.constrainedSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + let (_, plainLabelApply) = plainLabelLayout(TextNodeLayoutArguments(attributedString: placeholderString, backgroundColor: .clear, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: params.constrainedSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + + var updatedColor: UIColor? + var updatedIconImage: UIImage? + if !currentForegroundColor.isEqual(params.foregroundColor) { + updatedColor = params.foregroundColor + } + if !currentIconColor.isEqual(params.iconColor) { + updatedIconImage = generateLoupeIcon(color: params.iconColor) + } + + let height = params.constrainedSize.height * params.expansionProgress + + let _ = labelApply() + let _ = plainLabelApply() + + self.fillBackgroundColor = params.backgroundColor + self.foregroundColor = params.foregroundColor + self.iconColor = params.iconColor + self.plainBackgroundView.isUserInteractionEnabled = params.expansionProgress > 0.9999 + + if let updatedColor { + self.plainBackgroundView.backgroundColor = updatedColor + } + if let updatedIconImage { + self.iconNode.image = updatedIconImage + self.plainIconNode.image = updatedIconImage + } + + self.placeholderString = placeholderString + + var iconSize = CGSize() + var totalWidth = labelLayoutResult.size.width + + var spacing: CGFloat = 4.0 + if params.isActive { + spacing = 2.0 + } + + let iconX: CGFloat + if let iconImage = self.iconNode.image { + iconSize = iconImage.size + totalWidth += iconSize.width + spacing + if params.isActive { + iconX = 8.0 + } else { + iconX = floor((params.constrainedSize.width - totalWidth) / 2.0) + } + transition.updateFrame(node: self.iconNode, frame: CGRect(origin: CGPoint(x: iconX, y: floorToScreenPixels((height - iconSize.height) / 2.0)), size: iconSize)) + transition.updateFrame(node: self.plainIconNode, frame: CGRect(origin: CGPoint(x: iconX, y: floorToScreenPixels((height - iconSize.height) / 2.0)), size: iconSize)) + } else { + iconX = 12.0 + } + var textOffset: CGFloat = 0.0 + if params.constrainedSize.height >= 36.0 { + textOffset += 1.0 + } + let labelX: CGFloat = iconX + iconSize.width + spacing + params.additionalPlaceholderInset + let labelFrame = CGRect(origin: CGPoint(x: labelX, y: floorToScreenPixels((height - labelLayoutResult.size.height) / 2.0) + textOffset), size: labelLayoutResult.size) + transition.updateFrame(node: self.labelNode, frame: labelFrame) + transition.updateFrame(node: self.plainLabelNode, frame: labelFrame) + + var innerAlpha = max(0.0, params.expansionProgress - 0.77) / 0.23 + if innerAlpha > 0.9999 { + innerAlpha = 1.0 + } else if innerAlpha < 0.0001 { + innerAlpha = 0.0 + } + if self.labelNode.alpha != innerAlpha { + if !transition.isAnimated { + self.labelNode.layer.removeAnimation(forKey: "opacity") + self.iconNode.layer.removeAnimation(forKey: "opacity") + self.plainLabelNode.layer.removeAnimation(forKey: "opacity") + self.plainIconNode.layer.removeAnimation(forKey: "opacity") + } + + transition.updateAlpha(node: self.labelNode, alpha: innerAlpha) + transition.updateAlpha(node: self.iconNode, alpha: innerAlpha) + + transition.updateAlpha(node: self.plainLabelNode, alpha: innerAlpha) + transition.updateAlpha(node: self.plainIconNode, alpha: innerAlpha) + } + + let outerAlpha = min(0.3, params.expansionProgress) / 0.3 + let cornerRadius = height * 0.5 + + if self.plainBackgroundView.layer.cornerRadius != cornerRadius { + if !transition.isAnimated { + self.plainBackgroundView.layer.removeAnimation(forKey: "cornerRadius") + } + transition.updateCornerRadius(layer: self.plainBackgroundView.layer, cornerRadius: cornerRadius) + } + + var plainBackgroundAlpha = outerAlpha + if params.isActive { + plainBackgroundAlpha = 0.0 + } + if self.plainBackgroundView.alpha != plainBackgroundAlpha { + if !transition.isAnimated { + self.plainBackgroundView.layer.removeAnimation(forKey: "opacity") + } + transition.updateAlpha(layer: self.plainBackgroundView.layer, alpha: plainBackgroundAlpha) + } + + var backgroundFrame = CGRect(origin: CGPoint(), size: CGSize(width: params.constrainedSize.width, height: height)) + if params.isActive { + backgroundFrame.size.width -= 44.0 + 8.0 + } + + if self.plainBackgroundView.frame != backgroundFrame { + if !transition.isAnimated { + self.plainBackgroundView.layer.removeAnimation(forKey: "position") + self.plainBackgroundView.layer.removeAnimation(forKey: "bounds") + } + transition.updateFrame(view: self.plainBackgroundView, frame: CGRect(origin: CGPoint(), size: CGSize(width: params.constrainedSize.width, height: height))) + } + + if let glassBackgroundView = self.glassBackgroundView { + transition.updatePosition(layer: glassBackgroundView.layer, position: backgroundFrame.center) + transition.updateBounds(layer: glassBackgroundView.layer, bounds: CGRect(origin: CGPoint(), size: backgroundFrame.size)) + var backgroundAlpha: CGFloat = 1.0 + if backgroundFrame.height < 16.0 { + backgroundAlpha = max(0.0, min(1.0, backgroundFrame.height / 16.0)) + } + if !params.isActive { + backgroundAlpha = 0.0 + } + ComponentTransition(transition).setAlpha(view: glassBackgroundView, alpha: backgroundAlpha) + let isDark = params.backgroundColor.hsb.b < 0.5 + if params.isActive { + glassBackgroundView.update(size: backgroundFrame.size, cornerRadius: backgroundFrame.height * 0.5, isDark: isDark, tintColor: .init(kind: .panel, color: UIColor(white: isDark ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: ComponentTransition(transition)) + } + + if params.isActive { + let transition = ComponentTransition(transition) + + let closeFrame = CGRect(origin: CGPoint(x: params.constrainedSize.width - 44.0, y: 0.0), size: CGSize(width: 44.0, height: 44.0)) + + let close: (background: GlassBackgroundView, icon: UIImageView) + var closeTransition = transition + if let current = self.close { + close = current + } else { + closeTransition = closeTransition.withAnimation(.none) + close = (GlassBackgroundView(), UIImageView()) + self.close = close + + close.icon.image = generateImage(CGSize(width: 40.0, height: 40.0), contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + context.setLineWidth(2.0) + context.setLineCap(.round) + context.setStrokeColor(UIColor.white.cgColor) + + context.beginPath() + context.move(to: CGPoint(x: 12.0, y: 12.0)) + context.addLine(to: CGPoint(x: size.width - 12.0, y: size.height - 12.0)) + context.move(to: CGPoint(x: size.width - 12.0, y: 12.0)) + context.addLine(to: CGPoint(x: 12.0, y: size.height - 12.0)) + context.strokePath() + })?.withRenderingMode(.alwaysTemplate) + + close.background.contentView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.onCloseTapGesture(_:)))) + + close.background.contentView.addSubview(close.icon) + self.insertSubview(close.background, at: 0) + + if let image = close.icon.image { + close.icon.frame = image.size.centered(in: CGRect(origin: CGPoint(), size: closeFrame.size)) + } + + close.background.frame = closeFrame.offsetBy(dx: closeFrame.width + 40.0, dy: 0.0) + let isDark = params.backgroundColor.hsb.b < 0.5 + close.background.update(size: close.background.bounds.size, cornerRadius: close.background.bounds.height * 0.5, isDark: isDark, tintColor: .init(kind: .panel, color: UIColor(white: isDark ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: .immediate) + ComponentTransition.immediate.setScale(view: close.background, scale: 0.001) + } + + close.icon.tintColor = params.controlColor + + transition.setPosition(view: close.background, position: closeFrame.center) + transition.setBounds(view: close.background, bounds: CGRect(origin: CGPoint(), size: closeFrame.size)) + transition.setScale(view: close.background, scale: 1.0) + + if let image = close.icon.image { + transition.setFrame(view: close.icon, frame: image.size.centered(in: CGRect(origin: CGPoint(), size: closeFrame.size))) + } + + let isDark = params.backgroundColor.hsb.b < 0.5 + close.background.update(size: closeFrame.size, cornerRadius: closeFrame.height * 0.5, isDark: isDark, tintColor: .init(kind: .panel, color: UIColor(white: isDark ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: closeTransition) + } else { + let transition = ComponentTransition(transition) + + if let close = self.close { + self.close = nil + let closeBackground = close.background + let closeFrame = CGRect(origin: CGPoint(x: params.constrainedSize.width - 44.0, y: 0.0), size: CGSize(width: 44.0, height: 44.0)).offsetBy(dx: 44.0 + 40.0, dy: 0.0) + transition.setPosition(view: closeBackground, position: closeFrame.center) + transition.setBounds(view: closeBackground, bounds: CGRect(origin: CGPoint(), size: closeFrame.size)) + let isDark = params.backgroundColor.hsb.b < 0.5 + closeBackground.update(size: closeFrame.size, cornerRadius: closeFrame.height * 0.5, isDark: isDark, tintColor: .init(kind: .panel, color: UIColor(white: isDark ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition) + transition.setScale(view: closeBackground, scale: 0.001, completion: { [weak closeBackground] _ in + closeBackground?.removeFromSuperview() + }) + } + } + } + + /*if let accessoryComponentContainer = self.accessoryComponentContainer { + accessoryComponentContainer.frame = CGRect(origin: CGPoint(x: constrainedSize.width - accessoryComponentContainer.bounds.width - 4.0, y: floor((constrainedSize.height * expansionProgress - accessoryComponentContainer.bounds.height) / 2.0)), size: accessoryComponentContainer.bounds.size) + transition.updateAlpha(layer: accessoryComponentContainer.layer, alpha: innerAlpha) + }*/ + + return height + } + + public func updatePlaceholderVisibility(isVisible: Bool) { + self.labelNode.isHidden = !isVisible + self.plainLabelNode.isHidden = !isVisible + } + + public func updateSearchIconVisibility(isVisible: Bool) { + self.iconNode.isHidden = !isVisible + self.plainIconNode.isHidden = !isVisible + } +} + +public class SearchBarPlaceholderNode: ASDisplayNode { + private struct Params { + var placeholderString: NSAttributedString? + var compactPlaceholderString: NSAttributedString? + var constrainedSize: CGSize + var expansionProgress: CGFloat + var iconColor: UIColor + var foregroundColor: UIColor + var backgroundColor: UIColor + var controlColor: UIColor + + init(placeholderString: NSAttributedString?, compactPlaceholderString: NSAttributedString?, constrainedSize: CGSize, expansionProgress: CGFloat, iconColor: UIColor, foregroundColor: UIColor, backgroundColor: UIColor, controlColor: UIColor) { + self.placeholderString = placeholderString + self.compactPlaceholderString = compactPlaceholderString + self.constrainedSize = constrainedSize + self.expansionProgress = expansionProgress + self.iconColor = iconColor + self.foregroundColor = foregroundColor + self.backgroundColor = backgroundColor + self.controlColor = controlColor + } + } + + public var activate: (() -> Void)? + + private let containerView: UIView + private let contentView: SearchBarPlaceholderContentView + + public var backgroundView: UIView { + if let glassBackgroundView = self.contentView.glassBackgroundView { + return glassBackgroundView + } else { + return self.contentView.plainBackgroundView + } + } + + public var iconNode: ASImageNode { + return self.contentView.iconNode + } + + public var labelNode: TextNode { + return self.contentView.labelNode + } + + public var fieldStyle: SearchBarStyle { + return self.contentView.fieldStyle + } + + var pointerInteraction: PointerInteraction? + + public var placeholderString: NSAttributedString? { + return self.contentView.placeholderString + } + + private(set) var accessoryComponentContainer: UIView? + private(set) var accessoryComponentView: ComponentHostView? + + private var params: Params? + private var currentLayoutHeight: CGFloat? + private var isTakenOut: Bool = false + + public init(fieldStyle: SearchBarStyle = .legacy) { + self.containerView = UIView() + self.contentView = SearchBarPlaceholderContentView(fieldStyle: fieldStyle) + super.init() - self.addSubnode(self.backgroundNode) - self.addSubnode(self.iconNode) - self.addSubnode(self.labelNode) - - self.backgroundNode.isUserInteractionEnabled = true + self.view.addSubview(self.containerView) + self.containerView.addSubview(self.contentView) } override public func didLoad() { super.didLoad() let gestureRecognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.backgroundTap(_:))) - gestureRecognizer.highlight = { [weak self] point in + /*gestureRecognizer.highlight = { [weak self] point in guard let strongSelf = self else { return } - if let _ = point { - strongSelf.backgroundNode.layer.animate(from: (strongSelf.backgroundNode.backgroundColor ?? strongSelf.foregroundColor).cgColor, to: strongSelf.foregroundColor.withMultipliedBrightnessBy(0.9).cgColor, keyPath: "backgroundColor", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.2) - strongSelf.backgroundNode.backgroundColor = strongSelf.foregroundColor.withMultipliedBrightnessBy(0.9) - } else { - strongSelf.backgroundNode.layer.animate(from: (strongSelf.backgroundNode.backgroundColor ?? strongSelf.foregroundColor).cgColor, to: strongSelf.foregroundColor.cgColor, keyPath: "backgroundColor", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.4) - strongSelf.backgroundNode.backgroundColor = strongSelf.foregroundColor + if let backgroundNode = strongSelf.contentView.backgroundNode { + if let _ = point { + backgroundNode.layer.animate(from: (backgroundNode.backgroundColor ?? strongSelf.foregroundColor).cgColor, to: strongSelf.foregroundColor.withMultipliedBrightnessBy(0.9).cgColor, keyPath: "backgroundColor", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.2) + backgroundNode.backgroundColor = strongSelf.foregroundColor.withMultipliedBrightnessBy(0.9) + } else { + backgroundNode.layer.animate(from: (backgroundNode.backgroundColor ?? strongSelf.foregroundColor).cgColor, to: strongSelf.foregroundColor.cgColor, keyPath: "backgroundColor", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.4) + backgroundNode.backgroundColor = strongSelf.foregroundColor + } } - } + }*/ gestureRecognizer.tapActionAtPoint = { _ in return .waitForSingleTap } - self.backgroundNode.view.addGestureRecognizer(gestureRecognizer) + self.containerView.addGestureRecognizer(gestureRecognizer) - self.pointerInteraction = PointerInteraction(node: self, style: .caret, willEnter: { [weak self] in + /*self.pointerInteraction = PointerInteraction(node: self, style: .caret, willEnter: { [weak self] in guard let strongSelf = self else { return } - strongSelf.backgroundNode.backgroundColor = strongSelf.foregroundColor.withMultipliedBrightnessBy(0.95) + if let backgroundNode = strongSelf.contentView.backgroundNode { + backgroundNode.backgroundColor = strongSelf.foregroundColor.withMultipliedBrightnessBy(0.95) + } }, willExit: { [weak self] in guard let strongSelf = self else { return } - strongSelf.backgroundNode.backgroundColor = strongSelf.foregroundColor - }) + if let backgroundNode = strongSelf.contentView.backgroundNode { + backgroundNode.backgroundColor = strongSelf.foregroundColor + } + })*/ } public func setAccessoryComponent(component: AnyComponent?) { - if let component = component { + /*if let component = component { let accessoryComponentContainer: UIView if let current = self.accessoryComponentContainer { accessoryComponentContainer = current @@ -142,119 +549,43 @@ public class SearchBarPlaceholderNode: ASDisplayNode { accessoryComponentView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak accessoryComponentView] _ in accessoryComponentView?.removeFromSuperview() }) + }*/ + } + + public func takeContents() -> SearchBarPlaceholderContentView { + self.isTakenOut = true + return self.contentView + } + + public func putBackContents() { + self.isTakenOut = false + self.containerView.addSubview(self.contentView) + if let params = self.params { + let _ = self.update(params: params, transition: .immediate) } } - public func asyncLayout() -> (_ placeholderString: NSAttributedString?, _ compactPlaceholderString: NSAttributedString?, _ constrainedSize: CGSize, _ expansionProgress: CGFloat, _ iconColor: UIColor, _ foregroundColor: UIColor, _ backgroundColor: UIColor, _ transition: ContainedViewLayoutTransition) -> (CGFloat, () -> Void) { - let labelLayout = TextNode.asyncLayout(self.labelNode) - let currentForegroundColor = self.foregroundColor - let currentIconColor = self.iconColor + public func updateLayout(placeholderString: NSAttributedString?, compactPlaceholderString: NSAttributedString?, constrainedSize: CGSize, expansionProgress: CGFloat, iconColor: UIColor, foregroundColor: UIColor, backgroundColor: UIColor, controlColor: UIColor, transition: ContainedViewLayoutTransition) -> CGFloat { + let params = Params(placeholderString: placeholderString, compactPlaceholderString: compactPlaceholderString, constrainedSize: constrainedSize, expansionProgress: expansionProgress, iconColor: iconColor, foregroundColor: foregroundColor, backgroundColor: backgroundColor, controlColor: controlColor) + self.params = params - return { fullPlaceholderString, compactPlaceholderString, constrainedSize, expansionProgress, iconColor, foregroundColor, backgroundColor, transition in - let placeholderString: NSAttributedString? - if constrainedSize.width < 350.0 { - placeholderString = compactPlaceholderString - } else { - placeholderString = fullPlaceholderString - } - - let (labelLayoutResult, labelApply) = labelLayout(TextNodeLayoutArguments(attributedString: placeholderString, backgroundColor: .clear, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: constrainedSize, alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - var updatedColor: UIColor? - var updatedIconImage: UIImage? - if !currentForegroundColor.isEqual(foregroundColor) { - updatedColor = foregroundColor - } - if !currentIconColor.isEqual(iconColor) { - updatedIconImage = generateLoupeIcon(color: iconColor) - } - - let height = constrainedSize.height * expansionProgress - return (height, { [weak self] in - if let strongSelf = self { - let _ = labelApply() - - strongSelf.fillBackgroundColor = backgroundColor - strongSelf.foregroundColor = foregroundColor - strongSelf.iconColor = iconColor - strongSelf.backgroundNode.isUserInteractionEnabled = expansionProgress > 0.9999 - - if let updatedColor = updatedColor { - strongSelf.backgroundNode.backgroundColor = updatedColor - } - if let updatedIconImage = updatedIconImage { - strongSelf.iconNode.image = updatedIconImage - } - - strongSelf.placeholderString = placeholderString - - var iconSize = CGSize() - var totalWidth = labelLayoutResult.size.width - let spacing: CGFloat = 6.0 - - if let iconImage = strongSelf.iconNode.image { - iconSize = iconImage.size - totalWidth += iconSize.width + spacing - transition.updateFrame(node: strongSelf.iconNode, frame: CGRect(origin: CGPoint(x: floor((constrainedSize.width - totalWidth) / 2.0), y: floorToScreenPixels((height - iconSize.height) / 2.0)), size: iconSize)) - } - var textOffset: CGFloat = 0.0 - if constrainedSize.height >= 36.0 { - textOffset += 1.0 - } - let labelFrame = CGRect(origin: CGPoint(x: floor((constrainedSize.width - totalWidth) / 2.0) + iconSize.width + spacing, y: floorToScreenPixels((height - labelLayoutResult.size.height) / 2.0) + textOffset), size: labelLayoutResult.size) - transition.updateFrame(node: strongSelf.labelNode, frame: labelFrame) - - var innerAlpha = max(0.0, expansionProgress - 0.77) / 0.23 - if innerAlpha > 0.9999 { - innerAlpha = 1.0 - } else if innerAlpha < 0.0001 { - innerAlpha = 0.0 - } - if strongSelf.labelNode.alpha != innerAlpha { - if !transition.isAnimated { - strongSelf.labelNode.layer.removeAnimation(forKey: "opacity") - strongSelf.iconNode.layer.removeAnimation(forKey: "opacity") - } - - transition.updateAlpha(node: strongSelf.labelNode, alpha: innerAlpha) - transition.updateAlpha(node: strongSelf.iconNode, alpha: innerAlpha) - } - - let outerAlpha = min(0.3, expansionProgress) / 0.3 - let cornerRadius = min(strongSelf.fieldStyle.cornerDiameter / 2.0, height / 2.0) - - if strongSelf.backgroundNode.cornerRadius != cornerRadius { - if !transition.isAnimated { - strongSelf.backgroundNode.layer.removeAnimation(forKey: "cornerRadius") - } - transition.updateCornerRadius(node: strongSelf.backgroundNode, cornerRadius: cornerRadius) - } - - if strongSelf.backgroundNode.alpha != outerAlpha { - if !transition.isAnimated { - strongSelf.backgroundNode.layer.removeAnimation(forKey: "opacity") - } - transition.updateAlpha(node: strongSelf.backgroundNode, alpha: outerAlpha) - } - - if strongSelf.backgroundNode.frame != CGRect(origin: CGPoint(), size: CGSize(width: constrainedSize.width, height: height)) { - if !transition.isAnimated { - strongSelf.backgroundNode.layer.removeAnimation(forKey: "position") - strongSelf.backgroundNode.layer.removeAnimation(forKey: "bounds") - } - transition.updateFrame(node: strongSelf.backgroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: constrainedSize.width, height: height))) - } - - if let accessoryComponentContainer = strongSelf.accessoryComponentContainer { - accessoryComponentContainer.frame = CGRect(origin: CGPoint(x: constrainedSize.width - accessoryComponentContainer.bounds.width - 4.0, y: floor((constrainedSize.height * expansionProgress - accessoryComponentContainer.bounds.height) / 2.0)), size: accessoryComponentContainer.bounds.size) - transition.updateAlpha(layer: accessoryComponentContainer.layer, alpha: innerAlpha) - - } - } - }) + if self.isTakenOut { + return self.currentLayoutHeight ?? 44.0 + } else { + let height = self.update(params: params, transition: transition) + self.currentLayoutHeight = height + return height } } + private func update(params: Params, transition: ContainedViewLayoutTransition) -> CGFloat { + let height = self.contentView.updateLayout(placeholderString: params.placeholderString, compactPlaceholderString: params.compactPlaceholderString, constrainedSize: params.constrainedSize, expansionProgress: params.expansionProgress, iconColor: params.iconColor, foregroundColor: params.foregroundColor, backgroundColor: params.backgroundColor, controlColor: params.controlColor, transition: transition) + let size = CGSize(width: params.constrainedSize.width, height: height) + transition.updateFrame(view: self.containerView, frame: CGRect(origin: CGPoint(), size: size)) + transition.updateFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: size)) + return height + } + @objc private func backgroundTap(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) { if case .ended = recognizer.state { self.activate?() diff --git a/submodules/SearchUI/BUILD b/submodules/SearchUI/BUILD index 7210ffaa..84660a90 100644 --- a/submodules/SearchUI/BUILD +++ b/submodules/SearchUI/BUILD @@ -10,12 +10,16 @@ swift_library( "-warnings-as-errors", ], deps = [ - "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", - "//submodules/AsyncDisplayKit:AsyncDisplayKit", - "//submodules/Display:Display", - "//submodules/TelegramPresentationData:TelegramPresentationData", - "//submodules/SearchBarNode:SearchBarNode", - "//submodules/ChatListSearchItemNode:ChatListSearchItemNode", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/TelegramPresentationData", + "//submodules/SearchBarNode", + "//submodules/TelegramUI/Components/GlassBackgroundComponent", + "//submodules/ComponentFlow", + "//submodules/Components/ComponentDisplayAdapters", + "//submodules/AppBundle", + "//submodules/ActivityIndicator", ], visibility = [ "//visibility:public", diff --git a/submodules/SearchUI/Sources/FixSearchableListNodeScrolling.swift b/submodules/SearchUI/Sources/FixSearchableListNodeScrolling.swift index 9d202507..3704caa3 100644 --- a/submodules/SearchUI/Sources/FixSearchableListNodeScrolling.swift +++ b/submodules/SearchUI/Sources/FixSearchableListNodeScrolling.swift @@ -1,33 +1,6 @@ import Foundation import AsyncDisplayKit import Display -import ChatListSearchItemNode - -public func fixSearchableListNodeScrolling(_ listNode: ListView) { - var searchItemNode: ListViewItemNode? - var nextItemNode: ListViewItemNode? - - listNode.forEachItemNode { itemNode in - if let itemNode = itemNode as? ChatListSearchItemNode { - searchItemNode = itemNode - } else if searchItemNode != nil && nextItemNode == nil { - nextItemNode = itemNode as? ListViewItemNode - } - } - - if let searchItemNode = searchItemNode { - let itemFrame = searchItemNode.apparentFrame - if itemFrame.contains(CGPoint(x: 0.0, y: listNode.insets.top)) { - if itemFrame.minY + itemFrame.height * 0.6 < listNode.insets.top { - if let nextItemNode = nextItemNode { - listNode.ensureItemNodeVisibleAtTopInset(nextItemNode) - } - } else { - listNode.ensureItemNodeVisibleAtTopInset(searchItemNode) - } - } - } -} public func fixNavigationSearchableListNodeScrolling(_ listNode: ListView, searchNode: NavigationBarSearchContentNode) -> Bool { if searchNode.expansionProgress > 0.0 && searchNode.expansionProgress < 1.0 { @@ -47,22 +20,3 @@ public func fixNavigationSearchableListNodeScrolling(_ listNode: ListView, searc } return false } - -func fixNavigationSearchableGridNodeScrolling(_ gridNode: GridNode, searchNode: NavigationBarSearchContentNode) -> Bool { - if searchNode.expansionProgress > 0.0 && searchNode.expansionProgress < 1.0 { - let scrollToItem: GridNodeScrollToItem - let targetProgress: CGFloat - if searchNode.expansionProgress < 0.6 { - scrollToItem = GridNodeScrollToItem(index: 0, position: .top(-navigationBarSearchContentHeight), transition: .animated(duration: 0.3, curve: .easeInOut), directionHint: .up, adjustForSection: true, adjustForTopInset: true) - targetProgress = 0.0 - } else { - scrollToItem = GridNodeScrollToItem(index: 0, position: .top(0.0), transition: .animated(duration: 0.3, curve: .easeInOut), directionHint: .up, adjustForSection: true, adjustForTopInset: true) - targetProgress = 1.0 - } - searchNode.updateExpansionProgress(targetProgress, animated: true) - - gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: scrollToItem, updateLayout: nil, itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil, updateOpaqueState: nil, synchronousLoads: false), completion: { _ in }) - return true - } - return false -} diff --git a/submodules/SearchUI/Sources/NavigationBarSearchContentNode.swift b/submodules/SearchUI/Sources/NavigationBarSearchContentNode.swift index 49978807..e63cadc7 100644 --- a/submodules/SearchUI/Sources/NavigationBarSearchContentNode.swift +++ b/submodules/SearchUI/Sources/NavigationBarSearchContentNode.swift @@ -4,11 +4,28 @@ import AsyncDisplayKit import Display import TelegramPresentationData import SearchBarNode +import GlassBackgroundComponent +import ComponentFlow +import ComponentDisplayAdapters +import AppBundle +import ActivityIndicator private let searchBarFont = Font.regular(17.0) -public let navigationBarSearchContentHeight: CGFloat = 54.0 +public let navigationBarSearchContentHeight: CGFloat = 60.0 public class NavigationBarSearchContentNode: NavigationBarContentNode { + private struct Params: Equatable { + let size: CGSize + let leftInset: CGFloat + let rightInset: CGFloat + + init(size: CGSize, leftInset: CGFloat, rightInset: CGFloat) { + self.size = size + self.leftInset = leftInset + self.rightInset = rightInset + } + } + public var theme: PresentationTheme? public var placeholder: String public var compactPlaceholder: String @@ -19,8 +36,6 @@ public class NavigationBarSearchContentNode: NavigationBarContentNode { private var disabledOverlay: ASDisplayNode? public var expansionProgress: CGFloat = 1.0 - - public var additionalHeight: CGFloat = 0.0 private var validLayout: (CGSize, CGFloat, CGFloat)? @@ -30,7 +45,7 @@ public class NavigationBarSearchContentNode: NavigationBarContentNode { self.compactPlaceholder = compactPlaceholder ?? placeholder self.inline = inline - self.placeholderNode = SearchBarPlaceholderNode(fieldStyle: .modern) + self.placeholderNode = SearchBarPlaceholderNode(fieldStyle: .glass) self.placeholderNode.labelNode.displaysAsynchronously = false super.init() @@ -41,6 +56,8 @@ public class NavigationBarSearchContentNode: NavigationBarContentNode { self.addSubnode(self.placeholderNode) self.placeholderNode.activate = activate + + //self.backgroundColor = .red } public func updateThemeAndPlaceholder(theme: PresentationTheme, placeholder: String, compactPlaceholder: String? = nil) { @@ -102,10 +119,10 @@ public class NavigationBarSearchContentNode: NavigationBarContentNode { } private func updatePlaceholder(_ progress: CGFloat, size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { - let padding: CGFloat = 10.0 + let padding: CGFloat = 16.0 let baseWidth = size.width - padding * 2.0 - leftInset - rightInset - let fieldHeight: CGFloat = 36.0 + let fieldHeight: CGFloat = 44.0 let fraction = fieldHeight / self.nominalHeight let fullFraction = navigationBarSearchContentHeight / self.nominalHeight @@ -116,21 +133,19 @@ public class NavigationBarSearchContentNode: NavigationBarContentNode { var visibleProgress: CGFloat = toLow + (self.expansionProgress - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) visibleProgress = max(0.0, min(1.0, visibleProgress)) - let searchBarNodeLayout = self.placeholderNode.asyncLayout() - let textColor = self.theme?.rootController.navigationSearchBar.inputPlaceholderTextColor ?? UIColor(rgb: 0x8e8e93) var fillColor = self.theme?.rootController.navigationSearchBar.inputFillColor ?? .clear if self.inline, let theme = self.theme, fillColor.distance(to: theme.list.blocksBackgroundColor) < 100 { fillColor = fillColor.withMultipliedBrightnessBy(0.8) } - let backgroundColor = self.theme?.rootController.navigationBar.opaqueBackgroundColor ?? .clear + let backgroundColor = self.theme?.chatList.regularSearchBarColor ?? .clear + let controlColor = self.theme?.chat.inputPanel.panelControlColor ?? .black let placeholderString = NSAttributedString(string: self.placeholder, font: searchBarFont, textColor: textColor) let compactPlaceholderString = NSAttributedString(string: self.compactPlaceholder, font: searchBarFont, textColor: textColor) - let (searchBarHeight, searchBarApply) = searchBarNodeLayout(placeholderString, compactPlaceholderString, CGSize(width: baseWidth, height: fieldHeight), visibleProgress, textColor, fillColor, backgroundColor, transition) - searchBarApply() + let searchBarHeight = self.placeholderNode.updateLayout(placeholderString: placeholderString, compactPlaceholderString: compactPlaceholderString, constrainedSize: CGSize(width: baseWidth, height: fieldHeight), expansionProgress: visibleProgress, iconColor: textColor, foregroundColor: fillColor, backgroundColor: backgroundColor, controlColor: controlColor, transition: transition) let searchBarFrame = CGRect(origin: CGPoint(x: padding + leftInset, y: size.height + (1.0 - visibleProgress) * fieldHeight - 8.0 - fieldHeight), size: CGSize(width: baseWidth, height: fieldHeight)) transition.updateFrame(node: self.placeholderNode, frame: searchBarFrame) @@ -143,10 +158,12 @@ public class NavigationBarSearchContentNode: NavigationBarContentNode { } } - override public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { + override public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { self.validLayout = (size, leftInset, rightInset) self.updatePlaceholder(self.expansionProgress, size: size, leftInset: leftInset, rightInset: rightInset, transition: transition) + + return size } override public var height: CGFloat { @@ -158,7 +175,7 @@ public class NavigationBarSearchContentNode: NavigationBarContentNode { } override public var nominalHeight: CGFloat { - return navigationBarSearchContentHeight + self.additionalHeight + return 60.0 } override public var mode: NavigationBarContentMode { diff --git a/submodules/SearchUI/Sources/SearchDisplayController.swift b/submodules/SearchUI/Sources/SearchDisplayController.swift index 50420e6b..53cdd002 100644 --- a/submodules/SearchUI/Sources/SearchDisplayController.swift +++ b/submodules/SearchUI/Sources/SearchDisplayController.swift @@ -25,12 +25,14 @@ public final class SearchDisplayController { } } - private let searchBar: SearchBarNode + private var searchBar: SearchBarNode? + private let searchBarIsExternal: Bool private let mode: SearchDisplayControllerMode private let backgroundNode: BackgroundNode public let contentNode: SearchDisplayControllerContentNode private var hasSeparator: Bool private let inline: Bool + private let cancel: () -> Void private var containerLayout: (ContainerViewLayout, CGFloat)? @@ -38,71 +40,75 @@ public final class SearchDisplayController { private var isSearchingDisposable: Disposable? - public init(presentationData: PresentationData, mode: SearchDisplayControllerMode = .navigation, placeholder: String? = nil, hasBackground: Bool = false, hasSeparator: Bool = false, contentNode: SearchDisplayControllerContentNode, inline: Bool = false, cancel: @escaping () -> Void) { + public init( + presentationData: PresentationData, + mode: SearchDisplayControllerMode = .navigation, + placeholder: String? = nil, + hasBackground: Bool = false, + hasSeparator: Bool = false, + contentNode: SearchDisplayControllerContentNode, + inline: Bool = false, + cancel: @escaping () -> Void, + fieldStyle: SearchBarStyle = .modern, + searchBarIsExternal: Bool = false + ) { self.inline = inline - self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: presentationData.theme, hasBackground: hasBackground, hasSeparator: hasSeparator, inline: inline), strings: presentationData.strings, fieldStyle: .modern, forceSeparator: hasSeparator, displayBackground: hasBackground) + self.cancel = cancel + self.searchBarIsExternal = searchBarIsExternal + + if !searchBarIsExternal { + self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: presentationData.theme, hasBackground: hasBackground, hasSeparator: hasSeparator, inline: inline), presentationTheme: presentationData.theme, strings: presentationData.strings, fieldStyle: fieldStyle, forceSeparator: hasSeparator, displayBackground: hasBackground) + } self.backgroundNode = BackgroundNode() self.backgroundNode.allowsGroupOpacity = true self.mode = mode self.contentNode = contentNode self.hasSeparator = hasSeparator + + if let searchBar = self.searchBar { + self.setSearchBar(searchBar) + } - self.searchBar.textUpdated = { [weak contentNode] text, _ in - contentNode?.searchTextUpdated(text: text) - } - self.searchBar.tokensUpdated = { [weak contentNode] tokens in - contentNode?.searchTokensUpdated(tokens: tokens) - } - self.searchBar.cancel = { [weak self] in - self?.isDeactivating = true - cancel() - } - self.searchBar.clearPrefix = { [weak contentNode] in - contentNode?.searchTextClearPrefix() - } - self.searchBar.clearTokens = { [weak contentNode] in - contentNode?.searchTextClearTokens() - } self.contentNode.cancel = { [weak self] in self?.isDeactivating = true cancel() } self.contentNode.dismissInput = { [weak self] in - self?.searchBar.deactivate(clear: false) + self?.searchBar?.deactivate(clear: false) } var isFirstTime = true self.contentNode.setQuery = { [weak self] prefix, tokens, query in - if let strongSelf = self { - strongSelf.searchBar.prefixString = prefix - let previousTokens = strongSelf.searchBar.tokens - strongSelf.searchBar.tokens = tokens - strongSelf.searchBar.text = query + if let strongSelf = self, let searchBar = strongSelf.searchBar { + searchBar.prefixString = prefix + let previousTokens = searchBar.tokens + searchBar.tokens = tokens + searchBar.text = query if previousTokens.count < tokens.count && !isFirstTime { if let lastToken = tokens.last, !lastToken.permanent { - strongSelf.searchBar.selectLastToken() + searchBar.selectLastToken() } } isFirstTime = false } } if let placeholder = placeholder { - self.searchBar.placeholderString = NSAttributedString(string: placeholder, font: Font.regular(17.0), textColor: presentationData.theme.rootController.navigationSearchBar.inputPlaceholderTextColor) + self.searchBar?.placeholderString = NSAttributedString(string: placeholder, font: Font.regular(17.0), textColor: presentationData.theme.rootController.navigationSearchBar.inputPlaceholderTextColor) } self.contentNode.setPlaceholder = { [weak self] string in - guard string != self?.searchBar.placeholderString?.string else { + guard string != self?.searchBar?.placeholderString?.string else { return } - if let mutableAttributedString = self?.searchBar.placeholderString?.mutableCopy() as? NSMutableAttributedString { + if let mutableAttributedString = self?.searchBar?.placeholderString?.mutableCopy() as? NSMutableAttributedString { mutableAttributedString.mutableString.setString(string) - self?.searchBar.placeholderString = mutableAttributedString + self?.searchBar?.placeholderString = mutableAttributedString } } self.isSearchingDisposable = (contentNode.isSearching |> deliverOnMainQueue).start(next: { [weak self] value in - self?.searchBar.activity = value + self?.searchBar?.activity = value }) if self.contentNode.hasDim { @@ -113,9 +119,32 @@ public final class SearchDisplayController { self.backgroundNode.isTransparent = false } } + + public func setSearchBar(_ searchBar: SearchBarNode) { + self.searchBar = searchBar + + searchBar.textUpdated = { [weak contentNode] text, _ in + contentNode?.searchTextUpdated(text: text) + } + searchBar.tokensUpdated = { [weak contentNode] tokens in + contentNode?.searchTokensUpdated(tokens: tokens) + } + searchBar.cancel = { [weak self] in + self?.isDeactivating = true + self?.cancel() + } + searchBar.clearPrefix = { [weak contentNode] in + contentNode?.searchTextClearPrefix() + } + searchBar.clearTokens = { [weak contentNode] in + contentNode?.searchTextClearTokens() + } + } public func updatePresentationData(_ presentationData: PresentationData) { - self.searchBar.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: presentationData.theme, hasSeparator: self.hasSeparator, inline: self.inline), strings: presentationData.strings) + if !self.searchBarIsExternal { + self.searchBar?.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: presentationData.theme, hasSeparator: self.hasSeparator, inline: self.inline), presentationTheme: presentationData.theme, strings: presentationData.strings) + } self.contentNode.updatePresentationData(presentationData) if self.contentNode.hasDim { @@ -128,6 +157,8 @@ public final class SearchDisplayController { } public func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { + let defaultNavigationBarHeight = navigationBarHeight + let statusBarHeight: CGFloat = layout.statusBarHeight ?? 0.0 let searchBarHeight: CGFloat = max(20.0, statusBarHeight) + 44.0 let navigationBarOffset: CGFloat @@ -137,32 +168,44 @@ public final class SearchDisplayController { navigationBarOffset = 0.0 } var navigationBarFrame = CGRect(origin: CGPoint(x: 0.0, y: navigationBarOffset), size: CGSize(width: layout.size.width, height: searchBarHeight)) + if self.searchBarIsExternal { + navigationBarFrame.size.height -= 50.0 + } if layout.statusBarHeight == nil { navigationBarFrame.size.height = 64.0 } navigationBarFrame.size.height += 10.0 + var navigationBarHeight = navigationBarFrame.maxY - let searchBarFrame: CGRect - if case .navigation = self.mode { - searchBarFrame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: 54.0) - } else { - searchBarFrame = navigationBarFrame + if !self.searchBarIsExternal, let searchBar = self.searchBar { + let searchBarFrame: CGRect + if case .navigation = self.mode { + if case .glass = searchBar.fieldStyle { + navigationBarHeight = defaultNavigationBarHeight + searchBarFrame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: 44.0) + } else { + searchBarFrame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: 54.0) + } + } else { + searchBarFrame = navigationBarFrame + navigationBarHeight = navigationBarFrame.maxY + 8.0 + } + transition.updateFrame(node: searchBar, frame: searchBarFrame) + searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: transition) } - transition.updateFrame(node: self.searchBar, frame: searchBarFrame) - self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: transition) - self.containerLayout = (layout, navigationBarFrame.maxY) + self.containerLayout = (layout, navigationBarHeight) let bounds = CGRect(origin: CGPoint(), size: layout.size) - transition.updateFrame(node: self.backgroundNode, frame: bounds.insetBy(dx: -20.0, dy: -20.0)) + transition.updateFrame(node: self.backgroundNode, frame: bounds)//.insetBy(dx: -20.0, dy: -20.0)) - var size = layout.size - size.width += 20.0 * 2.0 - transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 20.0), size: size)) + let size = layout.size + //size.width += 20.0 * 2.0 + transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: size)) - var safeInsets = layout.safeInsets - safeInsets.left += 20.0 - safeInsets.right += 20.0 + let safeInsets = layout.safeInsets + //safeInsets.left += 20.0 + //safeInsets.right += 20.0 self.contentNode.containerLayoutUpdated(ContainerViewLayout(size: size, metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: layout.intrinsicInsets, safeInsets: safeInsets, additionalInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging, inVoiceOver: layout.inVoiceOver), navigationBarHeight: navigationBarHeight, transition: transition) } @@ -183,14 +226,14 @@ public final class SearchDisplayController { self.backgroundNode.isTransparent = false } - var size = layout.size - size.width += 20.0 * 2.0 + let size = layout.size + //size.width += 20.0 * 2.0 - self.contentNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 20.0), size: size) + self.contentNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: size) - var safeInsets = layout.safeInsets - safeInsets.left += 20.0 - safeInsets.right += 20.0 + let safeInsets = layout.safeInsets + //safeInsets.left += 20.0 + //safeInsets.right += 20.0 self.contentNode.containerLayoutUpdated(ContainerViewLayout(size: size, metrics: LayoutMetrics(), deviceMetrics: layout.deviceMetrics, intrinsicInsets: UIEdgeInsets(), safeInsets: safeInsets, additionalInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), navigationBarHeight: navigationBarHeight, transition: .immediate) var contentNavigationBarHeight = navigationBarHeight @@ -201,22 +244,22 @@ public final class SearchDisplayController { if !self.contentNode.hasDim { self.backgroundNode.alpha = 1.0 self.backgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, timingFunction: CAMediaTimingFunctionName.linear.rawValue) - - self.backgroundNode.layer.animateScale(from: 0.85, to: 1.0, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring) } - if !self.contentNode.hasDim { - if let placeholder = placeholder { - self.searchBar.placeholderString = placeholder.placeholderString - } - } else { - if let placeholder = placeholder { - let initialTextBackgroundFrame = placeholder.convert(placeholder.backgroundNode.frame, to: nil) - let contentNodePosition = self.backgroundNode.layer.position - if contentNode.animateBackgroundAppearance { - self.backgroundNode.layer.animatePosition(from: CGPoint(x: contentNodePosition.x, y: contentNodePosition.y + (initialTextBackgroundFrame.maxY + 8.0 - contentNavigationBarHeight)), to: contentNodePosition, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring) + if !self.searchBarIsExternal { + if !self.contentNode.hasDim { + if let placeholder = placeholder { + self.searchBar?.placeholderString = placeholder.placeholderString + } + } else { + if let placeholder = placeholder { + let initialTextBackgroundFrame = placeholder.convert(placeholder.backgroundView.frame, to: nil) + let contentNodePosition = self.backgroundNode.layer.position + if contentNode.animateBackgroundAppearance { + self.backgroundNode.layer.animatePosition(from: CGPoint(x: contentNodePosition.x, y: contentNodePosition.y + (initialTextBackgroundFrame.maxY + 8.0 - contentNavigationBarHeight)), to: contentNodePosition, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring) + } + self.searchBar?.placeholderString = placeholder.placeholderString } - self.searchBar.placeholderString = placeholder.placeholderString } } @@ -240,39 +283,49 @@ public final class SearchDisplayController { navigationBarFrame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: 54.0) } - self.searchBar.frame = navigationBarFrame - insertSubnode(self.searchBar, true) - self.searchBar.layout() - - if focus { - self.searchBar.activate() + if !self.searchBarIsExternal, let searchBar = self.searchBar { + searchBar.frame = navigationBarFrame + insertSubnode(searchBar, true) + searchBar.layout() + + if focus { + searchBar.activate() + } } + if let placeholder = placeholder { - self.searchBar.animateIn(from: placeholder, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring) + if !self.searchBarIsExternal, let searchBar = self.searchBar { + searchBar.animateIn(from: placeholder, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring) + } if self.contentNode.hasDim { self.contentNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue) } } else { - self.searchBar.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue) + if !self.searchBarIsExternal, let searchBar = self.searchBar { + searchBar.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue) + } self.contentNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue) } } - public func deactivate(placeholder: SearchBarPlaceholderNode?, animated: Bool = true) { - self.searchBar.deactivate(clear: false) + public func deactivate(placeholder: SearchBarPlaceholderNode?, animated: Bool = true, completion: (() -> Void)? = nil) { + if let searchBar = self.searchBar { + searchBar.deactivate(clear: false) + } - let searchBar = self.searchBar - if let placeholder = placeholder { - searchBar.transitionOut(to: placeholder, transition: animated ? .animated(duration: 0.5, curve: .spring) : .immediate, completion: { - [weak searchBar] in - searchBar?.removeFromSupernode() - }) - } else { - searchBar.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak searchBar] finished in - if finished { + if !self.searchBarIsExternal, let searchBar = self.searchBar { + if let placeholder = placeholder { + searchBar.transitionOut(to: placeholder, transition: animated ? .animated(duration: 0.5, curve: .spring) : .immediate, completion: { + [weak searchBar] in searchBar?.removeFromSupernode() - } - }) + }) + } else { + searchBar.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak searchBar] finished in + if finished { + searchBar?.removeFromSupernode() + } + }) + } } let backgroundNode = self.backgroundNode @@ -282,10 +335,12 @@ public final class SearchDisplayController { if finished { backgroundNode?.removeFromSupernode() } + completion?() }) } else { backgroundNode.removeFromSupernode() contentNode.removeFromSupernode() + completion?() } } diff --git a/submodules/SectionHeaderItem/Sources/SectionHeaderItem.swift b/submodules/SectionHeaderItem/Sources/SectionHeaderItem.swift index 37a1f4f9..2a5f15e6 100644 --- a/submodules/SectionHeaderItem/Sources/SectionHeaderItem.swift +++ b/submodules/SectionHeaderItem/Sources/SectionHeaderItem.swift @@ -73,7 +73,7 @@ private class SectionHeaderItemNode: ListViewItemNode { private var layoutParams: ListViewItemLayoutParams? required init() { - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) } override func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) { diff --git a/submodules/SettingsUI/BUILD b/submodules/SettingsUI/BUILD index 353a1ec8..06cc3d6d 100644 --- a/submodules/SettingsUI/BUILD +++ b/submodules/SettingsUI/BUILD @@ -23,7 +23,6 @@ swift_library( "//submodules/PresentationDataUtils:PresentationDataUtils", "//submodules/AvatarNode:AvatarNode", "//submodules/CallListUI:CallListUI", - "//submodules/ChatListSearchItemNode:ChatListSearchItemNode", "//submodules/ChatListSearchItemHeader:ChatListSearchItemHeader", "//submodules/ChatListUI:ChatListUI", "//submodules/ContactsPeerItem:ContactsPeerItem", @@ -129,9 +128,13 @@ swift_library( "//submodules/TelegramUI/Components/Settings/PasskeysScreen", "//submodules/TelegramUI/Components/FaceScanScreen", "//submodules/ComponentFlow", + "//submodules/Components/ComponentDisplayAdapters", "//submodules/Components/BundleIconComponent", "//submodules/TelegramUI/Components/ButtonComponent", "//submodules/TelegramUI/Components/SliderComponent", + "//submodules/TelegramUI/Components/AlertComponent", + "//submodules/TelegramUI/Components/AlertComponent/AlertInputFieldComponent", + "//submodules/TelegramUI/Components/EdgeEffect", ], visibility = [ "//visibility:public", diff --git a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift index 65965353..dfbb3230 100644 --- a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift +++ b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift @@ -320,7 +320,9 @@ final class BubbleSettingsController: ViewController { self.presentationData = context.sharedContext.currentPresentationData.with { $0 } self.presentationThemeSettings = presentationThemeSettings - super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationTheme: self.presentationData.theme, presentationStrings: self.presentationData.strings)) + super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationTheme: self.presentationData.theme, presentationStrings: self.presentationData.strings, style: .glass)) + + self._hasGlassStyle = true self.blocksBackgroundWhenInOverlay = true self.acceptsFocusWhenInOverlay = true diff --git a/submodules/SettingsUI/Sources/ChangePhoneNumberController.swift b/submodules/SettingsUI/Sources/ChangePhoneNumberController.swift index bda486bb..9a03e479 100644 --- a/submodules/SettingsUI/Sources/ChangePhoneNumberController.swift +++ b/submodules/SettingsUI/Sources/ChangePhoneNumberController.swift @@ -48,7 +48,7 @@ public func ChangePhoneNumberController(context: AccountContext) -> ViewControll controller?.inProgress = false var dismissImpl: (() -> Void)? - let codeController = AuthorizationSequenceCodeEntryController(presentationData: presentationData, back: { + let codeController = AuthorizationSequenceCodeEntryController(sharedContext: context.sharedContext, presentationData: presentationData, back: { dismissImpl?() }) codeController.loginWithCode = { [weak codeController] code in @@ -109,7 +109,7 @@ public func ChangePhoneNumberController(context: AccountContext) -> ViewControll let mnc = carrier.mobileNetworkCode ?? "none" let _ = context.engine.auth.reportMissingCode(phoneNumber: phoneNumber, phoneCodeHash: next.hash, mnc: mnc).start() - AuthorizationSequenceController.presentDidNotGetCodeUI(controller: codeController, presentationData: context.sharedContext.currentPresentationData.with({ $0 }), phoneNumber: phoneNumber, mnc: mnc) + AuthorizationSequenceController.presentDidNotGetCodeUI(sharedContext: context.sharedContext, controller: codeController, presentationData: context.sharedContext.currentPresentationData.with({ $0 }), phoneNumber: phoneNumber, mnc: mnc) } codeController.openFragment = { url in context.sharedContext.applicationBindings.openUrl(url) @@ -154,7 +154,15 @@ public func ChangePhoneNumberController(context: AccountContext) -> ViewControll controller?.view.window?.rootViewController?.present(composeController, animated: true, completion: nil) } else { - controller?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.Login_EmailNotConfiguredError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + let alertController = textAlertController( + context: context, + title: nil, + text: presentationData.strings.Login_EmailNotConfiguredError, + actions: [ + TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {}) + ] + ) + controller?.present(alertController, in: .window(.root)) } })) case .generic: diff --git a/submodules/SettingsUI/Sources/Data and Storage/AutodownloadDataUsagePickerItem.swift b/submodules/SettingsUI/Sources/Data and Storage/AutodownloadDataUsagePickerItem.swift index 735565cf..acb2d8f7 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/AutodownloadDataUsagePickerItem.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/AutodownloadDataUsagePickerItem.swift @@ -133,7 +133,7 @@ private final class AutodownloadDataUsagePickerItemNode: ListViewItemNode { self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.lowTextNode) self.addSubnode(self.mediumTextNode) diff --git a/submodules/SettingsUI/Sources/Data and Storage/AutodownloadSizeLimitItem.swift b/submodules/SettingsUI/Sources/Data and Storage/AutodownloadSizeLimitItem.swift index affc44c9..280d7365 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/AutodownloadSizeLimitItem.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/AutodownloadSizeLimitItem.swift @@ -143,7 +143,7 @@ private final class AutodownloadSizeLimitItemNode: ListViewItemNode { self.maxTextNode.isUserInteractionEnabled = false self.maxTextNode.displaysAsynchronously = false - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.textNode) self.addSubnode(self.minTextNode) diff --git a/submodules/SettingsUI/Sources/Data and Storage/CalculatingCacheSizeItem.swift b/submodules/SettingsUI/Sources/Data and Storage/CalculatingCacheSizeItem.swift index 2d6531d3..2f86442b 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/CalculatingCacheSizeItem.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/CalculatingCacheSizeItem.swift @@ -90,7 +90,7 @@ private final class CalculatingCacheSizeItemNode: ListViewItemNode { self.titleNode.contentMode = .left self.titleNode.contentsScale = UIScreen.main.scale - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.titleNode) } diff --git a/submodules/SettingsUI/Sources/Data and Storage/EnergyUsageBatteryLevelItem.swift b/submodules/SettingsUI/Sources/Data and Storage/EnergyUsageBatteryLevelItem.swift index 84ce6fb2..831492c2 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/EnergyUsageBatteryLevelItem.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/EnergyUsageBatteryLevelItem.swift @@ -112,7 +112,7 @@ class EnergyUsageBatteryLevelItemNode: ListViewItemNode { self.batteryBackgroundNode = ASImageNode() self.batteryForegroundNode = ASImageNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.leftTextNode) self.addSubnode(self.rightTextNode) diff --git a/submodules/SettingsUI/Sources/Data and Storage/KeepMediaDurationPickerItem.swift b/submodules/SettingsUI/Sources/Data and Storage/KeepMediaDurationPickerItem.swift index 69cf7884..a62039c1 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/KeepMediaDurationPickerItem.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/KeepMediaDurationPickerItem.swift @@ -107,7 +107,7 @@ private final class KeepMediaDurationPickerItemNode: ListViewItemNode { } self.textNodes = textNodes - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) for textNode in textNodes { self.addSubnode(textNode) diff --git a/submodules/SettingsUI/Sources/Data and Storage/MaximumCacheSizePickerItem.swift b/submodules/SettingsUI/Sources/Data and Storage/MaximumCacheSizePickerItem.swift index 6d6d7257..25c1db6d 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/MaximumCacheSizePickerItem.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/MaximumCacheSizePickerItem.swift @@ -122,7 +122,7 @@ private final class MaximumCacheSizePickerItemNode: ListViewItemNode { } self.textNodes = textNodes - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) for textNode in textNodes { self.addSubnode(textNode) diff --git a/submodules/SettingsUI/Sources/Data and Storage/ProxyServerActionSheetController.swift b/submodules/SettingsUI/Sources/Data and Storage/ProxyServerActionSheetController.swift index f4c1aee4..bcc32f58 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/ProxyServerActionSheetController.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/ProxyServerActionSheetController.swift @@ -14,6 +14,7 @@ import PresentationDataUtils import UrlEscaping public final class ProxyServerActionSheetController: ActionSheetController { + private let sharedContext: SharedAccountContext private var presentationDisposable: Disposable? private let _ready = Promise() @@ -25,10 +26,11 @@ public final class ProxyServerActionSheetController: ActionSheetController { convenience public init(context: AccountContext, server: ProxyServerSettings) { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - self.init(presentationData: presentationData, accountManager: context.sharedContext.accountManager, postbox: context.account.postbox, network: context.account.network, server: server, updatedPresentationData: context.sharedContext.presentationData) + self.init(sharedContext: context.sharedContext, presentationData: presentationData, accountManager: context.sharedContext.accountManager, postbox: context.account.postbox, network: context.account.network, server: server, updatedPresentationData: context.sharedContext.presentationData) } - public init(presentationData: PresentationData, accountManager: AccountManager, postbox: Postbox, network: Network, server: ProxyServerSettings, updatedPresentationData: Signal?) { + public init(sharedContext: SharedAccountContext, presentationData: PresentationData, accountManager: AccountManager, postbox: Postbox, network: Network, server: ProxyServerSettings, updatedPresentationData: Signal?) { + self.sharedContext = sharedContext let sheetTheme = ActionSheetControllerTheme(presentationData: presentationData) super.init(theme: sheetTheme) @@ -39,7 +41,7 @@ public final class ProxyServerActionSheetController: ActionSheetController { items.append(ActionSheetTextItem(title: presentationData.strings.SocksProxySetup_AdNoticeHelp)) } items.append(ProxyServerInfoItem(strings: presentationData.strings, network: network, server: server)) - items.append(ProxyServerActionItem(accountManager:accountManager, postbox: postbox, network: network, presentationData: presentationData, server: server, dismiss: { [weak self] success in + items.append(ProxyServerActionItem(sharedContext: sharedContext, accountManager:accountManager, postbox: postbox, network: network, presentationData: presentationData, server: server, dismiss: { [weak self] success in guard let strongSelf = self, !strongSelf.isDismissed else { return } @@ -262,6 +264,7 @@ private final class ProxyServerInfoItemNode: ActionSheetItemNode { } private final class ProxyServerActionItem: ActionSheetItem { + private let sharedContext: SharedAccountContext private let accountManager: AccountManager private let postbox: Postbox private let network: Network @@ -270,7 +273,8 @@ private final class ProxyServerActionItem: ActionSheetItem { private let dismiss: (Bool) -> Void private let present: (ViewController, Any?) -> Void - init(accountManager: AccountManager, postbox: Postbox, network: Network, presentationData: PresentationData, server: ProxyServerSettings, dismiss: @escaping (Bool) -> Void, present: @escaping (ViewController, Any?) -> Void) { + init(sharedContext: SharedAccountContext, accountManager: AccountManager, postbox: Postbox, network: Network, presentationData: PresentationData, server: ProxyServerSettings, dismiss: @escaping (Bool) -> Void, present: @escaping (ViewController, Any?) -> Void) { + self.sharedContext = sharedContext self.accountManager = accountManager self.postbox = postbox self.network = network @@ -281,7 +285,7 @@ private final class ProxyServerActionItem: ActionSheetItem { } func node(theme: ActionSheetControllerTheme) -> ActionSheetItemNode { - return ProxyServerActionItemNode(accountManager: self.accountManager, postbox: self.postbox, network: self.network, presentationData: self.presentationData, theme: theme, server: self.server, dismiss: self.dismiss, present: self.present) + return ProxyServerActionItemNode(sharedContext: self.sharedContext, accountManager: self.accountManager, postbox: self.postbox, network: self.network, presentationData: self.presentationData, theme: theme, server: self.server, dismiss: self.dismiss, present: self.present) } func updateNode(_ node: ActionSheetItemNode) { @@ -289,6 +293,7 @@ private final class ProxyServerActionItem: ActionSheetItem { } private final class ProxyServerActionItemNode: ActionSheetItemNode { + private let sharedContext: SharedAccountContext private let accountManager: AccountManager private let postbox: Postbox private let network: Network @@ -305,7 +310,8 @@ private final class ProxyServerActionItemNode: ActionSheetItemNode { private let disposable = MetaDisposable() private var revertSettings: ProxySettings? - init(accountManager: AccountManager, postbox: Postbox, network: Network, presentationData: PresentationData, theme: ActionSheetControllerTheme, server: ProxyServerSettings, dismiss: @escaping (Bool) -> Void, present: @escaping (ViewController, Any?) -> Void) { + init(sharedContext: SharedAccountContext, accountManager: AccountManager, postbox: Postbox, network: Network, presentationData: PresentationData, theme: ActionSheetControllerTheme, server: ProxyServerSettings, dismiss: @escaping (Bool) -> Void, present: @escaping (ViewController, Any?) -> Void) { + self.sharedContext = sharedContext self.accountManager = accountManager self.postbox = postbox self.network = network @@ -430,7 +436,7 @@ private final class ProxyServerActionItemNode: ActionSheetItemNode { strongSelf.buttonNode.isUserInteractionEnabled = true strongSelf.requestLayoutUpdate() - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.SocksProxySetup_FailedToConnect, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), nil) + strongSelf.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: strongSelf.presentationData.strings.SocksProxySetup_FailedToConnect, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), nil) } } })) diff --git a/submodules/SettingsUI/Sources/Data and Storage/ProxySettingsActionItem.swift b/submodules/SettingsUI/Sources/Data and Storage/ProxySettingsActionItem.swift index 6b3e7849..e1f086ab 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/ProxySettingsActionItem.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/ProxySettingsActionItem.swift @@ -114,7 +114,7 @@ private final class ProxySettingsActionItemNode: ListViewItemNode { self.highlightedBackgroundNode = ASDisplayNode() self.highlightedBackgroundNode.isLayerBacked = true - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.isAccessibilityElement = true diff --git a/submodules/SettingsUI/Sources/Data and Storage/ProxySettingsServerItem.swift b/submodules/SettingsUI/Sources/Data and Storage/ProxySettingsServerItem.swift index da7512dd..3108d649 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/ProxySettingsServerItem.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/ProxySettingsServerItem.swift @@ -174,7 +174,7 @@ private final class ProxySettingsServerItemNode: ItemListRevealOptionsItemNode { self.highlightedBackgroundNode = ASDisplayNode() self.highlightedBackgroundNode.isLayerBacked = true - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.titleNode) self.addSubnode(self.statusNode) diff --git a/submodules/SettingsUI/Sources/Data and Storage/StorageUsageItem.swift b/submodules/SettingsUI/Sources/Data and Storage/StorageUsageItem.swift index e50a2c31..7a085e60 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/StorageUsageItem.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/StorageUsageItem.swift @@ -119,7 +119,7 @@ private final class StorageUsageItemNode: ListViewItemNode { self.lineNodes = [] self.descriptionNodes = [] - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.lineMaskNode) } diff --git a/submodules/SettingsUI/Sources/Data and Storage/WebBrowserDomainController.swift b/submodules/SettingsUI/Sources/Data and Storage/WebBrowserDomainController.swift index 4d62514a..d75618da 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/WebBrowserDomainController.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/WebBrowserDomainController.swift @@ -7,465 +7,99 @@ import TelegramCore import TelegramPresentationData import AccountContext import UrlEscaping -import ActivityIndicator +import ComponentFlow +import AlertComponent +import AlertInputFieldComponent -private final class WebBrowserDomainInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegate { - private var theme: PresentationTheme - private let backgroundNode: ASImageNode - fileprivate let textInputNode: EditableTextNode - private let placeholderNode: ASTextNode +public func webBrowserDomainController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, apply: @escaping (String?) -> Void) -> ViewController { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let strings = presentationData.strings - var updateHeight: (() -> Void)? - var complete: (() -> Void)? - var textChanged: ((String) -> Void)? + let inputState = AlertInputFieldComponent.ExternalState() - private let backgroundInsets = UIEdgeInsets(top: 8.0, left: 16.0, bottom: 15.0, right: 16.0) - private let inputInsets = UIEdgeInsets(top: 5.0, left: 12.0, bottom: 5.0, right: 12.0) - - var text: String { - get { - return self.textInputNode.attributedText?.string ?? "" - } - set { - self.textInputNode.attributedText = NSAttributedString(string: newValue, font: Font.regular(17.0), textColor: self.theme.actionSheet.inputTextColor) - self.placeholderNode.isHidden = !newValue.isEmpty - } + let doneIsEnabled: Signal = inputState.valueSignal + |> map { value in + return !value.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty } - var placeholder: String = "" { - didSet { - self.placeholderNode.attributedText = NSAttributedString(string: self.placeholder, font: Font.regular(17.0), textColor: self.theme.actionSheet.inputPlaceholderColor) - } - } - - init(theme: PresentationTheme, placeholder: String) { - self.theme = theme + let doneInProgressPromise = ValuePromise(false) + + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: strings.WebBrowser_Exceptions_Create_Title) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(strings.WebBrowser_Exceptions_Create_Text)) + ) + )) - self.backgroundNode = ASImageNode() - self.backgroundNode.isLayerBacked = true - self.backgroundNode.displaysAsynchronously = false - self.backgroundNode.displayWithoutProcessing = true - self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 12.0, color: theme.actionSheet.inputHollowBackgroundColor, strokeColor: theme.actionSheet.inputBorderColor, strokeWidth: 1.0) - - self.textInputNode = EditableTextNode() - self.textInputNode.typingAttributes = [NSAttributedString.Key.font.rawValue: Font.regular(17.0), NSAttributedString.Key.foregroundColor.rawValue: theme.actionSheet.inputTextColor] - self.textInputNode.clipsToBounds = true - self.textInputNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0) - self.textInputNode.textContainerInset = UIEdgeInsets(top: self.inputInsets.top, left: 0.0, bottom: self.inputInsets.bottom, right: 0.0) - self.textInputNode.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance - self.textInputNode.keyboardType = .URL - self.textInputNode.autocapitalizationType = .none - self.textInputNode.returnKeyType = .done - self.textInputNode.autocorrectionType = .no - self.textInputNode.tintColor = theme.actionSheet.controlAccentColor - - self.placeholderNode = ASTextNode() - self.placeholderNode.isUserInteractionEnabled = false - self.placeholderNode.displaysAsynchronously = false - self.placeholderNode.attributedText = NSAttributedString(string: placeholder, font: Font.regular(17.0), textColor: self.theme.actionSheet.inputPlaceholderColor) - - super.init() - - self.textInputNode.delegate = self - - self.addSubnode(self.backgroundNode) - self.addSubnode(self.textInputNode) - self.addSubnode(self.placeholderNode) - } - - func updateTheme(_ theme: PresentationTheme) { - self.theme = theme - - self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 12.0, color: self.theme.actionSheet.inputHollowBackgroundColor, strokeColor: self.theme.actionSheet.inputBorderColor, strokeWidth: 1.0) - self.textInputNode.keyboardAppearance = self.theme.rootController.keyboardColor.keyboardAppearance - self.placeholderNode.attributedText = NSAttributedString(string: self.placeholderNode.attributedText?.string ?? "", font: Font.regular(17.0), textColor: self.theme.actionSheet.inputPlaceholderColor) - self.textInputNode.tintColor = self.theme.actionSheet.controlAccentColor - } - - func updateLayout(width: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat { - let backgroundInsets = self.backgroundInsets - let inputInsets = self.inputInsets - - let textFieldHeight = self.calculateTextFieldMetrics(width: width) - let panelHeight = textFieldHeight + backgroundInsets.top + backgroundInsets.bottom - - let backgroundFrame = CGRect(origin: CGPoint(x: backgroundInsets.left, y: backgroundInsets.top), size: CGSize(width: width - backgroundInsets.left - backgroundInsets.right, height: panelHeight - backgroundInsets.top - backgroundInsets.bottom)) - transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame) - - let placeholderSize = self.placeholderNode.measure(backgroundFrame.size) - transition.updateFrame(node: self.placeholderNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + inputInsets.left, y: backgroundFrame.minY + floor((backgroundFrame.size.height - placeholderSize.height) / 2.0)), size: placeholderSize)) - - transition.updateFrame(node: self.textInputNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + inputInsets.left, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.size.width - inputInsets.left - inputInsets.right, height: backgroundFrame.size.height))) - - return panelHeight - } - - func activateInput() { - self.textInputNode.becomeFirstResponder() - } - - func deactivateInput() { - self.textInputNode.resignFirstResponder() - } - - @objc func editableTextNodeDidUpdateText(_ editableTextNode: ASEditableTextNode) { - self.updateTextNodeText(animated: true) - self.textChanged?(editableTextNode.textView.text) - self.placeholderNode.isHidden = !(editableTextNode.textView.text ?? "").isEmpty - } - - private let domainRegex = try? NSRegularExpression(pattern: "^(https?://)?([a-zA-Z0-9-]+\\.?)*([a-zA-Z]*)?(:)?(/)?$", options: []) - private let pathRegex = try? NSRegularExpression(pattern: "^(https?://)?([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}/", options: []) - - var inProgress = false - - func editableTextNode(_ editableTextNode: ASEditableTextNode, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { - if self.inProgress { - return false - } - if text == "\n" { - self.complete?() - return false - } - - if let domainRegex = self.domainRegex, let pathRegex = self.pathRegex { - let updatedText = (editableTextNode.textView.text as NSString).replacingCharacters(in: range, with: text) - let domainMatches = domainRegex.matches(in: updatedText, options: [], range: NSRange(location: 0, length: updatedText.utf16.count)) - let pathMatches = pathRegex.matches(in: updatedText, options: [], range: NSRange(location: 0, length: updatedText.utf16.count)) - - if domainMatches.count > 0, pathMatches.count == 0 { - return true - } else { - return false - } - } - - return true - } - - private func calculateTextFieldMetrics(width: CGFloat) -> CGFloat { - let backgroundInsets = self.backgroundInsets - let inputInsets = self.inputInsets - - let unboundTextFieldHeight = max(33.0, ceil(self.textInputNode.measure(CGSize(width: width - backgroundInsets.left - backgroundInsets.right - inputInsets.left - inputInsets.right, height: CGFloat.greatestFiniteMagnitude)).height)) - - return min(61.0, max(33.0, unboundTextFieldHeight)) - } - - private func updateTextNodeText(animated: Bool) { - let backgroundInsets = self.backgroundInsets - - let textFieldHeight = self.calculateTextFieldMetrics(width: self.bounds.size.width) - - let panelHeight = textFieldHeight + backgroundInsets.top + backgroundInsets.bottom - if !self.bounds.size.height.isEqual(to: panelHeight) { - self.updateHeight?() - } - } - - @objc func clearPressed() { - self.textInputNode.attributedText = nil - self.deactivateInput() - } -} - -private final class WebBrowserDomainAlertContentNode: AlertContentNode { - private let strings: PresentationStrings - - private let titleNode: ASTextNode - private let textNode: ASTextNode - let activityIndicator: ActivityIndicator - let inputFieldNode: WebBrowserDomainInputFieldNode - - private let actionNodesSeparator: ASDisplayNode - private let actionNodes: [TextAlertContentActionNode] - private let actionVerticalSeparators: [ASDisplayNode] - - private let disposable = MetaDisposable() - - private var validLayout: CGSize? - - private let hapticFeedback = HapticFeedback() - - var complete: (() -> Void)? { - didSet { - self.inputFieldNode.complete = self.complete - } - } - - override var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled - } - - init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction]) { - self.strings = strings - - self.titleNode = ASTextNode() - self.titleNode.maximumNumberOfLines = 2 - self.textNode = ASTextNode() - self.textNode.maximumNumberOfLines = 2 - - self.activityIndicator = ActivityIndicator(type: .custom(ptheme.rootController.navigationBar.secondaryTextColor, 20.0, 1.5, false), speed: .slow) - self.activityIndicator.isHidden = true - - self.inputFieldNode = WebBrowserDomainInputFieldNode(theme: ptheme, placeholder: strings.WebBrowser_Exceptions_Create_Placeholder) - self.inputFieldNode.text = "" - - self.actionNodesSeparator = ASDisplayNode() - self.actionNodesSeparator.isLayerBacked = true - - self.actionNodes = actions.map { action -> TextAlertContentActionNode in - return TextAlertContentActionNode(theme: theme, action: action) - } - - var actionVerticalSeparators: [ASDisplayNode] = [] - if actions.count > 1 { - for _ in 0 ..< actions.count - 1 { - let separatorNode = ASDisplayNode() - separatorNode.isLayerBacked = true - actionVerticalSeparators.append(separatorNode) - } - } - self.actionVerticalSeparators = actionVerticalSeparators - - super.init() - - self.addSubnode(self.titleNode) - self.addSubnode(self.textNode) - - self.addSubnode(self.inputFieldNode) - self.addSubnode(self.activityIndicator) - - self.addSubnode(self.actionNodesSeparator) - - for actionNode in self.actionNodes { - self.addSubnode(actionNode) - } - self.actionNodes.last?.actionEnabled = false - - for separatorNode in self.actionVerticalSeparators { - self.addSubnode(separatorNode) - } - - self.inputFieldNode.updateHeight = { [weak self] in - if let strongSelf = self { - if let _ = strongSelf.validLayout { - strongSelf.requestLayout?(.animated(duration: 0.15, curve: .spring)) - } - } - } - - self.inputFieldNode.textChanged = { [weak self] text in - if let strongSelf = self, let lastNode = strongSelf.actionNodes.last { - lastNode.actionEnabled = !text.isEmpty - } - } - - self.updateTheme(theme) - } - - deinit { - self.disposable.dispose() - } - - var link: String { - return self.inputFieldNode.text - } - - override func updateTheme(_ theme: AlertControllerTheme) { - self.titleNode.attributedText = NSAttributedString(string: self.strings.WebBrowser_Exceptions_Create_Title, font: Font.bold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center) - self.textNode.attributedText = NSAttributedString(string: self.strings.WebBrowser_Exceptions_Create_Text, font: Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center) - - self.actionNodesSeparator.backgroundColor = theme.separatorColor - for actionNode in self.actionNodes { - actionNode.updateTheme(theme) - } - for separatorNode in self.actionVerticalSeparators { - separatorNode.backgroundColor = theme.separatorColor - } - - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) - } - } - - override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - var size = size - size.width = min(size.width, 270.0) - let measureSize = CGSize(width: size.width - 16.0 * 2.0, height: CGFloat.greatestFiniteMagnitude) - - let hadValidLayout = self.validLayout != nil - - self.validLayout = size - - var origin: CGPoint = CGPoint(x: 0.0, y: 20.0) - let spacing: CGFloat = 5.0 - - let titleSize = self.titleNode.measure(measureSize) - transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize)) - origin.y += titleSize.height + 4.0 - - let textSize = self.textNode.measure(measureSize) - transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize)) - origin.y += textSize.height + 6.0 + spacing - - let actionButtonHeight: CGFloat = 44.0 - var minActionsWidth: CGFloat = 0.0 - let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) - let actionTitleInsets: CGFloat = 8.0 - - var effectiveActionLayout = TextAlertContentActionLayout.horizontal - for actionNode in self.actionNodes { - let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) - if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 { - effectiveActionLayout = .vertical - } - switch effectiveActionLayout { - case .horizontal: - minActionsWidth += actionTitleSize.width + actionTitleInsets - case .vertical: - minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) - } - } - - let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 9.0, right: 18.0) - - var contentWidth = max(titleSize.width, minActionsWidth) - contentWidth = max(contentWidth, 234.0) - - var actionsHeight: CGFloat = 0.0 - switch effectiveActionLayout { - case .horizontal: - actionsHeight = actionButtonHeight - case .vertical: - actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) - } - - let resultWidth = contentWidth + insets.left + insets.right - - let inputFieldWidth = resultWidth - let inputFieldHeight = self.inputFieldNode.updateLayout(width: inputFieldWidth, transition: transition) - let inputHeight = inputFieldHeight - let inputFrame = CGRect(x: 0.0, y: origin.y, width: resultWidth, height: inputFieldHeight) - transition.updateFrame(node: self.inputFieldNode, frame: inputFrame) - transition.updateAlpha(node: self.inputFieldNode, alpha: inputHeight > 0.0 ? 1.0 : 0.0) - - let activitySize = CGSize(width: 20.0, height: 20.0) - transition.updateFrame(node: self.activityIndicator, frame: CGRect(origin: CGPoint(x: inputFrame.maxX - activitySize.width - 23.0, y: inputFrame.midY - activitySize.height / 2.0 - 3.0), size: activitySize)) - - let resultSize = CGSize(width: resultWidth, height: titleSize.height + textSize.height + spacing + inputHeight + actionsHeight + insets.top + insets.bottom) - - transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - - var actionOffset: CGFloat = 0.0 - let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) - var separatorIndex = -1 - var nodeIndex = 0 - for actionNode in self.actionNodes { - if separatorIndex >= 0 { - let separatorNode = self.actionVerticalSeparators[separatorIndex] - switch effectiveActionLayout { - case .horizontal: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) - case .vertical: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - } - } - separatorIndex += 1 - - let currentActionWidth: CGFloat - switch effectiveActionLayout { - case .horizontal: - if nodeIndex == self.actionNodes.count - 1 { - currentActionWidth = resultSize.width - actionOffset - } else { - currentActionWidth = actionWidth - } - case .vertical: - currentActionWidth = resultSize.width - } - - let actionNodeFrame: CGRect - switch effectiveActionLayout { - case .horizontal: - actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += currentActionWidth - case .vertical: - actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += actionButtonHeight - } - - transition.updateFrame(node: actionNode, frame: actionNodeFrame) - - nodeIndex += 1 - } - - if !hadValidLayout { - self.inputFieldNode.activateInput() - } - - return resultSize - } - - func animateError() { - self.inputFieldNode.layer.addShakeAnimation() - self.hapticFeedback.error() - } -} - -public func webBrowserDomainController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, apply: @escaping (String?) -> Void) -> AlertController { - let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } - - var dismissImpl: ((Bool) -> Void)? + let domainRegex = try? NSRegularExpression(pattern: "^(https?://)?([a-zA-Z0-9-]+\\.?)*([a-zA-Z]*)?(:)?(/)?$", options: []) + let pathRegex = try? NSRegularExpression(pattern: "^(https?://)?([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}/", options: []) var applyImpl: (() -> Void)? + content.append(AnyComponentWithIdentity( + id: "input", + component: AnyComponent( + AlertInputFieldComponent( + context: context, + initialValue: nil, + placeholder: strings.WebBrowser_Exceptions_Create_Placeholder, + characterLimit: nil, + hasClearButton: true, + keyboardType: .URL, + autocapitalizationType: .none, + autocorrectionType: .no, + isInitiallyFocused: true, + externalState: inputState, + shouldChangeText: { updatedText in + guard let domainRegex, let pathRegex else { + return true + } + let domainMatches = domainRegex.matches(in: updatedText, options: [], range: NSRange(location: 0, length: updatedText.utf16.count)) + let pathMatches = pathRegex.matches(in: updatedText, options: [], range: NSRange(location: 0, length: updatedText.utf16.count)) + if domainMatches.count > 0, pathMatches.count == 0 { + return true + } else { + return false + } + }, + returnKeyAction: { + applyImpl?() + } + ) + ) + )) - var inProgress = false - let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - if !inProgress { - dismissImpl?(true) - apply(nil) - } - }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Done, action: { - if !inProgress { - applyImpl?() - } - })] - - let contentNode = WebBrowserDomainAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: actions) - contentNode.complete = { - applyImpl?() + var effectiveUpdatedPresentationData: (PresentationData, Signal) + if let updatedPresentationData { + effectiveUpdatedPresentationData = updatedPresentationData + } else { + effectiveUpdatedPresentationData = (presentationData, context.sharedContext.presentationData) } - applyImpl = { [weak contentNode] in - guard let contentNode = contentNode else { - return - } - inProgress = true - contentNode.inputFieldNode.inProgress = true - contentNode.activityIndicator.isHidden = false - - let updatedLink = explicitUrl(contentNode.link) + + let alertController = AlertScreen( + configuration: AlertScreen.Configuration(allowInputInset: true), + content: content, + actions: [ + .init(title: strings.Common_Cancel), + .init(title: strings.Common_Done, type: .default, action: { + applyImpl?() + }, autoDismiss: false, isEnabled: doneIsEnabled, progress: doneInProgressPromise.get()) + ], + updatedPresentationData: effectiveUpdatedPresentationData + ) + applyImpl = { + let updatedLink = explicitUrl(inputState.value) if !updatedLink.isEmpty && isValidUrl(updatedLink, validSchemes: ["http": true, "https": true]) { + doneInProgressPromise.set(true) apply(updatedLink) } else { - contentNode.animateError() + inputState.animateError() } } - - let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode) - let presentationDataDisposable = (updatedPresentationData?.signal ?? context.sharedContext.presentationData).start(next: { [weak controller, weak contentNode] presentationData in - controller?.theme = AlertControllerTheme(presentationData: presentationData) - contentNode?.inputFieldNode.updateTheme(presentationData.theme) - }) - controller.dismissed = { _ in - presentationDataDisposable.dispose() - } - dismissImpl = { [weak controller] animated in - contentNode.inputFieldNode.deactivateInput() - if animated { - controller?.dismissAnimated() - } else { - controller?.dismiss() - } - } - return controller + return alertController } diff --git a/submodules/SettingsUI/Sources/Data and Storage/WebBrowserDomainExceptionItem.swift b/submodules/SettingsUI/Sources/Data and Storage/WebBrowserDomainExceptionItem.swift index 2ba71084..29197201 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/WebBrowserDomainExceptionItem.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/WebBrowserDomainExceptionItem.swift @@ -132,7 +132,7 @@ final class WebBrowserDomainExceptionItemNode: ItemListRevealOptionsItemNode, It self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.iconNode) self.addSubnode(self.titleNode) diff --git a/submodules/SettingsUI/Sources/Data and Storage/WebBrowserItem.swift b/submodules/SettingsUI/Sources/Data and Storage/WebBrowserItem.swift index 410254c4..ecaa1035 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/WebBrowserItem.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/WebBrowserItem.swift @@ -117,7 +117,7 @@ private final class WebBrowserItemNode: ListViewItemNode { self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.iconNode) self.addSubnode(self.checkIconNode) diff --git a/submodules/SettingsUI/Sources/Data and Storage/WebBrowserSettingsController.swift b/submodules/SettingsUI/Sources/Data and Storage/WebBrowserSettingsController.swift index c66c0a99..85fd2174 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/WebBrowserSettingsController.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/WebBrowserSettingsController.swift @@ -419,7 +419,7 @@ public func webBrowserSettingsController(context: AccountContext) -> ViewControl }) dismissImpl = { [weak linkController] in linkController?.view.endEditing(true) - linkController?.dismissAnimated() + linkController?.dismiss(completion: nil) } controller?.present(linkController, in: .window(.root)) } diff --git a/submodules/SettingsUI/Sources/DeleteAccountOptionsController.swift b/submodules/SettingsUI/Sources/DeleteAccountOptionsController.swift index df1a0039..7cd58def 100644 --- a/submodules/SettingsUI/Sources/DeleteAccountOptionsController.swift +++ b/submodules/SettingsUI/Sources/DeleteAccountOptionsController.swift @@ -16,6 +16,7 @@ import AccountUtils import PremiumUI import PasswordSetupUI import StorageUsageScreen +import AlertComponent private struct DeleteAccountOptionsArguments { let changePhoneNumber: () -> Void @@ -102,43 +103,43 @@ private enum DeleteAccountOptionsEntry: ItemListNodeEntry, Equatable { let arguments = arguments as! DeleteAccountOptionsArguments switch self { case let .changePhoneNumber(_, title, text): - return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.changePhoneNumber, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { + return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesSettings.changePhoneNumber, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { arguments.changePhoneNumber() }) case let .addAccount(_, title, text): - return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.deleteAddAccount, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { + return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesSettings.deleteAddAccount, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { arguments.addAccount() }) case let .changePrivacy(_, title, text): - return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.security, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { + return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesSettings.security, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { arguments.setupPrivacy() }) case let .setTwoStepAuth(_, title, text): - return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.deleteSetTwoStepAuth, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { + return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesSettings.deleteSetTwoStepAuth, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { arguments.setupTwoStepAuth() }) case let .setPasscode(_, title, text): - return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.deleteSetPasscode, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { + return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesSettings.deleteSetPasscode, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { arguments.setPasscode() }) case let .clearCache(_, title, text): - return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.dataAndStorage, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { + return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesSettings.dataAndStorage, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { arguments.clearCache() }) case let .clearSyncedContacts(_, title, text): - return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.clearSynced, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { + return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesSettings.clearSynced, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { arguments.clearSyncedContacts() }) case let .deleteChats(_, title, text): - return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.deleteChats, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { + return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesSettings.deleteChats, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { arguments.deleteChats() }) case let .contactSupport(_, title, text): - return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.support, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { + return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, icon: PresentationResourcesSettings.support, title: title, label: text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, disclosureStyle: .arrow, action: { arguments.contactSupport() }) case let .deleteAccount(_, title): - return ItemListActionItem(presentationData: presentationData, title: title, kind: .destructive, alignment: .natural, sectionId: self.section, style: .blocks, action: { + return ItemListActionItem(presentationData: presentationData, systemStyle: .glass, title: title, kind: .destructive, alignment: .natural, sectionId: self.section, style: .blocks, action: { arguments.deleteAccount() }) } @@ -362,32 +363,36 @@ public func deleteAccountOptionsController(context: AccountContext, navigationCo }, dismissInput: {}, contentContext: nil, progress: nil, completion: nil) }) } - - let alertController = textAlertController(context: context, title: nil, text: presentationData.strings.Settings_FAQ_Intro, actions: [ - TextAlertAction(type: .genericAction, title: presentationData.strings.Settings_FAQ_Button, action: { - openFaq(resolvedUrlPromise) - dismissImpl?() - }), - TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { - supportPeerDisposable.set((supportPeer.get() - |> take(1) - |> deliverOnMainQueue).start(next: { peerId in - guard let peerId = peerId else { - return - } - let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) - |> deliverOnMainQueue).start(next: { peer in - guard let peer = peer else { + let alertController = AlertScreen( + context: context, + title: nil, + text: presentationData.strings.Settings_FAQ_Intro, + actions: [ + .init(title: presentationData.strings.Settings_FAQ_Button, action: { + openFaq(resolvedUrlPromise) + dismissImpl?() + }), + .init(title: presentationData.strings.Common_OK, type: .default, action: { + supportPeerDisposable.set((supportPeer.get() + |> take(1) + |> deliverOnMainQueue).start(next: { peerId in + guard let peerId else { return } - if let navigationController = navigationController { - dismissImpl?() - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer))) - } - }) - })) - }) - ]) + let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) + |> deliverOnMainQueue).start(next: { peer in + guard let peer else { + return + } + if let navigationController = navigationController { + dismissImpl?() + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer))) + } + }) + })) + }) + ] + ) alertController.dismissed = { _ in addAppLogEvent(postbox: context.account.postbox, type: "deactivate.options_support_cancel") } diff --git a/submodules/SettingsUI/Sources/DeleteAccountPeersItem.swift b/submodules/SettingsUI/Sources/DeleteAccountPeersItem.swift index 0e7581bd..1ce362fc 100644 --- a/submodules/SettingsUI/Sources/DeleteAccountPeersItem.swift +++ b/submodules/SettingsUI/Sources/DeleteAccountPeersItem.swift @@ -169,7 +169,7 @@ class DeleteAccountPeersItemNode: ListViewItemNode, ItemListItemNode { self.listView = ListView() self.listView.transform = CATransform3DMakeRotation(-CGFloat.pi / 2.0, 0.0, 0.0, 1.0) - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.listView) diff --git a/submodules/SettingsUI/Sources/DeleteAccountPhoneItem.swift b/submodules/SettingsUI/Sources/DeleteAccountPhoneItem.swift index 11c8ad49..396d6583 100644 --- a/submodules/SettingsUI/Sources/DeleteAccountPhoneItem.swift +++ b/submodules/SettingsUI/Sources/DeleteAccountPhoneItem.swift @@ -167,7 +167,7 @@ final class DeleteAccountPhoneItemNode: ListViewItemNode, ItemListItemNode { self.phoneInputNode = PhoneInputNode(fontSize: 17.0) - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.phoneBackground) self.addSubnode(self.countryButton) diff --git a/submodules/SettingsUI/Sources/Language Selection/LocalizationListController.swift b/submodules/SettingsUI/Sources/Language Selection/LocalizationListController.swift index 8417ebce..63ec3ccf 100644 --- a/submodules/SettingsUI/Sources/Language Selection/LocalizationListController.swift +++ b/submodules/SettingsUI/Sources/Language Selection/LocalizationListController.swift @@ -36,7 +36,7 @@ public class LocalizationListController: ViewController { self.presentationData = context.sharedContext.currentPresentationData.with { $0 } - super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData)) + super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData, style: .glass)) self.editItem = UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.editPressed)) self.doneItem = UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed)) @@ -86,7 +86,7 @@ public class LocalizationListController: ViewController { private func updateThemeAndStrings() { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData, style: .glass), transition: .immediate) self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search) self.title = self.presentationData.strings.Settings_AppLanguage self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) diff --git a/submodules/SettingsUI/Sources/Language Selection/LocalizationListControllerNode.swift b/submodules/SettingsUI/Sources/Language Selection/LocalizationListControllerNode.swift index 4651de00..ec93cca6 100644 --- a/submodules/SettingsUI/Sources/Language Selection/LocalizationListControllerNode.swift +++ b/submodules/SettingsUI/Sources/Language Selection/LocalizationListControllerNode.swift @@ -165,7 +165,7 @@ private final class LocalizationListSearchContainerNode: SearchDisplayController self.presentationDataPromise = Promise(self.presentationData) self.dimNode = ASDisplayNode() - self.dimNode.backgroundColor = UIColor.black.withAlphaComponent(0.5) + self.dimNode.backgroundColor = .clear self.listNode = ListView() self.listNode.accessibilityPageScrolledString = { row, count in @@ -809,7 +809,7 @@ final class LocalizationListControllerNode: ViewControllerTracingNode { self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: LocalizationListSearchContainerNode(context: self.context, listState: self.currentListState ?? LocalizationListState.defaultSettings, selectLocalization: { [weak self] info in self?.selectLocalization(info) }, applyingCode: self.applyingCode.get()), inline: true, cancel: { [weak self] in self?.requestDeactivateSearch() - }) + }, fieldStyle: placeholderNode.fieldStyle) self.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: navigationBarHeight, transition: .immediate) self.searchDisplayController?.activate(insertSubnode: { [weak self, weak placeholderNode] subnode, isSearchBar in diff --git a/submodules/SettingsUI/Sources/LogoutOptionsController.swift b/submodules/SettingsUI/Sources/LogoutOptionsController.swift index 9deadca7..862a3054 100644 --- a/submodules/SettingsUI/Sources/LogoutOptionsController.swift +++ b/submodules/SettingsUI/Sources/LogoutOptionsController.swift @@ -272,10 +272,6 @@ public func logoutOptionsController(context: AccountContext, navigationControlle context.sharedContext.accountManager.accessChallengeData() ) |> map { presentationData, accessChallengeData -> (ItemListControllerState, (ItemListNodeState, Any)) in - let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: { - dismissImpl?() - }) - var hasPasscode = false switch accessChallengeData.data { case .numericalPassword, .plaintextPassword: @@ -284,7 +280,7 @@ public func logoutOptionsController(context: AccountContext, navigationControlle break } - let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.LogoutOptions_Title), leftNavigationButton: leftNavigationButton, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)) + let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.LogoutOptions_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)) let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: logoutOptionsEntries(presentationData: presentationData, canAddAccounts: canAddAccounts, hasPasscode: hasPasscode), style: .blocks) return (controllerState, (listState, arguments)) diff --git a/submodules/SettingsUI/Sources/Notifications/Exceptions/NotificationExceptionControllerNode.swift b/submodules/SettingsUI/Sources/Notifications/Exceptions/NotificationExceptionControllerNode.swift index 336ce4ce..32ca8fcb 100644 --- a/submodules/SettingsUI/Sources/Notifications/Exceptions/NotificationExceptionControllerNode.swift +++ b/submodules/SettingsUI/Sources/Notifications/Exceptions/NotificationExceptionControllerNode.swift @@ -243,7 +243,6 @@ private final class NotificationExceptionArguments { } private enum NotificationExceptionEntryId: Hashable { - case search case peerId(Int64) case addException case removeAll @@ -254,13 +253,6 @@ private enum NotificationExceptionEntryId: Hashable { static func ==(lhs: NotificationExceptionEntryId, rhs: NotificationExceptionEntryId) -> Bool { switch lhs { - case .search: - switch rhs { - case .search: - return true - default: - return false - } case .addException: switch rhs { case .addException: @@ -302,7 +294,6 @@ private enum NotificationExceptionEntry : ItemListNodeEntry { typealias ItemGenerationArguments = NotificationExceptionArguments - case search(PresentationTheme, PresentationStrings) case peer(index: Int, peer: EnginePeer, theme: PresentationTheme, strings: PresentationStrings, dateFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, description: String, notificationSettings: TelegramPeerNotificationSettings, revealed: Bool, editing: Bool, isSearching: Bool) case addPeer(index: Int, peer: EnginePeer, theme: PresentationTheme, strings: PresentationStrings, dateFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder) case addException(PresentationTheme, PresentationStrings, NotificationExceptionMode.Mode, Bool) @@ -311,10 +302,6 @@ private enum NotificationExceptionEntry : ItemListNodeEntry { func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem { let arguments = arguments as! NotificationExceptionArguments switch self { - case let .search(theme, strings): - return NotificationSearchItem(theme: theme, placeholder: strings.Common_Search, activate: { - arguments.activateSearch() - }) case let .addException(theme, strings, mode, editing): let icon: UIImage? switch mode { @@ -352,8 +339,6 @@ private enum NotificationExceptionEntry : ItemListNodeEntry { var stableId: NotificationExceptionEntryId { switch self { - case .search: - return .search case .addException: return .addException case let .peer(_, peer, _, _, _, _, _, _, _, _, _): @@ -367,13 +352,6 @@ private enum NotificationExceptionEntry : ItemListNodeEntry { static func == (lhs: NotificationExceptionEntry, rhs: NotificationExceptionEntry) -> Bool { switch lhs { - case let .search(lhsTheme, lhsStrings): - switch rhs { - case let .search(rhsTheme, rhsStrings): - return lhsTheme === rhsTheme && lhsStrings === rhsStrings - default: - return false - } case let .addException(lhsTheme, lhsStrings, lhsMode, lhsEditing): switch rhs { case let .addException(rhsTheme, rhsStrings, rhsMode, rhsEditing): @@ -406,18 +384,16 @@ private enum NotificationExceptionEntry : ItemListNodeEntry { static func <(lhs: NotificationExceptionEntry, rhs: NotificationExceptionEntry) -> Bool { switch lhs { - case .search: - return true case .addException: switch rhs { - case .search, .addException: + case .addException: return false default: return true } case let .peer(lhsIndex, _, _, _, _, _, _, _, _, _, _): switch rhs { - case .search, .addException: + case .addException: return false case let .peer(rhsIndex, _, _, _, _, _, _, _, _, _, _): return lhsIndex < rhsIndex @@ -428,7 +404,7 @@ private enum NotificationExceptionEntry : ItemListNodeEntry { } case let .addPeer(lhsIndex, _, _, _, _, _): switch rhs { - case .search, .addException: + case .addException: return false case let .peer(rhsIndex, _, _, _, _, _, _, _, _, _, _): return lhsIndex < rhsIndex @@ -936,7 +912,7 @@ final class NotificationExceptionsControllerNode: ViewControllerTracingNode { self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: NotificationExceptionsSearchContainerNode(context: self.context, mode: self.stateValue.modify {$0}.mode, arguments: self.arguments!), cancel: { [weak self] in self?.requestDeactivateSearch(true) - }) + }, fieldStyle: placeholderNode.fieldStyle) self.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: navigationBarHeight, transition: .immediate) self.searchDisplayController?.activate(insertSubnode: { [weak self, weak placeholderNode] subnode, isSearchBar in @@ -1007,7 +983,7 @@ private final class NotificationExceptionsSearchContainerNode: SearchDisplayCont self.themeAndStringsPromise = Promise((self.presentationData.theme, self.presentationData.strings)) self.dimNode = ASDisplayNode() - self.dimNode.backgroundColor = UIColor.black.withAlphaComponent(0.5) + self.dimNode.backgroundColor = .clear self.listNode = ListView() self.listNode.accessibilityPageScrolledString = { row, count in diff --git a/submodules/SettingsUI/Sources/Notifications/Exceptions/NotificationExceptions.swift b/submodules/SettingsUI/Sources/Notifications/Exceptions/NotificationExceptions.swift index 7cdd4094..7a13bda8 100644 --- a/submodules/SettingsUI/Sources/Notifications/Exceptions/NotificationExceptions.swift +++ b/submodules/SettingsUI/Sources/Notifications/Exceptions/NotificationExceptions.swift @@ -39,7 +39,9 @@ public class NotificationExceptionsController: ViewController { self.updatedMode = updatedMode self.presentationData = context.sharedContext.currentPresentationData.with { $0 } - super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData)) + super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData, style: .glass)) + + self._hasGlassStyle = true self.removeAllItem = UIBarButtonItem(title: self.presentationData.strings.Notification_Exceptions_DeleteAll, style: .plain, target: self, action: #selector(self.removeAllPressed)) self.editItem = UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.editPressed)) @@ -90,7 +92,7 @@ public class NotificationExceptionsController: ViewController { private func updateThemeAndStrings() { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData, style: .glass), transition: .immediate) self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search) self.title = self.presentationData.strings.Notifications_ExceptionsTitle self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) diff --git a/submodules/SettingsUI/Sources/Notifications/NotificationsCategoryItemListItem.swift b/submodules/SettingsUI/Sources/Notifications/NotificationsCategoryItemListItem.swift index 9e0f727d..a63e4174 100644 --- a/submodules/SettingsUI/Sources/Notifications/NotificationsCategoryItemListItem.swift +++ b/submodules/SettingsUI/Sources/Notifications/NotificationsCategoryItemListItem.swift @@ -148,7 +148,7 @@ public class NotificationsCategoryItemListItemNode: ListViewItemNode, ItemListIt self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.titleNode) self.addSubnode(self.subtitleNode) diff --git a/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift b/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift index f522f382..d7bb50ba 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift @@ -115,7 +115,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode { self.measureTextNode = TextNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.clipsToBounds = true diff --git a/submodules/SettingsUI/Sources/Privacy and Security/GlobalAutoremoveHeaderItem.swift b/submodules/SettingsUI/Sources/Privacy and Security/GlobalAutoremoveHeaderItem.swift index 585f1d76..5eda7838 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/GlobalAutoremoveHeaderItem.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/GlobalAutoremoveHeaderItem.swift @@ -68,7 +68,7 @@ class GlobalAutoremoveHeaderItemNode: ListViewItemNode { init() { self.animationNode = DefaultAnimatedStickerNodeImpl() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.animationNode) } diff --git a/submodules/SettingsUI/Sources/Privacy and Security/GlobalAutoremoveScreen.swift b/submodules/SettingsUI/Sources/Privacy and Security/GlobalAutoremoveScreen.swift index a10a405f..6b4d5aae 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/GlobalAutoremoveScreen.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/GlobalAutoremoveScreen.swift @@ -269,8 +269,8 @@ public func globalAutoremoveScreen(context: AccountContext, initialValue: Int32, } else { let presentationData = context.sharedContext.currentPresentationData.with { $0 } let valueText = timeIntervalString(strings: presentationData.strings, value: timeout, usage: .afterTime) - presentControllerImpl?(standardTextAlertController( - theme: AlertControllerTheme(presentationData: presentationData), + presentControllerImpl?(textAlertController( + context: context, title: presentationData.strings.GlobalAutodeleteSettings_SetConfirmTitle, text: presentationData.strings.GlobalAutodeleteSettings_SetConfirmText(valueText).string, actions: [ @@ -350,8 +350,8 @@ public func globalAutoremoveScreen(context: AccountContext, initialValue: Int32, text = presentationData.strings.GlobalAutodeleteSettings_AttemptDisabledGenericSelection } - presentControllerImpl?(standardTextAlertController( - theme: AlertControllerTheme(presentationData: presentationData), + presentControllerImpl?(textAlertController( + context: context, title: nil, text: text, actions: [ diff --git a/submodules/SettingsUI/Sources/Privacy and Security/LoginEmailSetupController.swift b/submodules/SettingsUI/Sources/Privacy and Security/LoginEmailSetupController.swift index 45cb19e0..60087d54 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/LoginEmailSetupController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/LoginEmailSetupController.swift @@ -5,6 +5,7 @@ import SwiftSignalKit import TelegramCore import AccountContext import TelegramPresentationData +import PresentationDataUtils import AuthorizationUI import AuthenticationServices import UndoUI @@ -76,7 +77,7 @@ public func loginEmailSetupController(context: AccountContext, blocking: Bool, e var dismissCodeControllerImpl: (() -> Void)? var presentControllerImpl: ((ViewController) -> Void)? - let codeController = AuthorizationSequenceCodeEntryController(presentationData: presentationData, back: { + let codeController = AuthorizationSequenceCodeEntryController(sharedContext: context.sharedContext, presentationData: presentationData, back: { dismissCodeControllerImpl?() dismiss() }) @@ -119,7 +120,7 @@ public func loginEmailSetupController(context: AccountContext, blocking: Bool, e codeController?.resetCode() } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])) + presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])) } } }, completed: { [weak codeController] in @@ -148,7 +149,7 @@ public func loginEmailSetupController(context: AccountContext, blocking: Bool, e text = presentationData.strings.Login_EmailNotAllowedError } - presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])) + presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])) }, completed: { [weak emailController] in emailController?.inProgress = false }) @@ -173,7 +174,7 @@ public func loginEmailSetupController(context: AccountContext, blocking: Bool, e switch credential { case let appleIdCredential as ASAuthorizationAppleIDCredential: guard let tokenData = appleIdCredential.identityToken, let token = String(data: tokenData, encoding: .utf8) else { - emailController?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + emailController?.present(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) return } let _ = (verifyLoginEmailChange(account: context.account, code: .appleToken(token)) @@ -193,7 +194,7 @@ public func loginEmailSetupController(context: AccountContext, blocking: Bool, e case .emailNotAllowed: text = presentationData.strings.Login_EmailNotAllowedError } - emailController?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + emailController?.present(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) }, completed: { [weak emailController] in emailController?.authorization = nil emailController?.authorizationDelegate = nil diff --git a/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift b/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift index 09aeb828..ed2b35e3 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift @@ -45,8 +45,11 @@ private final class PrivacyAndSecurityControllerArguments { let openEmailSettings: (String?) -> Void let openMessagePrivacy: () -> Void let openGiftsPrivacy: () -> Void + let openDeletedMessages: () -> Void + let openGhostMode: () -> Void + let openMisc: () -> Void - init(account: Account, openBlockedUsers: @escaping () -> Void, openLastSeenPrivacy: @escaping () -> Void, openGroupsPrivacy: @escaping () -> Void, openVoiceCallPrivacy: @escaping () -> Void, openProfilePhotoPrivacy: @escaping () -> Void, openForwardPrivacy: @escaping () -> Void, openPhoneNumberPrivacy: @escaping () -> Void, openVoiceMessagePrivacy: @escaping () -> Void, openBioPrivacy: @escaping () -> Void, openBirthdayPrivacy: @escaping () -> Void, openSavedMusicPrivacy: @escaping () -> Void, openPasscode: @escaping () -> Void, openTwoStepVerification: @escaping (TwoStepVerificationAccessConfiguration?) -> Void, openPasskeys: @escaping () -> Void, openActiveSessions: @escaping () -> Void, toggleArchiveAndMuteNonContacts: @escaping (Bool) -> Void, setupAccountAutoremove: @escaping () -> Void, setupMessageAutoremove: @escaping () -> Void, openDataSettings: @escaping () -> Void, openEmailSettings: @escaping (String?) -> Void, openMessagePrivacy: @escaping () -> Void, openGiftsPrivacy: @escaping () -> Void) { + init(account: Account, openBlockedUsers: @escaping () -> Void, openLastSeenPrivacy: @escaping () -> Void, openGroupsPrivacy: @escaping () -> Void, openVoiceCallPrivacy: @escaping () -> Void, openProfilePhotoPrivacy: @escaping () -> Void, openForwardPrivacy: @escaping () -> Void, openPhoneNumberPrivacy: @escaping () -> Void, openVoiceMessagePrivacy: @escaping () -> Void, openBioPrivacy: @escaping () -> Void, openBirthdayPrivacy: @escaping () -> Void, openSavedMusicPrivacy: @escaping () -> Void, openPasscode: @escaping () -> Void, openTwoStepVerification: @escaping (TwoStepVerificationAccessConfiguration?) -> Void, openPasskeys: @escaping () -> Void, openActiveSessions: @escaping () -> Void, toggleArchiveAndMuteNonContacts: @escaping (Bool) -> Void, setupAccountAutoremove: @escaping () -> Void, setupMessageAutoremove: @escaping () -> Void, openDataSettings: @escaping () -> Void, openEmailSettings: @escaping (String?) -> Void, openMessagePrivacy: @escaping () -> Void, openGiftsPrivacy: @escaping () -> Void, openDeletedMessages: @escaping () -> Void, openGhostMode: @escaping () -> Void, openMisc: @escaping () -> Void) { self.account = account self.openBlockedUsers = openBlockedUsers self.openLastSeenPrivacy = openLastSeenPrivacy @@ -70,6 +73,9 @@ private final class PrivacyAndSecurityControllerArguments { self.openEmailSettings = openEmailSettings self.openMessagePrivacy = openMessagePrivacy self.openGiftsPrivacy = openGiftsPrivacy + self.openDeletedMessages = openDeletedMessages + self.openGhostMode = openGhostMode + self.openMisc = openMisc } } @@ -81,6 +87,9 @@ private enum PrivacyAndSecuritySection: Int32 { case messageAutoremove case dataSettings case loginEmail + case antiDelete + case ghostMode + case misc } public enum PrivacyAndSecurityEntryTag: ItemListItemTag { @@ -130,6 +139,12 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry { case messageAutoremoveInfo(PresentationTheme, String) case dataSettings(PresentationTheme, String) case dataSettingsInfo(PresentationTheme, String) + case deletedMessages(PresentationTheme, String, String) + case deletedMessagesInfo(PresentationTheme, String) + case ghostMode(PresentationTheme, String, String) + case ghostModeInfo(PresentationTheme, String) + case misc(PresentationTheme, String, String) + case miscInfo(PresentationTheme, String) var section: ItemListSectionId { switch self { @@ -145,6 +160,12 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry { return PrivacyAndSecuritySection.account.rawValue case .dataSettings, .dataSettingsInfo: return PrivacyAndSecuritySection.dataSettings.rawValue + case .deletedMessages, .deletedMessagesInfo: + return PrivacyAndSecuritySection.antiDelete.rawValue + case .ghostMode, .ghostModeInfo: + return PrivacyAndSecuritySection.ghostMode.rawValue + case .misc, .miscInfo: + return PrivacyAndSecuritySection.misc.rawValue } } @@ -214,6 +235,18 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry { return 31 case .dataSettingsInfo: return 32 + case .deletedMessages: + return 33 + case .deletedMessagesInfo: + return 34 + case .ghostMode: + return 35 + case .ghostModeInfo: + return 36 + case .misc: + return 37 + case .miscInfo: + return 38 } } @@ -411,6 +444,42 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry { } else { return false } + case let .deletedMessages(lhsTheme, lhsText, lhsValue): + if case let .deletedMessages(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { + return true + } else { + return false + } + case let .deletedMessagesInfo(lhsTheme, lhsText): + if case let .deletedMessagesInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { + return true + } else { + return false + } + case let .ghostMode(lhsTheme, lhsText, lhsValue): + if case let .ghostMode(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { + return true + } else { + return false + } + case let .ghostModeInfo(lhsTheme, lhsText): + if case let .ghostModeInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { + return true + } else { + return false + } + case let .misc(lhsTheme, lhsText, lhsValue): + if case let .misc(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { + return true + } else { + return false + } + case let .miscInfo(lhsTheme, lhsText): + if case let .miscInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { + return true + } else { + return false + } } } @@ -542,6 +611,24 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry { }) case let .dataSettingsInfo(_, text): return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) + case let .deletedMessages(_, text, value): + return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, icon: UIImage(bundleImageName: "Settings/Menu/Timer")?.precomposed(), title: text, label: value, sectionId: self.section, style: .blocks, action: { + arguments.openDeletedMessages() + }) + case let .deletedMessagesInfo(_, text): + return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) + case let .ghostMode(_, text, value): + return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, icon: UIImage(bundleImageName: "Settings/Menu/Appearance")?.precomposed(), title: text, label: value, sectionId: self.section, style: .blocks, action: { + arguments.openGhostMode() + }) + case let .ghostModeInfo(_, text): + return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) + case let .misc(_, text, value): + return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, icon: UIImage(bundleImageName: "Settings/Menu/Storage")?.precomposed(), title: text, label: value, sectionId: self.section, style: .blocks, action: { + arguments.openMisc() + }) + case let .miscInfo(_, text): + return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) } } } @@ -1406,10 +1493,10 @@ public func privacyAndSecurityController( let presentationData = context.sharedContext.currentPresentationData.with { $0 } let controller = textAlertController( context: context, title: emailPattern, text: presentationData.strings.PrivacySettings_LoginEmailAlertText, actions: [ - TextAlertAction(type: .genericAction, title: presentationData.strings.PrivacySettings_LoginEmailAlertChange, action: { + TextAlertAction(type: .defaultAction, title: presentationData.strings.PrivacySettings_LoginEmailAlertChange, action: { setupEmailImpl?(emailPattern) }), - TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: { + TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { }) ], actionLayout: .vertical @@ -1497,6 +1584,12 @@ public func privacyAndSecurityController( }), true) } })) + }, openDeletedMessages: { + pushControllerImpl?(deletedMessagesController(context: context), true) + }, openGhostMode: { + pushControllerImpl?(ghostModeController(context: context), true) + }, openMisc: { + pushControllerImpl?(miscController(context: context), true) }) actionsDisposable.add(context.engine.peers.managedUpdatedRecentPeers().start()) diff --git a/submodules/SettingsUI/Sources/Privacy and Security/PrivacyIntroController.swift b/submodules/SettingsUI/Sources/Privacy and Security/PrivacyIntroController.swift index 73a50a65..b6c12d70 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/PrivacyIntroController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/PrivacyIntroController.swift @@ -120,7 +120,7 @@ public final class PrivacyIntroController: ViewController { self.presentationData = context.sharedContext.currentPresentationData.with { $0 } - super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData)) + super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData, style: .glass)) self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style @@ -161,7 +161,7 @@ public final class PrivacyIntroController: ViewController { private func updateThemeAndStrings() { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData, style: .glass), transition: .immediate) self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) self.controllerNode.updatePresentationData(self.presentationData) } diff --git a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListRecentSessionItem.swift b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListRecentSessionItem.swift index 77684f5b..a872ae6a 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListRecentSessionItem.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListRecentSessionItem.swift @@ -241,7 +241,7 @@ class ItemListRecentSessionItemNode: ItemListRevealOptionsItemNode { self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.containerNode) self.containerNode.addSubnode(self.iconNode) diff --git a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListWebsiteItem.swift b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListWebsiteItem.swift index cd902c96..1dc5be38 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListWebsiteItem.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/ItemListWebsiteItem.swift @@ -190,7 +190,7 @@ class ItemListWebsiteItemNode: ItemListRevealOptionsItemNode { self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.containerNode) self.containerNode.addSubnode(self.avatarNode) diff --git a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/RecentSessionsHeaderItem.swift b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/RecentSessionsHeaderItem.swift index 2bd1a4d1..0b9dab7e 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/RecentSessionsHeaderItem.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/RecentSessionsHeaderItem.swift @@ -92,7 +92,7 @@ class RecentSessionsHeaderItemNode: ListViewItemNode { self.buttonNode = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(backgroundColor: .black, foregroundColor: .white), fontSize: 16.0, height: 50.0, cornerRadius: 11.0) - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.titleNode) self.addSubnode(self.animationNode) diff --git a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift index 5f656d9b..6913f3f3 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/SelectivePrivacySettingsController.swift @@ -1664,6 +1664,12 @@ public func selectivePrivacySettingsController( } else { updatedDisallowedGifts.remove(.premium) } + case .channel: + if value { + updatedDisallowedGifts.insert(.channel) + } else { + updatedDisallowedGifts.remove(.channel) + } default: break } diff --git a/submodules/SettingsUI/Sources/Search/SettingsSearchItem.swift b/submodules/SettingsUI/Sources/Search/SettingsSearchItem.swift index 595c4adb..076f1874 100644 --- a/submodules/SettingsUI/Sources/Search/SettingsSearchItem.swift +++ b/submodules/SettingsUI/Sources/Search/SettingsSearchItem.swift @@ -13,6 +13,9 @@ import AccountContext import SearchBarNode import SearchUI import ChatListSearchItemHeader +import EdgeEffect +import ComponentFlow +import ComponentDisplayAdapters extension SettingsSearchableItemIcon { func image() -> UIImage? { @@ -59,113 +62,6 @@ extension SettingsSearchableItemIcon { } } -final class SettingsSearchItem: ItemListControllerSearch { - let context: AccountContext - let theme: PresentationTheme - let placeholder: String - let activated: Bool - let updateActivated: (Bool) -> Void - let presentController: (ViewController, Any?) -> Void - let pushController: (ViewController) -> Void - let getNavigationController: (() -> NavigationController?)? - let resolvedFaqUrl: Signal - let exceptionsList: Signal - let archivedStickerPacks: Signal<[ArchivedStickerPackItem]?, NoError> - let privacySettings: Signal - let hasTwoStepAuth: Signal - let twoStepAuthData: Signal - let activeSessionsContext: Signal - let webSessionsContext: Signal - - private var updateActivity: ((Bool) -> Void)? - private var activity: ValuePromise = ValuePromise(ignoreRepeated: false) - private let activityDisposable = MetaDisposable() - - init(context: AccountContext, theme: PresentationTheme, placeholder: String, activated: Bool, updateActivated: @escaping (Bool) -> Void, presentController: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void, getNavigationController: (() -> NavigationController?)?, resolvedFaqUrl: Signal, exceptionsList: Signal, archivedStickerPacks: Signal<[ArchivedStickerPackItem]?, NoError>, privacySettings: Signal, hasTwoStepAuth: Signal, twoStepAuthData: Signal, activeSessionsContext: Signal, webSessionsContext: Signal) { - self.context = context - self.theme = theme - self.placeholder = placeholder - self.activated = activated - self.updateActivated = updateActivated - self.presentController = presentController - self.pushController = pushController - self.getNavigationController = getNavigationController - self.resolvedFaqUrl = resolvedFaqUrl - self.exceptionsList = exceptionsList - self.archivedStickerPacks = archivedStickerPacks - self.privacySettings = privacySettings - self.hasTwoStepAuth = hasTwoStepAuth - self.twoStepAuthData = twoStepAuthData - self.activeSessionsContext = activeSessionsContext - self.webSessionsContext = webSessionsContext - self.activityDisposable.set((activity.get() |> mapToSignal { value -> Signal in - if value { - return .single(value) |> delay(0.2, queue: Queue.mainQueue()) - } else { - return .single(value) - } - }).start(next: { [weak self] value in - self?.updateActivity?(value) - })) - } - - deinit { - self.activityDisposable.dispose() - } - - func isEqual(to: ItemListControllerSearch) -> Bool { - if let to = to as? SettingsSearchItem { - if self.context !== to.context || self.theme !== to.theme || self.placeholder != to.placeholder || self.activated != to.activated { - return false - } - return true - } else { - return false - } - } - - func titleContentNode(current: (NavigationBarContentNode & ItemListControllerSearchNavigationContentNode)?) -> (NavigationBarContentNode & ItemListControllerSearchNavigationContentNode)? { - let updateActivated: (Bool) -> Void = self.updateActivated - if let current = current as? NavigationBarSearchContentNode { - current.updateThemeAndPlaceholder(theme: self.theme, placeholder: self.placeholder) - return current - } else { - let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - return NavigationBarSearchContentNode(theme: presentationData.theme, placeholder: presentationData.strings.Settings_Search, activate: { - updateActivated(true) - }) - } - } - - func node(current: ItemListControllerSearchNode?, titleContentNode: (NavigationBarContentNode & ItemListControllerSearchNavigationContentNode)?) -> ItemListControllerSearchNode { - let updateActivated: (Bool) -> Void = self.updateActivated - let presentController: (ViewController, Any?) -> Void = self.presentController - let pushController: (ViewController) -> Void = self.pushController - - if let current = current as? SettingsSearchItemNode, let titleContentNode = titleContentNode as? NavigationBarSearchContentNode { - current.updatePresentationData(self.context.sharedContext.currentPresentationData.with { $0 }) - if current.isSearching != self.activated { - if self.activated { - current.activateSearch(placeholderNode: titleContentNode.placeholderNode) - } else { - current.deactivateSearch(placeholderNode: titleContentNode.placeholderNode) - } - } - return current - } else { - return SettingsSearchItemNode(context: self.context, cancel: { - updateActivated(false) - }, updateActivity: { [weak self] value in - self?.activity.set(value) - }, pushController: { c in - pushController(c) - }, presentController: { c, a in - presentController(c, a) - }, getNavigationController: self.getNavigationController, resolvedFaqUrl: self.resolvedFaqUrl, exceptionsList: self.exceptionsList, archivedStickerPacks: self.archivedStickerPacks, privacySettings: self.privacySettings, hasTwoStepAuth: self.hasTwoStepAuth, twoStepAuthData: self.twoStepAuthData, activeSessionsContext: self.activeSessionsContext, webSessionsContext: self.webSessionsContext) - } - } -} - final class SettingsSearchInteraction { let openItem: (SettingsSearchableItem) -> Void let deleteRecentItem: (SettingsSearchableItemId) -> Void @@ -333,6 +229,8 @@ public final class SettingsSearchContainerNode: SearchDisplayControllerContentNo private let listNode: ListView private let recentListNode: ListView + private let edgeEffectView: EdgeEffectView + private var enqueuedTransitions: [SettingsSearchContainerTransition] = [] private var enqueuedRecentTransitions: [(SettingsSearchContainerRecentTransition, Bool)] = [] private var hasValidLayout = false @@ -365,12 +263,15 @@ public final class SettingsSearchContainerNode: SearchDisplayControllerContentNo return presentationData.strings.VoiceOver_ScrollStatus(row, count).string } + self.edgeEffectView = EdgeEffectView() + super.init() self.backgroundColor = self.presentationData.theme.chatList.backgroundColor self.addSubnode(self.recentListNode) self.addSubnode(self.listNode) + self.view.addSubview(self.edgeEffectView) let interaction = SettingsSearchInteraction(openItem: { result in addRecentSettingsSearchItem(engine: context.engine, item: result.id) @@ -620,6 +521,12 @@ public final class SettingsSearchContainerNode: SearchDisplayControllerContentNo self.dequeueTransition() } } + + let edgeEffectHeight: CGFloat = insets.bottom + 8.0 + let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - edgeEffectHeight), size: CGSize(width: layout.size.width, height: edgeEffectHeight)) + transition.updateFrame(view: self.edgeEffectView, frame: edgeEffectFrame) + self.edgeEffectView.update(content: self.presentationData.theme.list.plainBackgroundColor, rect: edgeEffectFrame, edge: .bottom, edgeSize: min(edgeEffectHeight, 50.0), transition: ComponentTransition(transition)) + transition.updateAlpha(layer: self.edgeEffectView.layer, alpha: edgeEffectHeight > 21.0 ? 1.0 : 0.0) } public override func scrollToTop() { @@ -715,7 +622,7 @@ private final class SettingsSearchItemNode: ItemListControllerSearchNode { } }, resolvedFaqUrl: self.resolvedFaqUrl, exceptionsList: self.exceptionsList, archivedStickerPacks: self.archivedStickerPacks, privacySettings: self.privacySettings, hasTwoStepAuth: self.hasTwoStepAuth, twoStepAuthData: self.twoStepAuthData, activeSessionsContext: self.activeSessionsContext, webSessionsContext: self.webSessionsContext), cancel: { [weak self] in self?.cancel() - }) + }, fieldStyle: placeholderNode.fieldStyle) self.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: navigationBarHeight, transition: .immediate) self.searchDisplayController?.activate(insertSubnode: { [weak self, weak placeholderNode] subnode, isSearchBar in diff --git a/submodules/SettingsUI/Sources/Search/SettingsSearchRecentItem.swift b/submodules/SettingsUI/Sources/Search/SettingsSearchRecentItem.swift index 10f315d5..f2863b3b 100644 --- a/submodules/SettingsUI/Sources/Search/SettingsSearchRecentItem.swift +++ b/submodules/SettingsUI/Sources/Search/SettingsSearchRecentItem.swift @@ -127,7 +127,7 @@ class SettingsSearchRecentItemNode: ItemListRevealOptionsItemNode { self.subtitleNode.contentMode = .left self.subtitleNode.contentsScale = UIScreenScale - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.backgroundNode) self.addSubnode(self.separatorNode) diff --git a/submodules/SettingsUI/Sources/Search/SettingsSearchResultItem.swift b/submodules/SettingsUI/Sources/Search/SettingsSearchResultItem.swift index 7e730023..c04b38af 100644 --- a/submodules/SettingsUI/Sources/Search/SettingsSearchResultItem.swift +++ b/submodules/SettingsUI/Sources/Search/SettingsSearchResultItem.swift @@ -117,7 +117,7 @@ class SettingsSearchResultItemNode: ListViewItemNode { self.highlightedBackgroundNode = ASDisplayNode() self.highlightedBackgroundNode.isLayerBacked = true - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.iconNode) self.addSubnode(self.titleNode) diff --git a/submodules/SettingsUI/Sources/Terms of Service/TermsOfServiceController.swift b/submodules/SettingsUI/Sources/Terms of Service/TermsOfServiceController.swift index 333125c4..2317be18 100644 --- a/submodules/SettingsUI/Sources/Terms of Service/TermsOfServiceController.swift +++ b/submodules/SettingsUI/Sources/Terms of Service/TermsOfServiceController.swift @@ -6,7 +6,9 @@ import Display import AsyncDisplayKit import TelegramPresentationData import TelegramUIPreferences +import PresentationDataUtils import ProgressNavigationButtonNode +import AccountContext public class TermsOfServiceControllerTheme { public let statusBarStyle: StatusBarStyle @@ -43,6 +45,7 @@ public class TermsOfServiceController: ViewController, StandalonePresentableCont return self.displayNode as! TermsOfServiceControllerNode } + private let context: AccountContext private let presentationData: PresentationData private let text: String private let entities: [MessageTextEntity] @@ -67,7 +70,8 @@ public class TermsOfServiceController: ViewController, StandalonePresentableCont } } - public init(presentationData: PresentationData, text: String, entities: [MessageTextEntity], ageConfirmation: Int32?, signingUp: Bool, accept: @escaping (String?) -> Void, decline: @escaping () -> Void, openUrl: @escaping (String) -> Void) { + public init(context: AccountContext, presentationData: PresentationData, text: String, entities: [MessageTextEntity], ageConfirmation: Int32?, signingUp: Bool, accept: @escaping (String?) -> Void, decline: @escaping () -> Void, openUrl: @escaping (String) -> Void) { + self.context = context self.presentationData = presentationData self.text = text self.entities = entities @@ -112,7 +116,7 @@ public class TermsOfServiceController: ViewController, StandalonePresentableCont text = strongSelf.presentationData.strings.PrivacyPolicy_DeclineMessage declineTitle = strongSelf.presentationData.strings.PrivacyPolicy_DeclineDeclineAndDelete } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.PrivacyPolicy_Decline, text: text, actions: [TextAlertAction(type: .destructiveAction, title: declineTitle, action: { + strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.PrivacyPolicy_Decline, text: text, actions: [TextAlertAction(type: .destructiveAction, title: declineTitle, action: { self?.decline() }), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { })], actionLayout: .vertical), in: .window(.root)) @@ -122,7 +126,7 @@ public class TermsOfServiceController: ViewController, StandalonePresentableCont } if let ageConfirmation = strongSelf.ageConfirmation { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.PrivacyPolicy_AgeVerificationTitle, text: strongSelf.presentationData.strings.PrivacyPolicy_AgeVerificationMessage("\(ageConfirmation)").string, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.PrivacyPolicy_AgeVerificationAgree, action: { + strongSelf.present(textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.PrivacyPolicy_AgeVerificationTitle, text: strongSelf.presentationData.strings.PrivacyPolicy_AgeVerificationMessage("\(ageConfirmation)").string, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.PrivacyPolicy_AgeVerificationAgree, action: { self?.accept(self?.proccessBotNameAfterAccept) })]), in: .window(.root)) } else { diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index 44ab48af..d026f7a4 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -228,7 +228,6 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, ASScrollView }, openChatFolderUpdates: {}, hideChatFolderUpdates: { }, openStories: { _, _ in }, openStarsTopup: { _ in - }, dismissNotice: { _ in }, editPeer: { _ in }, openWebApp: { _ in }, openPhotoSetup: { @@ -625,7 +624,8 @@ final class TextSizeSelectionController: ViewController { self.presentationData = context.sharedContext.currentPresentationData.with { $0 } self.presentationThemeSettings = presentationThemeSettings - super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationTheme: self.presentationData.theme, presentationStrings: self.presentationData.strings)) + super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationTheme: self.presentationData.theme, presentationStrings: self.presentationData.strings, style: .glass)) + self._hasGlassStyle = true self.blocksBackgroundWhenInOverlay = true self.acceptsFocusWhenInOverlay = true diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionItem.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionItem.swift index f19fe421..125e1151 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionItem.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionItem.swift @@ -110,7 +110,7 @@ class BubbleSettingsRadiusItemNode: ListViewItemNode, ItemListItemNode { self.disabledOverlayNode = ASDisplayNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.leftIconNode) self.addSubnode(self.rightIconNode) diff --git a/submodules/SettingsUI/Sources/ThemePickerGridItem.swift b/submodules/SettingsUI/Sources/ThemePickerGridItem.swift index 5504d026..aae20dae 100644 --- a/submodules/SettingsUI/Sources/ThemePickerGridItem.swift +++ b/submodules/SettingsUI/Sources/ThemePickerGridItem.swift @@ -406,7 +406,7 @@ class ThemeGridThemeItemNode: ListViewItemNode, ItemListItemNode { self.scrollNode = ASScrollNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.containerNode) self.addSubnode(self.scrollNode) diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift index 2713ca2a..26504db1 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift @@ -157,7 +157,7 @@ public final class ThemePreviewController: ViewController { let titleView = CounterControllerTitleView(theme: strongSelf.previewTheme) titleView.title = CounterControllerTitle(title: themeName, counter: hasInstallsCount ? strongSelf.presentationData.strings.Theme_UsersCount(max(1, theme.installCount ?? 0)) : "") strongSelf.navigationItem.titleView = titleView - strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationTheme: presentationTheme, presentationStrings: strongSelf.presentationData.strings)) + strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationTheme: presentationTheme, presentationStrings: strongSelf.presentationData.strings), transition: .immediate) } }) diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index 9232eaf4..a0afa08c 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -377,7 +377,6 @@ final class ThemePreviewControllerNode: ASDisplayNode, ASScrollViewDelegate { }, openChatFolderUpdates: {}, hideChatFolderUpdates: { }, openStories: { _, _ in }, openStarsTopup: { _ in - }, dismissNotice: { _ in }, editPeer: { _ in }, openWebApp: { _ in }, openPhotoSetup: { diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsAccentColorItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsAccentColorItem.swift index 2e5218a8..7d673f35 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsAccentColorItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsAccentColorItem.swift @@ -298,7 +298,7 @@ private final class ThemeSettingsAccentColorIconItemNode : ListViewItemNode { self.dotsNode.displayWithoutProcessing = true self.dotsNode.image = generateDotsImage() - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.containerNode) self.containerNode.addSubnode(self.fillNode) @@ -537,7 +537,7 @@ private final class ThemeSettingsAccentColorPickerItemNode : ListViewItemNode { self.imageNode.displayWithoutProcessing = true self.imageNode.image = generateCustomSwatchImage() - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.imageNode) } @@ -734,7 +734,7 @@ class ThemeSettingsAccentColorItemNode: ListViewItemNode, ItemListItemNode { self.listNode = ListView() self.listNode.transform = CATransform3DMakeRotation(-CGFloat.pi / 2.0, 0.0, 0.0, 1.0) - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.containerNode) self.addSubnode(self.listNode) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsAppIconItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsAppIconItem.swift index 421d3d41..4ba20460 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsAppIconItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsAppIconItem.swift @@ -229,7 +229,7 @@ class ThemeSettingsAppIconItemNode: ListViewItemNode, ItemListItemNode { self.containerNode = ASDisplayNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.containerNode) } diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsBrightnessItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsBrightnessItem.swift index c4fa77d5..81aee3ec 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsBrightnessItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsBrightnessItem.swift @@ -90,7 +90,7 @@ class ThemeSettingsBrightnessItemNode: ListViewItemNode { self.rightIconNode.displaysAsynchronously = false self.rightIconNode.displayWithoutProcessing = true - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.leftIconNode) self.addSubnode(self.rightIconNode) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift index ac07ff62..b475dce6 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift @@ -130,7 +130,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode { self.containerNode = ASDisplayNode() self.containerNode.subnodeTransform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.clipsToBounds = true diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsFontSizeItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsFontSizeItem.swift index aff462c9..08d11cee 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsFontSizeItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsFontSizeItem.swift @@ -110,7 +110,7 @@ class ThemeSettingsFontSizeItemNode: ListViewItemNode, ItemListItemNode { self.disabledOverlayNode = ASDisplayNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.leftIconNode) self.addSubnode(self.rightIconNode) diff --git a/submodules/ShimmerEffect/Sources/ShimmerEffect.swift b/submodules/ShimmerEffect/Sources/ShimmerEffect.swift index 2c586b59..467e16a7 100644 --- a/submodules/ShimmerEffect/Sources/ShimmerEffect.swift +++ b/submodules/ShimmerEffect/Sources/ShimmerEffect.swift @@ -236,12 +236,11 @@ public final class ShimmerEffectForegroundNode: ASDisplayNode { let image: UIImage? if horizontal { - let baseAlpha: CGFloat = 0.1 + //let baseAlpha: CGFloat = 0.1 image = generateImage(CGSize(width: effectSize ?? 200.0, height: 16.0), opaque: false, scale: 1.0, rotatedContext: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) - let foregroundColor = UIColor(white: 1.0, alpha: min(1.0, baseAlpha * 4.0)) - + //let foregroundColor = foregroundColor //.withAlphaComponent(min(1.0, baseAlpha * 3.0)) if let shadowImage { UIGraphicsPushContext(context) diff --git a/submodules/StatisticsUI/BUILD b/submodules/StatisticsUI/BUILD index 293b755d..f5dbe1a1 100644 --- a/submodules/StatisticsUI/BUILD +++ b/submodules/StatisticsUI/BUILD @@ -49,13 +49,14 @@ swift_library( "//submodules/TelegramNotices", "//submodules/UIKitRuntimeUtils", "//submodules/PasswordSetupUI", - "//submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController", "//submodules/TelegramUI/Components/ListItemComponentAdaptor", "//submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen", "//submodules/TelegramUI/Components/ButtonComponent", "//submodules/TelegramUI/Components/ListActionItemComponent", "//submodules/TelegramUI/Components/Stars/StarsAvatarComponent", "//submodules/TelegramUI/Components/PremiumPeerShortcutComponent", + "//submodules/TelegramUI/Components/AlertComponent", + "//submodules/TelegramUI/Components/AlertComponent/AlertInputFieldComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/StatisticsUI/Sources/BackButton.swift b/submodules/StatisticsUI/Sources/BackButton.swift index 271cae3d..ff04a480 100644 --- a/submodules/StatisticsUI/Sources/BackButton.swift +++ b/submodules/StatisticsUI/Sources/BackButton.swift @@ -149,7 +149,7 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { case .back: text = presentationData.strings.Common_Back accessibilityText = presentationData.strings.Common_Back - icon = NavigationBar.backArrowImage(color: .white) + icon = navigationBarBackArrowImage(color: .white) case .edit: text = presentationData.strings.Common_Edit accessibilityText = text diff --git a/submodules/StatisticsUI/Sources/BoostLevelHeaderItem.swift b/submodules/StatisticsUI/Sources/BoostLevelHeaderItem.swift index 92707133..5bb6750c 100644 --- a/submodules/StatisticsUI/Sources/BoostLevelHeaderItem.swift +++ b/submodules/StatisticsUI/Sources/BoostLevelHeaderItem.swift @@ -76,7 +76,7 @@ class IncreaseLimitHeaderItemNode: ListViewItemNode { private var item: BoostLevelHeaderItem? init() { - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) } override func didLoad() { diff --git a/submodules/StatisticsUI/Sources/BoostsTabsItem.swift b/submodules/StatisticsUI/Sources/BoostsTabsItem.swift index 538b3235..334ede42 100644 --- a/submodules/StatisticsUI/Sources/BoostsTabsItem.swift +++ b/submodules/StatisticsUI/Sources/BoostsTabsItem.swift @@ -111,7 +111,7 @@ private final class BoostsTabsItemNode: ListViewItemNode { self.selectionNode = ASImageNode() self.selectionNode.displaysAsynchronously = false - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.boostsTextNode) self.addSubnode(self.giftsTextNode) diff --git a/submodules/StatisticsUI/Sources/ChannelStatsController.swift b/submodules/StatisticsUI/Sources/ChannelStatsController.swift index 3602d253..af215827 100644 --- a/submodules/StatisticsUI/Sources/ChannelStatsController.swift +++ b/submodules/StatisticsUI/Sources/ChannelStatsController.swift @@ -1229,7 +1229,7 @@ private enum StatsEntry: ItemListNodeEntry { arguments.presentCpmLocked() }) case .earnStarsInfo: - return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.earnStars, title: presentationData.strings.Monetization_EarnStarsInfo_Title, titleBadge: presentationData.strings.Settings_New, label: presentationData.strings.Monetization_EarnStarsInfo_Text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, action: { + return ItemListDisclosureItem(presentationData: presentationData, icon: PresentationResourcesSettings.earnStars, title: presentationData.strings.Monetization_EarnStarsInfo_Title, titleBadge: nil, label: presentationData.strings.Monetization_EarnStarsInfo_Text, labelStyle: .multilineDetailText, sectionId: self.section, style: .blocks, action: { arguments.openEarnStars() }) } diff --git a/submodules/StatisticsUI/Sources/MonetizationBalanceItem.swift b/submodules/StatisticsUI/Sources/MonetizationBalanceItem.swift index 98441dc4..687d2a42 100644 --- a/submodules/StatisticsUI/Sources/MonetizationBalanceItem.swift +++ b/submodules/StatisticsUI/Sources/MonetizationBalanceItem.swift @@ -140,7 +140,7 @@ final class MonetizationBalanceItemNode: ListViewItemNode, ItemListItemNode { self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.iconNode) self.addSubnode(self.balanceTextNode) diff --git a/submodules/StatisticsUI/Sources/MonetizationIntroScreen.swift b/submodules/StatisticsUI/Sources/MonetizationIntroScreen.swift index 2dc7e168..b82e5658 100644 --- a/submodules/StatisticsUI/Sources/MonetizationIntroScreen.swift +++ b/submodules/StatisticsUI/Sources/MonetizationIntroScreen.swift @@ -12,7 +12,7 @@ import SheetComponent import BundleIconComponent import BalancedTextComponent import MultilineTextComponent -import SolidRoundedButtonComponent +import ButtonComponent import LottieComponent import AccountContext @@ -73,7 +73,7 @@ private final class SheetContent: CombinedComponent { let title = Child(BalancedTextComponent.self) let list = Child(List.self) - let actionButton = Child(SolidRoundedButtonComponent.self) + let actionButton = Child(ButtonComponent.self) let infoBackground = Child(RoundedRectangle.self) let infoTitle = Child(MultilineTextComponent.self) @@ -284,7 +284,7 @@ private final class SheetContent: CombinedComponent { let infoBackground = infoBackground.update( component: RoundedRectangle( color: theme.list.blocksBackgroundColor, - cornerRadius: 10.0 + cornerRadius: 26.0 ), availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: totalInfoHeight), transition: .immediate @@ -307,37 +307,48 @@ private final class SheetContent: CombinedComponent { contentSize.height += infoPadding contentSize.height += spacing + var buttonTitle: [AnyComponentWithIdentity] = [] + buttonTitle.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(LottieComponent( + content: LottieComponent.AppBundleContent(name: "anim_ok"), + color: theme.list.itemCheckColors.foregroundColor, + startingPosition: .begin, + size: CGSize(width: 28.0, height: 28.0), + playOnce: state.playOnce + )))) + buttonTitle.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(ButtonTextContentComponent( + text: strings.Monetization_Intro_Understood, + badge: 0, + textColor: theme.list.itemCheckColors.foregroundColor, + badgeBackground: theme.list.itemCheckColors.foregroundColor, + badgeForeground: theme.list.itemCheckColors.fillColor + )))) + + let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: environment.safeInsets.bottom, innerDiameter: 52.0, sideInset: 30.0) let actionButton = actionButton.update( - component: SolidRoundedButtonComponent( - title: strings.Monetization_Intro_Understood, - theme: SolidRoundedButtonComponent.Theme( - backgroundColor: theme.list.itemCheckColors.fillColor, - backgroundColors: [], - foregroundColor: theme.list.itemCheckColors.foregroundColor + component: ButtonComponent( + background: ButtonComponent.Background( + style: .glass, + color: theme.list.itemCheckColors.fillColor, + foreground: theme.list.itemCheckColors.foregroundColor, + pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9) + ), + content: AnyComponentWithIdentity( + id: AnyHashable(0), + component: AnyComponent(HStack(buttonTitle, spacing: 2.0)) ), - font: .bold, - fontSize: 17.0, - height: 50.0, - cornerRadius: 10.0, - gloss: false, - iconName: nil, - animationName: nil, - iconPosition: .left, action: { component.dismiss() } ), - availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0), + availableSize: CGSize(width: context.availableSize.width - buttonInsets.left - buttonInsets.right, height: 52.0), transition: context.transition ) context.add(actionButton .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + actionButton.size.height / 2.0)) ) contentSize.height += actionButton.size.height - contentSize.height += 22.0 - - contentSize.height += environment.safeInsets.bottom - + contentSize.height += buttonInsets.bottom + state.playAnimationIfNeeded() return contentSize @@ -397,6 +408,7 @@ private final class SheetContainerComponent: CombinedComponent { }) } )), + style: .glass, backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor), followContentSizeChanges: true, externalState: sheetExternalState, diff --git a/submodules/StatisticsUI/Sources/RevenueWithdrawalController.swift b/submodules/StatisticsUI/Sources/RevenueWithdrawalController.swift index 7969836a..e6abc597 100644 --- a/submodules/StatisticsUI/Sources/RevenueWithdrawalController.swift +++ b/submodules/StatisticsUI/Sources/RevenueWithdrawalController.swift @@ -6,107 +6,148 @@ import TelegramPresentationData import PresentationDataUtils import AccountContext import PasswordSetupUI -import Markdown -import OwnershipTransferController +import ComponentFlow +import AlertComponent +import AlertInputFieldComponent -func confirmRevenueWithdrawalController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peerId: EnginePeer.Id, present: @escaping (ViewController, Any?) -> Void, completion: @escaping (String) -> Void) -> ViewController { - let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } +func confirmRevenueWithdrawalController( + context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, + peerId: EnginePeer.Id, + present: @escaping (ViewController, Any?) -> Void, + completion: @escaping (String) -> Void +) -> ViewController { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let strings = presentationData.strings + + let inputState = AlertInputFieldComponent.ExternalState() + + let doneIsEnabled: Signal = inputState.valueSignal + |> map { value in + return !value.isEmpty + } + + let doneInProgressPromise = ValuePromise(false) + + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: strings.Monetization_Withdraw_EnterPassword_Title) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(strings.Monetization_Withdraw_EnterPassword_Text)) + ) + )) + + var applyImpl: (() -> Void)? + content.append(AnyComponentWithIdentity( + id: "input", + component: AnyComponent( + AlertInputFieldComponent( + context: context, + placeholder: strings.Channel_OwnershipTransfer_PasswordPlaceholder, + isSecureTextEntry: true, + isInitiallyFocused: true, + externalState: inputState, + returnKeyAction: { + applyImpl?() + } + ) + ) + )) + + var effectiveUpdatedPresentationData: (PresentationData, Signal) + if let updatedPresentationData { + effectiveUpdatedPresentationData = updatedPresentationData + } else { + effectiveUpdatedPresentationData = (presentationData, context.sharedContext.presentationData) + } var dismissImpl: (() -> Void)? - var proceedImpl: (() -> Void)? - - let disposable = MetaDisposable() - - let contentNode = ChannelOwnershipTransferAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, title: presentationData.strings.Monetization_Withdraw_EnterPassword_Title, text: presentationData.strings.Monetization_Withdraw_EnterPassword_Text, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - dismissImpl?() - }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Monetization_Withdraw_EnterPassword_Done, action: { - proceedImpl?() - })]) - - contentNode.complete = { - proceedImpl?() - } - - let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode) - let presentationDataDisposable = (updatedPresentationData?.signal ?? context.sharedContext.presentationData).start(next: { [weak controller, weak contentNode] presentationData in - controller?.theme = AlertControllerTheme(presentationData: presentationData) - contentNode?.theme = presentationData.theme - }) - controller.dismissed = { _ in - presentationDataDisposable.dispose() - disposable.dispose() - } - dismissImpl = { [weak controller, weak contentNode] in - contentNode?.dismissInput() - controller?.dismissAnimated() - } - proceedImpl = { [weak contentNode] in - guard let contentNode = contentNode else { - return - } - contentNode.updateIsChecking(true) - - let signal = context.engine.peers.requestStarsRevenueWithdrawalUrl(peerId: peerId, ton: true, amount: nil, password: contentNode.password) - disposable.set((signal |> deliverOnMainQueue).start(next: { url in + let alertController = AlertScreen( + configuration: AlertScreen.Configuration(allowInputInset: true), + content: content, + actions: [ + .init(title: strings.Common_Cancel), + .init(title: strings.Monetization_Withdraw_EnterPassword_Done, type: .default, action: { + applyImpl?() + }, autoDismiss: false, isEnabled: doneIsEnabled, progress: doneInProgressPromise.get()) + ], + updatedPresentationData: effectiveUpdatedPresentationData + ) + applyImpl = { + doneInProgressPromise.set(true) + + let _ = (context.engine.peers.requestStarsRevenueWithdrawalUrl(peerId: peerId, ton: true, amount: nil, password: inputState.value) + |> deliverOnMainQueue).start(next: { url in dismissImpl?() completion(url) - }, error: { [weak contentNode] error in + }, error: { error in var errorTextAndActions: (String, [TextAlertAction])? switch error { - case .invalidPassword: - contentNode?.animateError() - case .limitExceeded: - errorTextAndActions = (presentationData.strings.TwoStepAuth_FloodError, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) - default: - errorTextAndActions = (presentationData.strings.Login_UnknownError, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) + case .invalidPassword: + inputState.animateError() + case .limitExceeded: + errorTextAndActions = (strings.TwoStepAuth_FloodError, [TextAlertAction(type: .defaultAction, title: strings.Common_OK, action: {})]) + default: + errorTextAndActions = (strings.Login_UnknownError, [TextAlertAction(type: .defaultAction, title: strings.Common_OK, action: {})]) } - contentNode?.updateIsChecking(false) - + doneInProgressPromise.set(false) + if let (text, actions) = errorTextAndActions { dismissImpl?() present(textAlertController(context: context, title: nil, text: text, actions: actions), nil) } - })) + }) } - - return controller + dismissImpl = { [weak alertController] in + alertController?.dismiss(completion: nil) + } + return alertController } public func revenueWithdrawalController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peerId: EnginePeer.Id, initialError: RequestStarsRevenueWithdrawalError, present: @escaping (ViewController, Any?) -> Void, completion: @escaping (String) -> Void) -> ViewController { let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } - let theme = AlertControllerTheme(presentationData: presentationData) + let strings = presentationData.strings - var title: NSAttributedString? = NSAttributedString(string: presentationData.strings.OwnershipTransfer_SecurityCheck, font: Font.semibold(presentationData.listsFontSize.itemListBaseFontSize), textColor: theme.primaryColor, paragraphAlignment: .center) + var title: String? = strings.OwnershipTransfer_SecurityCheck + var text = strings.Monetization_Withdraw_SecurityRequirements - var text = presentationData.strings.Monetization_Withdraw_SecurityRequirements - let textFontSize = presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0 - - var actions: [TextAlertAction] = [] + var actions: [AlertScreen.Action] = [ + .init(title: strings.Common_OK, type: .default) + ] switch initialError { - case .requestPassword: - return confirmRevenueWithdrawalController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, present: present, completion: completion) - case .twoStepAuthTooFresh, .authSessionTooFresh: - text = text + presentationData.strings.Monetization_Withdraw_ComeBackLater - actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})] - case .twoStepAuthMissing: - actions = [TextAlertAction(type: .genericAction, title: presentationData.strings.OwnershipTransfer_SetupTwoStepAuth, action: { + case .requestPassword: + return confirmRevenueWithdrawalController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, present: present, completion: completion) + case .twoStepAuthTooFresh, .authSessionTooFresh: + text = text + strings.Monetization_Withdraw_ComeBackLater + case .twoStepAuthMissing: + actions = [ + .init(title: strings.OwnershipTransfer_SetupTwoStepAuth, type: .default, action: { let controller = SetupTwoStepVerificationController(context: context, initialState: .automatic, stateUpdated: { update, shouldDismiss, controller in if shouldDismiss { controller.dismiss() } }) present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) - }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {})] - default: - title = nil - text = presentationData.strings.Login_UnknownError - actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})] + }), + .init(title: strings.Common_Cancel) + ] + default: + title = nil + text = strings.Login_UnknownError } - let body = MarkdownAttributeSet(font: Font.regular(textFontSize), textColor: theme.primaryColor) - let bold = MarkdownAttributeSet(font: Font.semibold(textFontSize), textColor: theme.primaryColor) - let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .center) - - return richTextAlertController(context: context, title: title, text: attributedText, actions: actions) + return AlertScreen( + context: context, + configuration: AlertScreen.Configuration(actionAlignment: .vertical), + title: title, + text: text, + actions: actions + ) } diff --git a/submodules/StatisticsUI/Sources/StarsTransactionItem.swift b/submodules/StatisticsUI/Sources/StarsTransactionItem.swift index 1ab05b98..f0fa8796 100644 --- a/submodules/StatisticsUI/Sources/StarsTransactionItem.swift +++ b/submodules/StatisticsUI/Sources/StarsTransactionItem.swift @@ -113,7 +113,7 @@ final class StarsTransactionItemNode: ListViewItemNode, ItemListItemNode { self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) } func asyncLayout() -> (_ item: StarsTransactionItem, _ params: ListViewItemLayoutParams, _ insets: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { diff --git a/submodules/StatisticsUI/Sources/StatsGraphItem.swift b/submodules/StatisticsUI/Sources/StatsGraphItem.swift index 7f8d1c85..0ab64e45 100644 --- a/submodules/StatisticsUI/Sources/StatsGraphItem.swift +++ b/submodules/StatisticsUI/Sources/StatsGraphItem.swift @@ -133,7 +133,7 @@ public final class StatsGraphItemNode: ListViewItemNode { self.activityIndicator = ActivityIndicator(type: ActivityIndicatorType.custom(.black, 16.0, 2.0, false)) self.activityIndicator.isHidden = true - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.chartContainerNode.addSubnode(self.chartNode) self.chartContainerNode.addSubnode(self.activityIndicator) diff --git a/submodules/StatisticsUI/Sources/StatsMessageItem.swift b/submodules/StatisticsUI/Sources/StatsMessageItem.swift index 1a2bf60b..641da989 100644 --- a/submodules/StatisticsUI/Sources/StatsMessageItem.swift +++ b/submodules/StatisticsUI/Sources/StatsMessageItem.swift @@ -184,7 +184,7 @@ final class StatsMessageItemNode: ListViewItemNode, ItemListItemNode { self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.containerNode.addSubnode(self.contextSourceNode) self.containerNode.targetNodeForActivationProgress = self.contextSourceNode.contentNode diff --git a/submodules/StatisticsUI/Sources/StatsOverviewItem.swift b/submodules/StatisticsUI/Sources/StatsOverviewItem.swift index a5b9d6d2..78952691 100644 --- a/submodules/StatisticsUI/Sources/StatsOverviewItem.swift +++ b/submodules/StatisticsUI/Sources/StatsOverviewItem.swift @@ -327,7 +327,7 @@ class StatsOverviewItemNode: ListViewItemNode { self.bottomLeftItem = ValueItemNode() self.bottomRightItem = ValueItemNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.clipsToBounds = true diff --git a/submodules/StatisticsUI/Sources/TransactionInfoScreen.swift b/submodules/StatisticsUI/Sources/TransactionInfoScreen.swift index 3a87d208..c652c059 100644 --- a/submodules/StatisticsUI/Sources/TransactionInfoScreen.swift +++ b/submodules/StatisticsUI/Sources/TransactionInfoScreen.swift @@ -12,11 +12,11 @@ import SheetComponent import BundleIconComponent import BalancedTextComponent import MultilineTextComponent -import SolidRoundedButtonComponent -import LottieComponent +import ButtonComponent import AccountContext import TelegramStringFormatting import PremiumPeerShortcutComponent +import GlassBarButtonComponent private final class SheetContent: CombinedComponent { typealias EnvironmentType = ViewControllerComponentContainer.Environment @@ -55,18 +55,6 @@ private final class SheetContent: CombinedComponent { } final class State: ComponentState { - var cachedCloseImage: (UIImage, PresentationTheme)? - - let playOnce = ActionSlot() - private var didPlayAnimation = false - - func playAnimationIfNeeded() { - guard !self.didPlayAnimation else { - return - } - self.didPlayAnimation = true - self.playOnce.invoke(Void()) - } } func makeState() -> State { @@ -74,25 +62,23 @@ private final class SheetContent: CombinedComponent { } static var body: Body { - let closeButton = Child(Button.self) + let closeButton = Child(GlassBarButtonComponent.self) let amount = Child(MultilineTextComponent.self) let title = Child(MultilineTextComponent.self) let date = Child(MultilineTextComponent.self) let peerShortcut = Child(PremiumPeerShortcutComponent.self) - let actionButton = Child(SolidRoundedButtonComponent.self) + let actionButton = Child(ButtonComponent.self) return { context in let environment = context.environment[EnvironmentType.self] let component = context.component - let state = context.state let theme = environment.theme let strings = environment.strings let dateTimeFormat = component.context.sharedContext.currentPresentationData.with { $0 }.dateTimeFormat - let sideInset: CGFloat = 16.0 + environment.safeInsets.left let textSideInset: CGFloat = 32.0 + environment.safeInsets.left let titleFont = Font.semibold(17.0) @@ -103,26 +89,27 @@ private final class SheetContent: CombinedComponent { var contentSize = CGSize(width: context.availableSize.width, height: 45.0) - let closeImage: UIImage - if let (image, theme) = state.cachedCloseImage, theme === environment.theme { - closeImage = image - } else { - closeImage = generateCloseButtonImage(backgroundColor: UIColor(rgb: 0x808084, alpha: 0.1), foregroundColor: theme.actionSheet.inputClearButtonColor)! - state.cachedCloseImage = (closeImage, theme) - } - let closeButton = closeButton.update( - component: Button( - content: AnyComponent(Image(image: closeImage)), - action: { [weak component] in - component?.dismiss() + component: GlassBarButtonComponent( + size: CGSize(width: 40.0, height: 40.0), + backgroundColor: theme.rootController.navigationBar.glassBarButtonBackgroundColor, + isDark: theme.overallDarkAppearance, + state: .generic, + component: AnyComponentWithIdentity(id: "close", component: AnyComponent( + BundleIconComponent( + name: "Navigation/Close", + tintColor: theme.chat.inputPanel.panelControlColor + ) + )), + action: { _ in + component.dismiss() } ), - availableSize: CGSize(width: 30.0, height: 30.0), + availableSize: CGSize(width: 40.0, height: 40.0), transition: .immediate ) context.add(closeButton - .position(CGPoint(x: context.availableSize.width - environment.safeInsets.left - closeButton.size.width, y: 28.0)) + .position(CGPoint(x: 16.0 + closeButton.size.width / 2.0, y: 16.0 + closeButton.size.height / 2.0)) ) let amountString: NSMutableAttributedString @@ -255,27 +242,30 @@ private final class SheetContent: CombinedComponent { .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + peerShortcut.size.height / 2.0)) ) contentSize.height += peerShortcut.size.height - contentSize.height += 50.0 + contentSize.height += 32.0 } else { - contentSize.height += 45.0 + contentSize.height += 27.0 } + let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: environment.safeInsets.bottom, innerDiameter: 52.0, sideInset: 30.0) let actionButton = actionButton.update( - component: SolidRoundedButtonComponent( - title: buttonTitle, - theme: SolidRoundedButtonComponent.Theme( - backgroundColor: theme.list.itemCheckColors.fillColor, - backgroundColors: [], - foregroundColor: theme.list.itemCheckColors.foregroundColor + component: ButtonComponent( + background: ButtonComponent.Background( + style: .glass, + color: theme.list.itemCheckColors.fillColor, + foreground: theme.list.itemCheckColors.foregroundColor, + pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9) + ), + content: AnyComponentWithIdentity( + id: AnyHashable(0), + component: AnyComponent(ButtonTextContentComponent( + text: buttonTitle, + badge: 0, + textColor: theme.list.itemCheckColors.foregroundColor, + badgeBackground: theme.list.itemCheckColors.foregroundColor, + badgeForeground: theme.list.itemCheckColors.fillColor + )) ), - font: .bold, - fontSize: 17.0, - height: 50.0, - cornerRadius: 10.0, - gloss: false, - iconName: nil, - animationName: nil, - iconPosition: .left, action: { component.dismiss() if let explorerUrl { @@ -283,18 +273,14 @@ private final class SheetContent: CombinedComponent { } } ), - availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0), + availableSize: CGSize(width: context.availableSize.width - buttonInsets.left - buttonInsets.right, height: 52.0), transition: context.transition ) context.add(actionButton .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + actionButton.size.height / 2.0)) ) contentSize.height += actionButton.size.height - contentSize.height += 22.0 - - contentSize.height += environment.safeInsets.bottom - - state.playAnimationIfNeeded() + contentSize.height += buttonInsets.bottom return contentSize } @@ -360,6 +346,7 @@ private final class SheetContainerComponent: CombinedComponent { }) } )), + style: .glass, backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor), followContentSizeChanges: true, externalState: sheetExternalState, diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift index 4597ed97..1e32fd99 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift @@ -1676,18 +1676,23 @@ private final class StickerPackContainer: ASDisplayNode { self.onLoading() var loadedCount = 0 - var error = false + var fetchingCount = 0 for content in contents { - if case .result = content { + switch content { + case .result: loadedCount += 1 - } else if case .none = content { - error = true + case .fetching: + fetchingCount += 1 + default: + break } } - if error { - self.onError() - } else if loadedCount == contents.count { + if fetchingCount == 0 { + if loadedCount == 0 { + self.onError() + return + } self.onReady() if !contents.isEmpty && self.currentStickerPacks.isEmpty { diff --git a/submodules/TabBarUI/BUILD b/submodules/TabBarUI/BUILD index a2bed9e0..2a3e1999 100644 --- a/submodules/TabBarUI/BUILD +++ b/submodules/TabBarUI/BUILD @@ -19,6 +19,7 @@ swift_library( "//submodules/ComponentFlow", "//submodules/Components/ComponentDisplayAdapters", "//submodules/TelegramUI/Components/TabBarComponent", + "//submodules/TelegramUI/Components/GlassControls", ], visibility = [ "//visibility:public", diff --git a/submodules/TabBarUI/Sources/TabBarContollerNode.swift b/submodules/TabBarUI/Sources/TabBarContollerNode.swift index abee8621..c4de51c3 100644 --- a/submodules/TabBarUI/Sources/TabBarContollerNode.swift +++ b/submodules/TabBarUI/Sources/TabBarContollerNode.swift @@ -6,27 +6,25 @@ import TelegramPresentationData import ComponentFlow import ComponentDisplayAdapters import TabBarComponent - -private extension ToolbarTheme { - convenience init(theme: PresentationTheme) { - self.init(barBackgroundColor: theme.rootController.tabBar.backgroundColor, barSeparatorColor: .clear, barTextColor: theme.rootController.tabBar.textColor, barSelectedTextColor: theme.rootController.tabBar.selectedTextColor) - } -} +import GlassControls final class TabBarControllerNode: ASDisplayNode { private struct Params: Equatable { let layout: ContainerViewLayout let toolbar: Toolbar? let isTabBarHidden: Bool + let currentControllerSearchState: ViewController.TabBarSearchState? init( layout: ContainerViewLayout, toolbar: Toolbar?, - isTabBarHidden: Bool + isTabBarHidden: Bool, + currentControllerSearchState: ViewController.TabBarSearchState? ) { self.layout = layout self.toolbar = toolbar self.isTabBarHidden = isTabBarHidden + self.currentControllerSearchState = currentControllerSearchState } } @@ -51,33 +49,36 @@ final class TabBarControllerNode: ASDisplayNode { } private var theme: PresentationTheme + private var strings: PresentationStrings private let itemSelected: (Int, Bool, [ASDisplayNode]) -> Void private let contextAction: (Int, ContextExtractedContentContainingView, ContextGesture) -> Void private let tabBarView = ComponentView() private let disabledOverlayNode: ASDisplayNode - private var toolbarNode: ToolbarNode? + private var toolbar: ComponentView? private let toolbarActionSelected: (ToolbarActionOption) -> Void private let disabledPressed: () -> Void + private let activateSearch: () -> Void + private let deactivateSearch: () -> Void private(set) var tabBarItems: [TabBarNodeItem] = [] private(set) var selectedIndex: Int = 0 - private(set) var currentControllerNode: ASDisplayNode? + private weak var currentController: ViewController? private var layoutResult: LayoutResult? private var isUpdateRequested: Bool = false private var isChangingSelectedIndex: Bool = false - func setCurrentControllerNode(_ node: ASDisplayNode?) -> () -> Void { - guard node !== self.currentControllerNode else { + func setCurrentController(_ controller: ViewController?) -> () -> Void { + guard controller !== self.currentController else { return {} } - let previousNode = self.currentControllerNode - self.currentControllerNode = node - if let currentControllerNode = self.currentControllerNode { + let previousNode = self.currentController?.displayNode + self.currentController = controller + if let currentControllerNode = self.currentController?.displayNode { if let previousNode { self.insertSubnode(currentControllerNode, aboveSubnode: previousNode) } else { @@ -89,14 +90,22 @@ final class TabBarControllerNode: ASDisplayNode { } return { [weak self, weak previousNode] in - if previousNode !== self?.currentControllerNode { + if previousNode !== self?.currentController?.displayNode { previousNode?.removeFromSupernode() } } } + + var currentSearchNode: ASDisplayNode? { + if let tabBarComponentView = self.tabBarView.view as? TabBarComponent.View { + return tabBarComponentView.currentSearchNode + } + return nil + } - init(theme: PresentationTheme, itemSelected: @escaping (Int, Bool, [ASDisplayNode]) -> Void, contextAction: @escaping (Int, ContextExtractedContentContainingView, ContextGesture) -> Void, swipeAction: @escaping (Int, TabBarItemSwipeDirection) -> Void, toolbarActionSelected: @escaping (ToolbarActionOption) -> Void, disabledPressed: @escaping () -> Void) { + init(theme: PresentationTheme, strings: PresentationStrings, itemSelected: @escaping (Int, Bool, [ASDisplayNode]) -> Void, contextAction: @escaping (Int, ContextExtractedContentContainingView, ContextGesture) -> Void, swipeAction: @escaping (Int, TabBarItemSwipeDirection) -> Void, toolbarActionSelected: @escaping (ToolbarActionOption) -> Void, disabledPressed: @escaping () -> Void, activateSearch: @escaping () -> Void, deactivateSearch: @escaping () -> Void) { self.theme = theme + self.strings = strings self.itemSelected = itemSelected self.contextAction = contextAction self.disabledOverlayNode = ASDisplayNode() @@ -104,7 +113,9 @@ final class TabBarControllerNode: ASDisplayNode { self.disabledOverlayNode.alpha = 0.0 self.toolbarActionSelected = toolbarActionSelected self.disabledPressed = disabledPressed - + self.activateSearch = activateSearch + self.deactivateSearch = deactivateSearch + super.init() self.setViewBlock({ @@ -146,7 +157,6 @@ final class TabBarControllerNode: ASDisplayNode { self.backgroundColor = theme.list.plainBackgroundColor self.disabledOverlayNode.backgroundColor = theme.rootController.tabBar.backgroundColor.withAlphaComponent(0.5) - self.toolbarNode?.updateTheme(ToolbarTheme(theme: theme)) self.requestUpdate() } @@ -163,7 +173,7 @@ final class TabBarControllerNode: ASDisplayNode { } func containerLayoutUpdated(_ layout: ContainerViewLayout, toolbar: Toolbar?, transition: ContainedViewLayoutTransition) -> CGFloat { - let params = Params(layout: layout, toolbar: toolbar, isTabBarHidden: self.tabBarHidden) + let params = Params(layout: layout, toolbar: toolbar, isTabBarHidden: self.tabBarHidden, currentControllerSearchState: self.currentController?.tabBarSearchState) if let layoutResult = self.layoutResult, layoutResult.params == params { return layoutResult.bottomInset } else { @@ -179,18 +189,27 @@ final class TabBarControllerNode: ASDisplayNode { } private func updateImpl(params: Params, transition: ContainedViewLayoutTransition) -> CGFloat { - var options: ContainerViewLayoutInsetOptions = [] - if params.layout.metrics.widthClass == .regular { - options.insert(.input) + var panelsBottomInset: CGFloat = params.layout.insets(options: []).bottom + if params.layout.metrics.widthClass == .regular, let inputHeight = params.layout.inputHeight, inputHeight != 0.0 { + panelsBottomInset = inputHeight + 8.0 } - - var bottomInset: CGFloat = params.layout.insets(options: options).bottom - if bottomInset == 0.0 { - bottomInset = 8.0 + if panelsBottomInset == 0.0 { + panelsBottomInset = 8.0 } else { - bottomInset = max(bottomInset, 8.0) + panelsBottomInset = max(panelsBottomInset, 8.0) + } + + var tabBarBottomInset: CGFloat = panelsBottomInset + if let currentController = self.currentController { + if let tabBarSearchState = currentController.tabBarSearchState, tabBarSearchState.isActive, let inputHeight = params.layout.inputHeight, inputHeight != 0.0 { + tabBarBottomInset = max(tabBarBottomInset, inputHeight + 8.0) + } + } + + var sideInset: CGFloat = 12.0 + if tabBarBottomInset <= 28.0 { + sideInset = 20.0 } - let sideInset: CGFloat = 20.0 var selectedId: AnyHashable? if self.selectedIndex < self.tabBarItems.count { @@ -208,6 +227,7 @@ final class TabBarControllerNode: ASDisplayNode { transition: tabBarTransition, component: AnyComponent(TabBarComponent( theme: self.theme, + strings: self.strings, items: self.tabBarItems.map { item in let itemId = AnyHashable(ObjectIdentifier(item.item)) return TabBarComponent.Item( @@ -230,13 +250,30 @@ final class TabBarControllerNode: ASDisplayNode { } ) }, + search: self.currentController?.tabBarSearchState.flatMap { tabBarSearchState in + return TabBarComponent.Search( + isActive: tabBarSearchState.isActive, + activate: { [weak self] in + guard let self else { + return + } + self.activateSearch() + }, + deactivate: { [weak self] in + guard let self else { + return + } + self.deactivateSearch() + } + ) + }, selectedId: selectedId, - isTablet: params.layout.metrics.isTablet + outerInsets: UIEdgeInsets(top: 0.0, left: sideInset, bottom: tabBarBottomInset, right: sideInset) )), environment: {}, containerSize: CGSize(width: params.layout.size.width - sideInset * 2.0, height: 100.0) ) - let tabBarFrame = CGRect(origin: CGPoint(x: floor((params.layout.size.width - tabBarSize.width) * 0.5), y: params.layout.size.height - (self.tabBarHidden ? 0.0 : (tabBarSize.height + bottomInset))), size: tabBarSize) + let tabBarFrame = CGRect(origin: CGPoint(x: floor((params.layout.size.width - tabBarSize.width) * 0.5), y: params.layout.size.height - (self.tabBarHidden ? 0.0 : (tabBarSize.height + tabBarBottomInset))), size: tabBarSize) if let tabBarComponentView = self.tabBarView.view { if tabBarComponentView.superview == nil { @@ -248,34 +285,90 @@ final class TabBarControllerNode: ASDisplayNode { transition.updateFrame(node: self.disabledOverlayNode, frame: tabBarFrame) - let toolbarHeight = 50.0 + params.layout.insets(options: options).bottom - let toolbarFrame = CGRect(origin: CGPoint(x: 0.0, y: params.layout.size.height - toolbarHeight), size: CGSize(width: params.layout.size.width, height: toolbarHeight)) + let toolbarHeight = 44.0 + let toolbarFrame = CGRect(origin: CGPoint(x: sideInset, y: params.layout.size.height - panelsBottomInset - toolbarHeight), size: CGSize(width: params.layout.size.width - sideInset * 2.0, height: toolbarHeight)) - if let toolbar = params.toolbar { - if let toolbarNode = self.toolbarNode { - transition.updateFrame(node: toolbarNode, frame: toolbarFrame) - toolbarNode.updateLayout(size: toolbarFrame.size, leftInset: params.layout.safeInsets.left, rightInset: params.layout.safeInsets.right, additionalSideInsets: params.layout.additionalInsets, bottomInset: bottomInset, toolbar: toolbar, transition: transition) + if let toolbarData = params.toolbar { + let toolbar: ComponentView + var toolbarTransition = ComponentTransition(transition) + if let current = self.toolbar { + toolbar = current } else { - let toolbarNode = ToolbarNode(theme: ToolbarTheme(theme: self.theme), displaySeparator: true, left: { [weak self] in - self?.toolbarActionSelected(.left) - }, right: { [weak self] in - self?.toolbarActionSelected(.right) - }, middle: { [weak self] in - self?.toolbarActionSelected(.middle) - }) - toolbarNode.frame = toolbarFrame - toolbarNode.updateLayout(size: toolbarFrame.size, leftInset: params.layout.safeInsets.left, rightInset: params.layout.safeInsets.right, additionalSideInsets: params.layout.additionalInsets, bottomInset: bottomInset, toolbar: toolbar, transition: .immediate) - self.addSubnode(toolbarNode) - self.toolbarNode = toolbarNode - if transition.isAnimated { - toolbarNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) - } + toolbar = ComponentView() + self.toolbar = toolbar + toolbarTransition = .immediate + } + + let _ = toolbar.update( + transition: toolbarTransition, + component: AnyComponent(GlassControlPanelComponent( + theme: self.theme, + leftItem: toolbarData.leftAction.flatMap { value in + return GlassControlPanelComponent.Item( + items: [GlassControlGroupComponent.Item( + id: "left_" + value.title, + content: .text(value.title), + action: value.isEnabled ? { [weak self] in + guard let self else { + return + } + self.toolbarActionSelected(.left) + } : nil + )], + background: .panel + ) + }, + centralItem: toolbarData.middleAction.flatMap { value in + return GlassControlPanelComponent.Item( + items: [GlassControlGroupComponent.Item( + id: "right_" + value.title, + content: .text(value.title), + action: value.isEnabled ? { [weak self] in + guard let self else { + return + } + self.toolbarActionSelected(.middle) + } : nil + )], + background: .panel + ) + }, + rightItem: toolbarData.rightAction.flatMap { value in + return GlassControlPanelComponent.Item( + items: [GlassControlGroupComponent.Item( + id: "right_" + value.title, + content: .text(value.title), + action: value.isEnabled ? { [weak self] in + guard let self else { + return + } + self.toolbarActionSelected(.right) + } : nil + )], + background: .panel + ) + }, + centerAlignmentIfPossible: true + )), + environment: {}, + containerSize: toolbarFrame.size + ) + + if let toolbarView = toolbar.view { + if toolbarView.superview == nil { + self.view.addSubview(toolbarView) + toolbarView.alpha = 0.0 + } + toolbarTransition.setFrame(view: toolbarView, frame: toolbarFrame) + ComponentTransition(transition).setAlpha(view: toolbarView, alpha: 1.0) + } + } else if let toolbar = self.toolbar { + self.toolbar = nil + if let toolbarView = toolbar.view { + ComponentTransition(transition).setAlpha(view: toolbarView, alpha: 0.0, completion: { [weak toolbarView] _ in + toolbarView?.removeFromSuperview() + }) } - } else if let toolbarNode = self.toolbarNode { - self.toolbarNode = nil - transition.updateAlpha(node: toolbarNode, alpha: 0.0, completion: { [weak toolbarNode] _ in - toolbarNode?.removeFromSupernode() - }) } return params.layout.size.height - tabBarFrame.minY diff --git a/submodules/TabBarUI/Sources/TabBarController.swift b/submodules/TabBarUI/Sources/TabBarController.swift index a3f7fe08..111fe208 100644 --- a/submodules/TabBarUI/Sources/TabBarController.swift +++ b/submodules/TabBarUI/Sources/TabBarController.swift @@ -93,9 +93,11 @@ open class TabBarControllerImpl: ViewController, TabBarController { private let pendingControllerDisposable = MetaDisposable() private var theme: PresentationTheme + private var strings: PresentationStrings - public init(theme: PresentationTheme) { + public init(theme: PresentationTheme, strings: PresentationStrings) { self.theme = theme + self.strings = strings super.init(navigationBarPresentationData: nil) @@ -152,7 +154,7 @@ open class TabBarControllerImpl: ViewController, TabBarController { } override open func loadDisplayNode() { - self.displayNode = TabBarControllerNode(theme: self.theme, itemSelected: { [weak self] index, longTap, itemNodes in + self.displayNode = TabBarControllerNode(theme: self.theme, strings: self.strings, itemSelected: { [weak self] index, longTap, itemNodes in if let strongSelf = self { if longTap, let controller = strongSelf.controllers[index] as? TabBarContainedController { controller.presentTabBarPreviewingController(sourceNodes: itemNodes) @@ -235,6 +237,16 @@ open class TabBarControllerImpl: ViewController, TabBarController { self?.currentController?.toolbarActionSelected(action: action) }, disabledPressed: { [weak self] in self?.currentController?.tabBarDisabledAction() + }, activateSearch: { [weak self] in + guard let self else { + return + } + self.currentController?.tabBarActivateSearch() + }, deactivateSearch: { [weak self] in + guard let self else { + return + } + self.currentController?.tabBarDeactivateSearch() }) self.updateSelectedIndex() @@ -263,7 +275,8 @@ open class TabBarControllerImpl: ViewController, TabBarController { } if let currentController = self.currentController { currentController.willMove(toParent: nil) - //self.tabBarControllerNode.currentControllerNode = nil + currentController.tabBarSearchStateUpdated = nil + currentController.currentTabBarSearchNode = nil if animated { currentController.view.layer.animateScale(from: 1.0, to: transitionScale, duration: 0.12, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { completed in @@ -286,7 +299,7 @@ open class TabBarControllerImpl: ViewController, TabBarController { currentController.willMove(toParent: self) self.addChild(currentController) - let commit = self.tabBarControllerNode.setCurrentControllerNode(currentController.displayNode) + let commit = self.tabBarControllerNode.setCurrentController(currentController) if animated { currentController.view.layer.animateScale(from: transitionScale, to: 1.0, duration: 0.15, delay: 0.1, timingFunction: kCAMediaTimingFunctionSpring) currentController.view.layer.allowsGroupOpacity = true @@ -303,6 +316,22 @@ open class TabBarControllerImpl: ViewController, TabBarController { currentController.displayNode.recursivelyEnsureDisplaySynchronously(true) self.statusBar.statusBarStyle = currentController.statusBar.statusBarStyle + + currentController.tabBarSearchStateUpdated = { [weak self] transition in + guard let self else { + return + } + if let layout = self.validLayout { + self.containerLayoutUpdated(layout, transition: transition) + } + } + + currentController.currentTabBarSearchNode = { [weak self] in + guard let self else { + return nil + } + return self.tabBarControllerNode.currentSearchNode + } } if let layout = self.validLayout { diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 1f0fce87..455ff48a 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -417,6 +417,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1279654347] = { return Api.InputMedia.parse_inputMediaPhoto($0) } dict[-440664550] = { return Api.InputMedia.parse_inputMediaPhotoExternal($0) } dict[261416433] = { return Api.InputMedia.parse_inputMediaPoll($0) } + dict[-207018934] = { return Api.InputMedia.parse_inputMediaStakeDice($0) } dict[-1979852936] = { return Api.InputMedia.parse_inputMediaStory($0) } dict[-1614454818] = { return Api.InputMedia.parse_inputMediaTodo($0) } dict[58495792] = { return Api.InputMedia.parse_inputMediaUploadedDocument($0) } @@ -432,6 +433,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1548122514] = { return Api.InputNotifyPeer.parse_inputNotifyForumTopic($0) } dict[-1195615476] = { return Api.InputNotifyPeer.parse_inputNotifyPeer($0) } dict[423314455] = { return Api.InputNotifyPeer.parse_inputNotifyUsers($0) } + dict[1528613672] = { return Api.InputPasskeyCredential.parse_inputPasskeyCredentialFirebasePNV($0) } dict[1009235855] = { return Api.InputPasskeyCredential.parse_inputPasskeyCredentialPublicKey($0) } dict[-1021329078] = { return Api.InputPasskeyResponse.parse_inputPasskeyResponseLogin($0) } dict[1046713180] = { return Api.InputPasskeyResponse.parse_inputPasskeyResponseRegister($0) } @@ -572,7 +574,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1098720356] = { return Api.MediaArea.parse_mediaAreaVenue($0) } dict[1235637404] = { return Api.MediaArea.parse_mediaAreaWeather($0) } dict[-808853502] = { return Api.MediaAreaCoordinates.parse_mediaAreaCoordinates($0) } - dict[-1188071729] = { return Api.Message.parse_message($0) } + dict[-1665888023] = { return Api.Message.parse_message($0) } dict[-1868117372] = { return Api.Message.parse_messageEmpty($0) } dict[2055212554] = { return Api.Message.parse_messageService($0) } dict[-872240531] = { return Api.MessageAction.parse_messageActionBoostApply($0) } @@ -660,7 +662,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1386050360] = { return Api.MessageExtendedMedia.parse_messageExtendedMediaPreview($0) } dict[1313731771] = { return Api.MessageFwdHeader.parse_messageFwdHeader($0) } dict[1882335561] = { return Api.MessageMedia.parse_messageMediaContact($0) } - dict[1065280907] = { return Api.MessageMedia.parse_messageMediaDice($0) } + dict[147581959] = { return Api.MessageMedia.parse_messageMediaDice($0) } dict[1389939929] = { return Api.MessageMedia.parse_messageMediaDocument($0) } dict[1038967584] = { return Api.MessageMedia.parse_messageMediaEmpty($0) } dict[-38694904] = { return Api.MessageMedia.parse_messageMediaGame($0) } @@ -1127,6 +1129,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-302247650] = { return Api.Update.parse_updateDraftMessage($0) } dict[457133559] = { return Api.Update.parse_updateEditChannelMessage($0) } dict[-469536605] = { return Api.Update.parse_updateEditMessage($0) } + dict[-73640838] = { return Api.Update.parse_updateEmojiGameInfo($0) } dict[386986326] = { return Api.Update.parse_updateEncryptedChatTyping($0) } dict[956179895] = { return Api.Update.parse_updateEncryptedMessagesRead($0) } dict[-1264392051] = { return Api.Update.parse_updateEncryption($0) } @@ -1417,6 +1420,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-253500010] = { return Api.messages.Dialogs.parse_dialogsNotModified($0) } dict[1910543603] = { return Api.messages.Dialogs.parse_dialogsSlice($0) } dict[-1506535550] = { return Api.messages.DiscussionMessage.parse_discussionMessage($0) } + dict[1155883043] = { return Api.messages.EmojiGameInfo.parse_emojiGameDiceInfo($0) } + dict[1508266805] = { return Api.messages.EmojiGameInfo.parse_emojiGameUnavailable($0) } + dict[-634726841] = { return Api.messages.EmojiGameOutcome.parse_emojiGameOutcome($0) } dict[-2011186869] = { return Api.messages.EmojiGroups.parse_emojiGroups($0) } dict[1874111879] = { return Api.messages.EmojiGroups.parse_emojiGroupsNotModified($0) } dict[410107472] = { return Api.messages.ExportedChatInvite.parse_exportedChatInvite($0) } @@ -2573,6 +2579,10 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.messages.DiscussionMessage: _1.serialize(buffer, boxed) + case let _1 as Api.messages.EmojiGameInfo: + _1.serialize(buffer, boxed) + case let _1 as Api.messages.EmojiGameOutcome: + _1.serialize(buffer, boxed) case let _1 as Api.messages.EmojiGroups: _1.serialize(buffer, boxed) case let _1 as Api.messages.ExportedChatInvite: diff --git a/submodules/TelegramApi/Sources/Api1.swift b/submodules/TelegramApi/Sources/Api1.swift index 2177c01e..1056c070 100644 --- a/submodules/TelegramApi/Sources/Api1.swift +++ b/submodules/TelegramApi/Sources/Api1.swift @@ -24,12 +24,8 @@ public extension Api { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.AccountDaysTTL.accountDaysTTL(days: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.AccountDaysTTL.accountDaysTTL(days: _1!) } } @@ -88,12 +84,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 3) == 0) || _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.AttachMenuBot.attachMenuBot(flags: _1!, botId: _2!, shortName: _3!, peerTypes: _4, icons: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.AttachMenuBot.attachMenuBot(flags: _1!, botId: _2!, shortName: _3!, peerTypes: _4, icons: _5!) } } @@ -144,12 +140,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.AttachMenuBotIcon.attachMenuBotIcon(flags: _1!, name: _2!, icon: _3!, colors: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.AttachMenuBotIcon.attachMenuBotIcon(flags: _1!, name: _2!, icon: _3!, colors: _4) } } @@ -184,12 +179,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.AttachMenuBotIconColor.attachMenuBotIconColor(name: _1!, color: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.AttachMenuBotIconColor.attachMenuBotIconColor(name: _1!, color: _2!) } } @@ -249,12 +241,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.AttachMenuBots.attachMenuBots(hash: _1!, bots: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.AttachMenuBots.attachMenuBots(hash: _1!, bots: _2!, users: _3!) } public static func parse_attachMenuBotsNotModified(_ reader: BufferReader) -> AttachMenuBots? { return Api.AttachMenuBots.attachMenuBotsNotModified @@ -300,12 +290,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.AttachMenuBotsBot.attachMenuBotsBot(bot: _1!, users: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.AttachMenuBotsBot.attachMenuBotsBot(bot: _1!, users: _2!) } } @@ -420,12 +407,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.AuctionBidLevel.auctionBidLevel(pos: _1!, amount: _2!, date: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.AuctionBidLevel.auctionBidLevel(pos: _1!, amount: _2!, date: _3!) } } @@ -504,12 +489,20 @@ public extension Api { let _c11 = _11 != nil let _c12 = _12 != nil let _c13 = _13 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 { - return Api.Authorization.authorization(flags: _1!, hash: _2!, deviceModel: _3!, platform: _4!, systemVersion: _5!, apiId: _6!, appName: _7!, appVersion: _8!, dateCreated: _9!, dateActive: _10!, ip: _11!, country: _12!, region: _13!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + if !_c13 { return nil } + return Api.Authorization.authorization(flags: _1!, hash: _2!, deviceModel: _3!, platform: _4!, systemVersion: _5!, apiId: _6!, appName: _7!, appVersion: _8!, dateCreated: _9!, dateActive: _10!, ip: _11!, country: _12!, region: _13!) } } @@ -564,12 +557,14 @@ public extension Api { let _c5 = _5 != nil let _c6 = _6 != nil let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.AutoDownloadSettings.autoDownloadSettings(flags: _1!, photoSizeMax: _2!, videoSizeMax: _3!, fileSizeMax: _4!, videoUploadMaxbitrate: _5!, smallQueueActiveOperationsMax: _6!, largeQueueActiveOperationsMax: _7!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.AutoDownloadSettings.autoDownloadSettings(flags: _1!, photoSizeMax: _2!, videoSizeMax: _3!, fileSizeMax: _4!, videoUploadMaxbitrate: _5!, smallQueueActiveOperationsMax: _6!, largeQueueActiveOperationsMax: _7!) } } @@ -608,12 +603,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.AutoSaveException.autoSaveException(peer: _1!, settings: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.AutoSaveException.autoSaveException(peer: _1!, settings: _2!) } } @@ -648,12 +640,9 @@ public extension Api { if Int(_1!) & Int(1 << 2) != 0 {_2 = reader.readInt64() } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 2) == 0) || _2 != nil - if _c1 && _c2 { - return Api.AutoSaveSettings.autoSaveSettings(flags: _1!, videoMaxSize: _2) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.AutoSaveSettings.autoSaveSettings(flags: _1!, videoMaxSize: _2) } } @@ -704,12 +693,13 @@ public extension Api { let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil let _c5 = _5 != nil let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.AvailableEffect.availableEffect(flags: _1!, id: _2!, emoticon: _3!, staticIconId: _4, effectStickerId: _5!, effectAnimationId: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.AvailableEffect.availableEffect(flags: _1!, id: _2!, emoticon: _3!, staticIconId: _4, effectStickerId: _5!, effectAnimationId: _6) } } @@ -790,12 +780,17 @@ public extension Api { let _c8 = _8 != nil let _c9 = (Int(_1!) & Int(1 << 1) == 0) || _9 != nil let _c10 = (Int(_1!) & Int(1 << 1) == 0) || _10 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 { - return Api.AvailableReaction.availableReaction(flags: _1!, reaction: _2!, title: _3!, staticIcon: _4!, appearAnimation: _5!, selectAnimation: _6!, activateAnimation: _7!, effectAnimation: _8!, aroundAnimation: _9, centerIcon: _10) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + return Api.AvailableReaction.availableReaction(flags: _1!, reaction: _2!, title: _3!, staticIcon: _4!, appearAnimation: _5!, selectAnimation: _6!, activateAnimation: _7!, effectAnimation: _8!, aroundAnimation: _9, centerIcon: _10) } } @@ -830,12 +825,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.BankCardOpenUrl.bankCardOpenUrl(url: _1!, name: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.BankCardOpenUrl.bankCardOpenUrl(url: _1!, name: _2!) } } @@ -954,12 +946,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.Birthday.birthday(flags: _1!, day: _2!, month: _3!, year: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.Birthday.birthday(flags: _1!, day: _2!, month: _3!, year: _4) } } @@ -1062,12 +1053,16 @@ public extension Api { let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil let _c8 = (Int(_1!) & Int(1 << 5) == 0) || _8 != nil let _c9 = (Int(_1!) & Int(1 << 6) == 0) || _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.Boost.boost(flags: _1!, id: _2!, userId: _3, giveawayMsgId: _4, date: _5!, expires: _6!, usedGiftSlug: _7, multiplier: _8, stars: _9) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + return Api.Boost.boost(flags: _1!, id: _2!, userId: _3, giveawayMsgId: _4, date: _5!, expires: _6!, usedGiftSlug: _7, multiplier: _8, stars: _9) } } @@ -1143,12 +1138,16 @@ public extension Api { let _c7 = _7 != nil let _c8 = (Int(_1!) & Int(1 << 0) == 0) || _8 != nil let _c9 = _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.BotApp.botApp(flags: _1!, id: _2!, accessHash: _3!, shortName: _4!, title: _5!, description: _6!, photo: _7!, document: _8, hash: _9!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + return Api.BotApp.botApp(flags: _1!, id: _2!, accessHash: _3!, shortName: _4!, title: _5!, description: _6!, photo: _7!, document: _8, hash: _9!) } public static func parse_botAppNotModified(_ reader: BufferReader) -> BotApp? { return Api.BotApp.botAppNotModified @@ -1202,12 +1201,13 @@ public extension Api { let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 4) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.BotAppSettings.botAppSettings(flags: _1!, placeholderPath: _2, backgroundColor: _3, backgroundDarkColor: _4, headerColor: _5, headerDarkColor: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.BotAppSettings.botAppSettings(flags: _1!, placeholderPath: _2, backgroundColor: _3, backgroundDarkColor: _4, headerColor: _5, headerDarkColor: _6) } } diff --git a/submodules/TelegramApi/Sources/Api10.swift b/submodules/TelegramApi/Sources/Api10.swift index 6da06977..08d007a9 100644 --- a/submodules/TelegramApi/Sources/Api10.swift +++ b/submodules/TelegramApi/Sources/Api10.swift @@ -138,12 +138,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputFileLocation.inputDocumentFileLocation(id: _1!, accessHash: _2!, fileReference: _3!, thumbSize: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.InputFileLocation.inputDocumentFileLocation(id: _1!, accessHash: _2!, fileReference: _3!, thumbSize: _4!) } public static func parse_inputEncryptedFileLocation(_ reader: BufferReader) -> InputFileLocation? { var _1: Int64? @@ -152,12 +151,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputFileLocation.inputEncryptedFileLocation(id: _1!, accessHash: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputFileLocation.inputEncryptedFileLocation(id: _1!, accessHash: _2!) } public static func parse_inputFileLocation(_ reader: BufferReader) -> InputFileLocation? { var _1: Int64? @@ -172,12 +168,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputFileLocation.inputFileLocation(volumeId: _1!, localId: _2!, secret: _3!, fileReference: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.InputFileLocation.inputFileLocation(volumeId: _1!, localId: _2!, secret: _3!, fileReference: _4!) } public static func parse_inputGroupCallStream(_ reader: BufferReader) -> InputFileLocation? { var _1: Int32? @@ -200,12 +195,13 @@ public extension Api { let _c4 = _4 != nil let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.InputFileLocation.inputGroupCallStream(flags: _1!, call: _2!, timeMs: _3!, scale: _4!, videoChannel: _5, videoQuality: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.InputFileLocation.inputGroupCallStream(flags: _1!, call: _2!, timeMs: _3!, scale: _4!, videoChannel: _5, videoQuality: _6) } public static func parse_inputPeerPhotoFileLocation(_ reader: BufferReader) -> InputFileLocation? { var _1: Int32? @@ -219,12 +215,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.InputFileLocation.inputPeerPhotoFileLocation(flags: _1!, peer: _2!, photoId: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.InputFileLocation.inputPeerPhotoFileLocation(flags: _1!, peer: _2!, photoId: _3!) } public static func parse_inputPhotoFileLocation(_ reader: BufferReader) -> InputFileLocation? { var _1: Int64? @@ -239,12 +233,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputFileLocation.inputPhotoFileLocation(id: _1!, accessHash: _2!, fileReference: _3!, thumbSize: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.InputFileLocation.inputPhotoFileLocation(id: _1!, accessHash: _2!, fileReference: _3!, thumbSize: _4!) } public static func parse_inputPhotoLegacyFileLocation(_ reader: BufferReader) -> InputFileLocation? { var _1: Int64? @@ -265,12 +258,13 @@ public extension Api { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.InputFileLocation.inputPhotoLegacyFileLocation(id: _1!, accessHash: _2!, fileReference: _3!, volumeId: _4!, localId: _5!, secret: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.InputFileLocation.inputPhotoLegacyFileLocation(id: _1!, accessHash: _2!, fileReference: _3!, volumeId: _4!, localId: _5!, secret: _6!) } public static func parse_inputSecureFileLocation(_ reader: BufferReader) -> InputFileLocation? { var _1: Int64? @@ -279,12 +273,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputFileLocation.inputSecureFileLocation(id: _1!, accessHash: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputFileLocation.inputSecureFileLocation(id: _1!, accessHash: _2!) } public static func parse_inputStickerSetThumb(_ reader: BufferReader) -> InputFileLocation? { var _1: Api.InputStickerSet? @@ -295,12 +286,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputFileLocation.inputStickerSetThumb(stickerset: _1!, thumbVersion: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputFileLocation.inputStickerSetThumb(stickerset: _1!, thumbVersion: _2!) } public static func parse_inputTakeoutFileLocation(_ reader: BufferReader) -> InputFileLocation? { return Api.InputFileLocation.inputTakeoutFileLocation @@ -340,12 +328,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputFolderPeer.inputFolderPeer(peer: _1!, folderId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputFolderPeer.inputFolderPeer(peer: _1!, folderId: _2!) } } @@ -390,12 +375,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputGame.inputGameID(id: _1!, accessHash: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputGame.inputGameID(id: _1!, accessHash: _2!) } public static func parse_inputGameShortName(_ reader: BufferReader) -> InputGame? { var _1: Api.InputUser? @@ -406,12 +388,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputGame.inputGameShortName(botId: _1!, shortName: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputGame.inputGameShortName(botId: _1!, shortName: _2!) } } @@ -463,12 +442,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputGeoPoint.inputGeoPoint(flags: _1!, lat: _2!, long: _3!, accuracyRadius: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.InputGeoPoint.inputGeoPoint(flags: _1!, lat: _2!, long: _3!, accuracyRadius: _4) } public static func parse_inputGeoPointEmpty(_ reader: BufferReader) -> InputGeoPoint? { return Api.InputGeoPoint.inputGeoPointEmpty @@ -524,34 +502,23 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputGroupCall.inputGroupCall(id: _1!, accessHash: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputGroupCall.inputGroupCall(id: _1!, accessHash: _2!) } public static func parse_inputGroupCallInviteMessage(_ reader: BufferReader) -> InputGroupCall? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.InputGroupCall.inputGroupCallInviteMessage(msgId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputGroupCall.inputGroupCallInviteMessage(msgId: _1!) } public static func parse_inputGroupCallSlug(_ reader: BufferReader) -> InputGroupCall? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.InputGroupCall.inputGroupCallSlug(slug: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputGroupCall.inputGroupCallSlug(slug: _1!) } } @@ -731,23 +698,16 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputInvoice.inputInvoiceBusinessBotTransferStars(bot: _1!, stars: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputInvoice.inputInvoiceBusinessBotTransferStars(bot: _1!, stars: _2!) } public static func parse_inputInvoiceChatInviteSubscription(_ reader: BufferReader) -> InputInvoice? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.InputInvoice.inputInvoiceChatInviteSubscription(hash: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputInvoice.inputInvoiceChatInviteSubscription(hash: _1!) } public static func parse_inputInvoiceMessage(_ reader: BufferReader) -> InputInvoice? { var _1: Api.InputPeer? @@ -758,12 +718,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputInvoice.inputInvoiceMessage(peer: _1!, msgId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputInvoice.inputInvoiceMessage(peer: _1!, msgId: _2!) } public static func parse_inputInvoicePremiumAuthCode(_ reader: BufferReader) -> InputInvoice? { var _1: Api.InputStorePaymentPurpose? @@ -771,12 +728,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.InputStorePaymentPurpose } let _c1 = _1 != nil - if _c1 { - return Api.InputInvoice.inputInvoicePremiumAuthCode(purpose: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputInvoice.inputInvoicePremiumAuthCode(purpose: _1!) } public static func parse_inputInvoicePremiumGiftCode(_ reader: BufferReader) -> InputInvoice? { var _1: Api.InputStorePaymentPurpose? @@ -789,12 +742,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputInvoice.inputInvoicePremiumGiftCode(purpose: _1!, option: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputInvoice.inputInvoicePremiumGiftCode(purpose: _1!, option: _2!) } public static func parse_inputInvoicePremiumGiftStars(_ reader: BufferReader) -> InputInvoice? { var _1: Int32? @@ -813,23 +763,18 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputInvoice.inputInvoicePremiumGiftStars(flags: _1!, userId: _2!, months: _3!, message: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.InputInvoice.inputInvoicePremiumGiftStars(flags: _1!, userId: _2!, months: _3!, message: _4) } public static func parse_inputInvoiceSlug(_ reader: BufferReader) -> InputInvoice? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.InputInvoice.inputInvoiceSlug(slug: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputInvoice.inputInvoiceSlug(slug: _1!) } public static func parse_inputInvoiceStarGift(_ reader: BufferReader) -> InputInvoice? { var _1: Int32? @@ -848,12 +793,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputInvoice.inputInvoiceStarGift(flags: _1!, peer: _2!, giftId: _3!, message: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.InputInvoice.inputInvoiceStarGift(flags: _1!, peer: _2!, giftId: _3!, message: _4) } public static func parse_inputInvoiceStarGiftAuctionBid(_ reader: BufferReader) -> InputInvoice? { var _1: Int32? @@ -875,12 +819,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.InputInvoice.inputInvoiceStarGiftAuctionBid(flags: _1!, peer: _2, giftId: _3!, bidAmount: _4!, message: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.InputInvoice.inputInvoiceStarGiftAuctionBid(flags: _1!, peer: _2, giftId: _3!, bidAmount: _4!, message: _5) } public static func parse_inputInvoiceStarGiftDropOriginalDetails(_ reader: BufferReader) -> InputInvoice? { var _1: Api.InputSavedStarGift? @@ -888,12 +832,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.InputSavedStarGift } let _c1 = _1 != nil - if _c1 { - return Api.InputInvoice.inputInvoiceStarGiftDropOriginalDetails(stargift: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputInvoice.inputInvoiceStarGiftDropOriginalDetails(stargift: _1!) } public static func parse_inputInvoiceStarGiftPrepaidUpgrade(_ reader: BufferReader) -> InputInvoice? { var _1: Api.InputPeer? @@ -904,12 +844,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputInvoice.inputInvoiceStarGiftPrepaidUpgrade(peer: _1!, hash: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputInvoice.inputInvoiceStarGiftPrepaidUpgrade(peer: _1!, hash: _2!) } public static func parse_inputInvoiceStarGiftResale(_ reader: BufferReader) -> InputInvoice? { var _1: Int32? @@ -923,12 +860,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.InputInvoice.inputInvoiceStarGiftResale(flags: _1!, slug: _2!, toId: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.InputInvoice.inputInvoiceStarGiftResale(flags: _1!, slug: _2!, toId: _3!) } public static func parse_inputInvoiceStarGiftTransfer(_ reader: BufferReader) -> InputInvoice? { var _1: Api.InputSavedStarGift? @@ -941,12 +876,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputInvoice.inputInvoiceStarGiftTransfer(stargift: _1!, toId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputInvoice.inputInvoiceStarGiftTransfer(stargift: _1!, toId: _2!) } public static func parse_inputInvoiceStarGiftUpgrade(_ reader: BufferReader) -> InputInvoice? { var _1: Int32? @@ -957,12 +889,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputInvoice.inputInvoiceStarGiftUpgrade(flags: _1!, stargift: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputInvoice.inputInvoiceStarGiftUpgrade(flags: _1!, stargift: _2!) } public static func parse_inputInvoiceStars(_ reader: BufferReader) -> InputInvoice? { var _1: Api.InputStorePaymentPurpose? @@ -970,12 +899,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.InputStorePaymentPurpose } let _c1 = _1 != nil - if _c1 { - return Api.InputInvoice.inputInvoiceStars(purpose: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputInvoice.inputInvoiceStars(purpose: _1!) } } diff --git a/submodules/TelegramApi/Sources/Api11.swift b/submodules/TelegramApi/Sources/Api11.swift index 4914c64e..f44bd4e7 100644 --- a/submodules/TelegramApi/Sources/Api11.swift +++ b/submodules/TelegramApi/Sources/Api11.swift @@ -13,6 +13,7 @@ public extension Api { case inputMediaPhoto(flags: Int32, id: Api.InputPhoto, ttlSeconds: Int32?) case inputMediaPhotoExternal(flags: Int32, url: String, ttlSeconds: Int32?) case inputMediaPoll(flags: Int32, poll: Api.Poll, correctAnswers: [Buffer]?, solution: String?, solutionEntities: [Api.MessageEntity]?) + case inputMediaStakeDice(gameHash: String, tonAmount: Int64, clientSeed: Buffer) case inputMediaStory(peer: Api.InputPeer, id: Int32) case inputMediaTodo(todo: Api.TodoList) case inputMediaUploadedDocument(flags: Int32, file: Api.InputFile, thumb: Api.InputFile?, mimeType: String, attributes: [Api.DocumentAttribute], stickers: [Api.InputDocument]?, videoCover: Api.InputPhoto?, videoTimestamp: Int32?, ttlSeconds: Int32?) @@ -148,6 +149,14 @@ public extension Api { item.serialize(buffer, true) }} break + case .inputMediaStakeDice(let gameHash, let tonAmount, let clientSeed): + if boxed { + buffer.appendInt32(-207018934) + } + serializeString(gameHash, buffer: buffer, boxed: false) + serializeInt64(tonAmount, buffer: buffer, boxed: false) + serializeBytes(clientSeed, buffer: buffer, boxed: false) + break case .inputMediaStory(let peer, let id): if boxed { buffer.appendInt32(-1979852936) @@ -245,6 +254,8 @@ public extension Api { return ("inputMediaPhotoExternal", [("flags", flags as Any), ("url", url as Any), ("ttlSeconds", ttlSeconds as Any)]) case .inputMediaPoll(let flags, let poll, let correctAnswers, let solution, let solutionEntities): return ("inputMediaPoll", [("flags", flags as Any), ("poll", poll as Any), ("correctAnswers", correctAnswers as Any), ("solution", solution as Any), ("solutionEntities", solutionEntities as Any)]) + case .inputMediaStakeDice(let gameHash, let tonAmount, let clientSeed): + return ("inputMediaStakeDice", [("gameHash", gameHash as Any), ("tonAmount", tonAmount as Any), ("clientSeed", clientSeed as Any)]) case .inputMediaStory(let peer, let id): return ("inputMediaStory", [("peer", peer as Any), ("id", id as Any)]) case .inputMediaTodo(let todo): @@ -273,23 +284,18 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputMedia.inputMediaContact(phoneNumber: _1!, firstName: _2!, lastName: _3!, vcard: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.InputMedia.inputMediaContact(phoneNumber: _1!, firstName: _2!, lastName: _3!, vcard: _4!) } public static func parse_inputMediaDice(_ reader: BufferReader) -> InputMedia? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.InputMedia.inputMediaDice(emoticon: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputMedia.inputMediaDice(emoticon: _1!) } public static func parse_inputMediaDocument(_ reader: BufferReader) -> InputMedia? { var _1: Int32? @@ -314,12 +320,13 @@ public extension Api { let _c4 = (Int(_1!) & Int(1 << 4) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.InputMedia.inputMediaDocument(flags: _1!, id: _2!, videoCover: _3, videoTimestamp: _4, ttlSeconds: _5, query: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.InputMedia.inputMediaDocument(flags: _1!, id: _2!, videoCover: _3, videoTimestamp: _4, ttlSeconds: _5, query: _6) } public static func parse_inputMediaDocumentExternal(_ reader: BufferReader) -> InputMedia? { var _1: Int32? @@ -339,12 +346,12 @@ public extension Api { let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.InputMedia.inputMediaDocumentExternal(flags: _1!, url: _2!, ttlSeconds: _3, videoCover: _4, videoTimestamp: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.InputMedia.inputMediaDocumentExternal(flags: _1!, url: _2!, ttlSeconds: _3, videoCover: _4, videoTimestamp: _5) } public static func parse_inputMediaEmpty(_ reader: BufferReader) -> InputMedia? { return Api.InputMedia.inputMediaEmpty @@ -355,12 +362,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.InputGame } let _c1 = _1 != nil - if _c1 { - return Api.InputMedia.inputMediaGame(id: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputMedia.inputMediaGame(id: _1!) } public static func parse_inputMediaGeoLive(_ reader: BufferReader) -> InputMedia? { var _1: Int32? @@ -380,12 +383,12 @@ public extension Api { let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.InputMedia.inputMediaGeoLive(flags: _1!, geoPoint: _2!, heading: _3, period: _4, proximityNotificationRadius: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.InputMedia.inputMediaGeoLive(flags: _1!, geoPoint: _2!, heading: _3, period: _4, proximityNotificationRadius: _5) } public static func parse_inputMediaGeoPoint(_ reader: BufferReader) -> InputMedia? { var _1: Api.InputGeoPoint? @@ -393,12 +396,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.InputGeoPoint } let _c1 = _1 != nil - if _c1 { - return Api.InputMedia.inputMediaGeoPoint(geoPoint: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputMedia.inputMediaGeoPoint(geoPoint: _1!) } public static func parse_inputMediaInvoice(_ reader: BufferReader) -> InputMedia? { var _1: Int32? @@ -439,12 +438,17 @@ public extension Api { let _c8 = _8 != nil let _c9 = (Int(_1!) & Int(1 << 1) == 0) || _9 != nil let _c10 = (Int(_1!) & Int(1 << 2) == 0) || _10 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 { - return Api.InputMedia.inputMediaInvoice(flags: _1!, title: _2!, description: _3!, photo: _4, invoice: _5!, payload: _6!, provider: _7, providerData: _8!, startParam: _9, extendedMedia: _10) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + return Api.InputMedia.inputMediaInvoice(flags: _1!, title: _2!, description: _3!, photo: _4, invoice: _5!, payload: _6!, provider: _7, providerData: _8!, startParam: _9, extendedMedia: _10) } public static func parse_inputMediaPaidMedia(_ reader: BufferReader) -> InputMedia? { var _1: Int32? @@ -461,12 +465,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputMedia.inputMediaPaidMedia(flags: _1!, starsAmount: _2!, extendedMedia: _3!, payload: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.InputMedia.inputMediaPaidMedia(flags: _1!, starsAmount: _2!, extendedMedia: _3!, payload: _4) } public static func parse_inputMediaPhoto(_ reader: BufferReader) -> InputMedia? { var _1: Int32? @@ -480,12 +483,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.InputMedia.inputMediaPhoto(flags: _1!, id: _2!, ttlSeconds: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.InputMedia.inputMediaPhoto(flags: _1!, id: _2!, ttlSeconds: _3) } public static func parse_inputMediaPhotoExternal(_ reader: BufferReader) -> InputMedia? { var _1: Int32? @@ -497,12 +498,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.InputMedia.inputMediaPhotoExternal(flags: _1!, url: _2!, ttlSeconds: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.InputMedia.inputMediaPhotoExternal(flags: _1!, url: _2!, ttlSeconds: _3) } public static func parse_inputMediaPoll(_ reader: BufferReader) -> InputMedia? { var _1: Int32? @@ -526,12 +525,27 @@ public extension Api { let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.InputMedia.inputMediaPoll(flags: _1!, poll: _2!, correctAnswers: _3, solution: _4, solutionEntities: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.InputMedia.inputMediaPoll(flags: _1!, poll: _2!, correctAnswers: _3, solution: _4, solutionEntities: _5) + } + public static func parse_inputMediaStakeDice(_ reader: BufferReader) -> InputMedia? { + var _1: String? + _1 = parseString(reader) + var _2: Int64? + _2 = reader.readInt64() + var _3: Buffer? + _3 = parseBytes(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.InputMedia.inputMediaStakeDice(gameHash: _1!, tonAmount: _2!, clientSeed: _3!) } public static func parse_inputMediaStory(_ reader: BufferReader) -> InputMedia? { var _1: Api.InputPeer? @@ -542,12 +556,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputMedia.inputMediaStory(peer: _1!, id: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputMedia.inputMediaStory(peer: _1!, id: _2!) } public static func parse_inputMediaTodo(_ reader: BufferReader) -> InputMedia? { var _1: Api.TodoList? @@ -555,12 +566,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.TodoList } let _c1 = _1 != nil - if _c1 { - return Api.InputMedia.inputMediaTodo(todo: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputMedia.inputMediaTodo(todo: _1!) } public static func parse_inputMediaUploadedDocument(_ reader: BufferReader) -> InputMedia? { var _1: Int32? @@ -600,12 +607,16 @@ public extension Api { let _c7 = (Int(_1!) & Int(1 << 6) == 0) || _7 != nil let _c8 = (Int(_1!) & Int(1 << 7) == 0) || _8 != nil let _c9 = (Int(_1!) & Int(1 << 1) == 0) || _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.InputMedia.inputMediaUploadedDocument(flags: _1!, file: _2!, thumb: _3, mimeType: _4!, attributes: _5!, stickers: _6, videoCover: _7, videoTimestamp: _8, ttlSeconds: _9) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + return Api.InputMedia.inputMediaUploadedDocument(flags: _1!, file: _2!, thumb: _3, mimeType: _4!, attributes: _5!, stickers: _6, videoCover: _7, videoTimestamp: _8, ttlSeconds: _9) } public static func parse_inputMediaUploadedPhoto(_ reader: BufferReader) -> InputMedia? { var _1: Int32? @@ -624,12 +635,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputMedia.inputMediaUploadedPhoto(flags: _1!, file: _2!, stickers: _3, ttlSeconds: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.InputMedia.inputMediaUploadedPhoto(flags: _1!, file: _2!, stickers: _3, ttlSeconds: _4) } public static func parse_inputMediaVenue(_ reader: BufferReader) -> InputMedia? { var _1: Api.InputGeoPoint? @@ -652,12 +662,13 @@ public extension Api { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.InputMedia.inputMediaVenue(geoPoint: _1!, title: _2!, address: _3!, provider: _4!, venueId: _5!, venueType: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.InputMedia.inputMediaVenue(geoPoint: _1!, title: _2!, address: _3!, provider: _4!, venueId: _5!, venueType: _6!) } public static func parse_inputMediaWebPage(_ reader: BufferReader) -> InputMedia? { var _1: Int32? @@ -666,12 +677,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputMedia.inputMediaWebPage(flags: _1!, url: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputMedia.inputMediaWebPage(flags: _1!, url: _2!) } } @@ -733,23 +741,16 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputMessage.inputMessageCallbackQuery(id: _1!, queryId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputMessage.inputMessageCallbackQuery(id: _1!, queryId: _2!) } public static func parse_inputMessageID(_ reader: BufferReader) -> InputMessage? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.InputMessage.inputMessageID(id: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputMessage.inputMessageID(id: _1!) } public static func parse_inputMessagePinned(_ reader: BufferReader) -> InputMessage? { return Api.InputMessage.inputMessagePinned @@ -758,12 +759,8 @@ public extension Api { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.InputMessage.inputMessageReplyTo(id: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputMessage.inputMessageReplyTo(id: _1!) } } @@ -842,12 +839,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputNotifyPeer.inputNotifyForumTopic(peer: _1!, topMsgId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputNotifyPeer.inputNotifyForumTopic(peer: _1!, topMsgId: _2!) } public static func parse_inputNotifyPeer(_ reader: BufferReader) -> InputNotifyPeer? { var _1: Api.InputPeer? @@ -855,12 +849,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.InputPeer } let _c1 = _1 != nil - if _c1 { - return Api.InputNotifyPeer.inputNotifyPeer(peer: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputNotifyPeer.inputNotifyPeer(peer: _1!) } public static func parse_inputNotifyUsers(_ reader: BufferReader) -> InputNotifyPeer? { return Api.InputNotifyPeer.inputNotifyUsers @@ -870,10 +860,17 @@ public extension Api { } public extension Api { enum InputPasskeyCredential: TypeConstructorDescription { + case inputPasskeyCredentialFirebasePNV(pnvToken: String) case inputPasskeyCredentialPublicKey(id: String, rawId: String, response: Api.InputPasskeyResponse) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { + case .inputPasskeyCredentialFirebasePNV(let pnvToken): + if boxed { + buffer.appendInt32(1528613672) + } + serializeString(pnvToken, buffer: buffer, boxed: false) + break case .inputPasskeyCredentialPublicKey(let id, let rawId, let response): if boxed { buffer.appendInt32(1009235855) @@ -887,11 +884,20 @@ public extension Api { public func descriptionFields() -> (String, [(String, Any)]) { switch self { + case .inputPasskeyCredentialFirebasePNV(let pnvToken): + return ("inputPasskeyCredentialFirebasePNV", [("pnvToken", pnvToken as Any)]) case .inputPasskeyCredentialPublicKey(let id, let rawId, let response): return ("inputPasskeyCredentialPublicKey", [("id", id as Any), ("rawId", rawId as Any), ("response", response as Any)]) } } + public static func parse_inputPasskeyCredentialFirebasePNV(_ reader: BufferReader) -> InputPasskeyCredential? { + var _1: String? + _1 = parseString(reader) + let _c1 = _1 != nil + if !_c1 { return nil } + return Api.InputPasskeyCredential.inputPasskeyCredentialFirebasePNV(pnvToken: _1!) + } public static func parse_inputPasskeyCredentialPublicKey(_ reader: BufferReader) -> InputPasskeyCredential? { var _1: String? _1 = parseString(reader) @@ -904,12 +910,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.InputPasskeyCredential.inputPasskeyCredentialPublicKey(id: _1!, rawId: _2!, response: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.InputPasskeyCredential.inputPasskeyCredentialPublicKey(id: _1!, rawId: _2!, response: _3!) } } @@ -964,12 +968,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputPasskeyResponse.inputPasskeyResponseLogin(clientData: _1!, authenticatorData: _2!, signature: _3!, userHandle: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.InputPasskeyResponse.inputPasskeyResponseLogin(clientData: _1!, authenticatorData: _2!, signature: _3!, userHandle: _4!) } public static func parse_inputPasskeyResponseRegister(_ reader: BufferReader) -> InputPasskeyResponse? { var _1: Api.DataJSON? @@ -980,122 +983,9 @@ public extension Api { _2 = parseBytes(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputPasskeyResponse.inputPasskeyResponseRegister(clientData: _1!, attestationData: _2!) - } - else { - return nil - } - } - - } -} -public extension Api { - enum InputPaymentCredentials: TypeConstructorDescription { - case inputPaymentCredentials(flags: Int32, data: Api.DataJSON) - case inputPaymentCredentialsApplePay(paymentData: Api.DataJSON) - case inputPaymentCredentialsGooglePay(paymentToken: Api.DataJSON) - case inputPaymentCredentialsSaved(id: String, tmpPassword: Buffer) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .inputPaymentCredentials(let flags, let data): - if boxed { - buffer.appendInt32(873977640) - } - serializeInt32(flags, buffer: buffer, boxed: false) - data.serialize(buffer, true) - break - case .inputPaymentCredentialsApplePay(let paymentData): - if boxed { - buffer.appendInt32(178373535) - } - paymentData.serialize(buffer, true) - break - case .inputPaymentCredentialsGooglePay(let paymentToken): - if boxed { - buffer.appendInt32(-1966921727) - } - paymentToken.serialize(buffer, true) - break - case .inputPaymentCredentialsSaved(let id, let tmpPassword): - if boxed { - buffer.appendInt32(-1056001329) - } - serializeString(id, buffer: buffer, boxed: false) - serializeBytes(tmpPassword, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .inputPaymentCredentials(let flags, let data): - return ("inputPaymentCredentials", [("flags", flags as Any), ("data", data as Any)]) - case .inputPaymentCredentialsApplePay(let paymentData): - return ("inputPaymentCredentialsApplePay", [("paymentData", paymentData as Any)]) - case .inputPaymentCredentialsGooglePay(let paymentToken): - return ("inputPaymentCredentialsGooglePay", [("paymentToken", paymentToken as Any)]) - case .inputPaymentCredentialsSaved(let id, let tmpPassword): - return ("inputPaymentCredentialsSaved", [("id", id as Any), ("tmpPassword", tmpPassword as Any)]) - } - } - - public static func parse_inputPaymentCredentials(_ reader: BufferReader) -> InputPaymentCredentials? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Api.DataJSON? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.DataJSON - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputPaymentCredentials.inputPaymentCredentials(flags: _1!, data: _2!) - } - else { - return nil - } - } - public static func parse_inputPaymentCredentialsApplePay(_ reader: BufferReader) -> InputPaymentCredentials? { - var _1: Api.DataJSON? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.DataJSON - } - let _c1 = _1 != nil - if _c1 { - return Api.InputPaymentCredentials.inputPaymentCredentialsApplePay(paymentData: _1!) - } - else { - return nil - } - } - public static func parse_inputPaymentCredentialsGooglePay(_ reader: BufferReader) -> InputPaymentCredentials? { - var _1: Api.DataJSON? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.DataJSON - } - let _c1 = _1 != nil - if _c1 { - return Api.InputPaymentCredentials.inputPaymentCredentialsGooglePay(paymentToken: _1!) - } - else { - return nil - } - } - public static func parse_inputPaymentCredentialsSaved(_ reader: BufferReader) -> InputPaymentCredentials? { - var _1: String? - _1 = parseString(reader) - var _2: Buffer? - _2 = parseBytes(reader) - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputPaymentCredentials.inputPaymentCredentialsSaved(id: _1!, tmpPassword: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputPasskeyResponse.inputPasskeyResponseRegister(clientData: _1!, attestationData: _2!) } } diff --git a/submodules/TelegramApi/Sources/Api12.swift b/submodules/TelegramApi/Sources/Api12.swift index ae88c149..42fc616a 100644 --- a/submodules/TelegramApi/Sources/Api12.swift +++ b/submodules/TelegramApi/Sources/Api12.swift @@ -1,3 +1,99 @@ +public extension Api { + enum InputPaymentCredentials: TypeConstructorDescription { + case inputPaymentCredentials(flags: Int32, data: Api.DataJSON) + case inputPaymentCredentialsApplePay(paymentData: Api.DataJSON) + case inputPaymentCredentialsGooglePay(paymentToken: Api.DataJSON) + case inputPaymentCredentialsSaved(id: String, tmpPassword: Buffer) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .inputPaymentCredentials(let flags, let data): + if boxed { + buffer.appendInt32(873977640) + } + serializeInt32(flags, buffer: buffer, boxed: false) + data.serialize(buffer, true) + break + case .inputPaymentCredentialsApplePay(let paymentData): + if boxed { + buffer.appendInt32(178373535) + } + paymentData.serialize(buffer, true) + break + case .inputPaymentCredentialsGooglePay(let paymentToken): + if boxed { + buffer.appendInt32(-1966921727) + } + paymentToken.serialize(buffer, true) + break + case .inputPaymentCredentialsSaved(let id, let tmpPassword): + if boxed { + buffer.appendInt32(-1056001329) + } + serializeString(id, buffer: buffer, boxed: false) + serializeBytes(tmpPassword, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .inputPaymentCredentials(let flags, let data): + return ("inputPaymentCredentials", [("flags", flags as Any), ("data", data as Any)]) + case .inputPaymentCredentialsApplePay(let paymentData): + return ("inputPaymentCredentialsApplePay", [("paymentData", paymentData as Any)]) + case .inputPaymentCredentialsGooglePay(let paymentToken): + return ("inputPaymentCredentialsGooglePay", [("paymentToken", paymentToken as Any)]) + case .inputPaymentCredentialsSaved(let id, let tmpPassword): + return ("inputPaymentCredentialsSaved", [("id", id as Any), ("tmpPassword", tmpPassword as Any)]) + } + } + + public static func parse_inputPaymentCredentials(_ reader: BufferReader) -> InputPaymentCredentials? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.DataJSON? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.DataJSON + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputPaymentCredentials.inputPaymentCredentials(flags: _1!, data: _2!) + } + public static func parse_inputPaymentCredentialsApplePay(_ reader: BufferReader) -> InputPaymentCredentials? { + var _1: Api.DataJSON? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.DataJSON + } + let _c1 = _1 != nil + if !_c1 { return nil } + return Api.InputPaymentCredentials.inputPaymentCredentialsApplePay(paymentData: _1!) + } + public static func parse_inputPaymentCredentialsGooglePay(_ reader: BufferReader) -> InputPaymentCredentials? { + var _1: Api.DataJSON? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.DataJSON + } + let _c1 = _1 != nil + if !_c1 { return nil } + return Api.InputPaymentCredentials.inputPaymentCredentialsGooglePay(paymentToken: _1!) + } + public static func parse_inputPaymentCredentialsSaved(_ reader: BufferReader) -> InputPaymentCredentials? { + var _1: String? + _1 = parseString(reader) + var _2: Buffer? + _2 = parseBytes(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputPaymentCredentials.inputPaymentCredentialsSaved(id: _1!, tmpPassword: _2!) + } + + } +} public extension Api { indirect enum InputPeer: TypeConstructorDescription { case inputPeerChannel(channelId: Int64, accessHash: Int64) @@ -87,12 +183,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputPeer.inputPeerChannel(channelId: _1!, accessHash: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputPeer.inputPeerChannel(channelId: _1!, accessHash: _2!) } public static func parse_inputPeerChannelFromMessage(_ reader: BufferReader) -> InputPeer? { var _1: Api.InputPeer? @@ -106,23 +199,17 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.InputPeer.inputPeerChannelFromMessage(peer: _1!, msgId: _2!, channelId: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.InputPeer.inputPeerChannelFromMessage(peer: _1!, msgId: _2!, channelId: _3!) } public static func parse_inputPeerChat(_ reader: BufferReader) -> InputPeer? { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.InputPeer.inputPeerChat(chatId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputPeer.inputPeerChat(chatId: _1!) } public static func parse_inputPeerEmpty(_ reader: BufferReader) -> InputPeer? { return Api.InputPeer.inputPeerEmpty @@ -137,12 +224,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputPeer.inputPeerUser(userId: _1!, accessHash: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputPeer.inputPeerUser(userId: _1!, accessHash: _2!) } public static func parse_inputPeerUserFromMessage(_ reader: BufferReader) -> InputPeer? { var _1: Api.InputPeer? @@ -156,12 +240,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.InputPeer.inputPeerUserFromMessage(peer: _1!, msgId: _2!, userId: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.InputPeer.inputPeerUserFromMessage(peer: _1!, msgId: _2!, userId: _3!) } } @@ -232,12 +314,15 @@ public extension Api { let _c6 = (Int(_1!) & Int(1 << 6) == 0) || _6 != nil let _c7 = (Int(_1!) & Int(1 << 7) == 0) || _7 != nil let _c8 = (Int(_1!) & Int(1 << 8) == 0) || _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.InputPeerNotifySettings.inputPeerNotifySettings(flags: _1!, showPreviews: _2, silent: _3, muteUntil: _4, sound: _5, storiesMuted: _6, storiesHideSender: _7, storiesSound: _8) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.InputPeerNotifySettings.inputPeerNotifySettings(flags: _1!, showPreviews: _2, silent: _3, muteUntil: _4, sound: _5, storiesMuted: _6, storiesHideSender: _7, storiesSound: _8) } } @@ -272,12 +357,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputPhoneCall.inputPhoneCall(id: _1!, accessHash: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputPhoneCall.inputPhoneCall(id: _1!, accessHash: _2!) } } @@ -325,12 +407,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.InputPhoto.inputPhoto(id: _1!, accessHash: _2!, fileReference: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.InputPhoto.inputPhoto(id: _1!, accessHash: _2!, fileReference: _3!) } public static func parse_inputPhotoEmpty(_ reader: BufferReader) -> InputPhoto? { return Api.InputPhoto.inputPhotoEmpty @@ -671,12 +751,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) } let _c1 = _1 != nil - if _c1 { - return Api.InputPrivacyRule.inputPrivacyValueAllowChatParticipants(chats: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputPrivacyRule.inputPrivacyValueAllowChatParticipants(chats: _1!) } public static func parse_inputPrivacyValueAllowCloseFriends(_ reader: BufferReader) -> InputPrivacyRule? { return Api.InputPrivacyRule.inputPrivacyValueAllowCloseFriends @@ -693,12 +769,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.InputUser.self) } let _c1 = _1 != nil - if _c1 { - return Api.InputPrivacyRule.inputPrivacyValueAllowUsers(users: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputPrivacyRule.inputPrivacyValueAllowUsers(users: _1!) } public static func parse_inputPrivacyValueDisallowAll(_ reader: BufferReader) -> InputPrivacyRule? { return Api.InputPrivacyRule.inputPrivacyValueDisallowAll @@ -712,12 +784,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) } let _c1 = _1 != nil - if _c1 { - return Api.InputPrivacyRule.inputPrivacyValueDisallowChatParticipants(chats: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputPrivacyRule.inputPrivacyValueDisallowChatParticipants(chats: _1!) } public static func parse_inputPrivacyValueDisallowContacts(_ reader: BufferReader) -> InputPrivacyRule? { return Api.InputPrivacyRule.inputPrivacyValueDisallowContacts @@ -728,12 +796,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.InputUser.self) } let _c1 = _1 != nil - if _c1 { - return Api.InputPrivacyRule.inputPrivacyValueDisallowUsers(users: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputPrivacyRule.inputPrivacyValueDisallowUsers(users: _1!) } } diff --git a/submodules/TelegramApi/Sources/Api13.swift b/submodules/TelegramApi/Sources/Api13.swift index 43504702..fa5243cf 100644 --- a/submodules/TelegramApi/Sources/Api13.swift +++ b/submodules/TelegramApi/Sources/Api13.swift @@ -33,23 +33,15 @@ public extension Api { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.InputQuickReplyShortcut.inputQuickReplyShortcut(shortcut: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputQuickReplyShortcut.inputQuickReplyShortcut(shortcut: _1!) } public static func parse_inputQuickReplyShortcutId(_ reader: BufferReader) -> InputQuickReplyShortcut? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.InputQuickReplyShortcut.inputQuickReplyShortcutId(shortcutId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputQuickReplyShortcut.inputQuickReplyShortcutId(shortcutId: _1!) } } @@ -141,12 +133,16 @@ public extension Api { let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil let _c8 = (Int(_1!) & Int(1 << 5) == 0) || _8 != nil let _c9 = (Int(_1!) & Int(1 << 6) == 0) || _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.InputReplyTo.inputReplyToMessage(flags: _1!, replyToMsgId: _2!, topMsgId: _3, replyToPeerId: _4, quoteText: _5, quoteEntities: _6, quoteOffset: _7, monoforumPeerId: _8, todoItemId: _9) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + return Api.InputReplyTo.inputReplyToMessage(flags: _1!, replyToMsgId: _2!, topMsgId: _3, replyToPeerId: _4, quoteText: _5, quoteEntities: _6, quoteOffset: _7, monoforumPeerId: _8, todoItemId: _9) } public static func parse_inputReplyToMonoForum(_ reader: BufferReader) -> InputReplyTo? { var _1: Api.InputPeer? @@ -154,12 +150,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.InputPeer } let _c1 = _1 != nil - if _c1 { - return Api.InputReplyTo.inputReplyToMonoForum(monoforumPeerId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputReplyTo.inputReplyToMonoForum(monoforumPeerId: _1!) } public static func parse_inputReplyToStory(_ reader: BufferReader) -> InputReplyTo? { var _1: Api.InputPeer? @@ -170,12 +162,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputReplyTo.inputReplyToStory(peer: _1!, storyId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputReplyTo.inputReplyToStory(peer: _1!, storyId: _2!) } } @@ -230,34 +219,23 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputSavedStarGift.inputSavedStarGiftChat(peer: _1!, savedId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputSavedStarGift.inputSavedStarGiftChat(peer: _1!, savedId: _2!) } public static func parse_inputSavedStarGiftSlug(_ reader: BufferReader) -> InputSavedStarGift? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.InputSavedStarGift.inputSavedStarGiftSlug(slug: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputSavedStarGift.inputSavedStarGiftSlug(slug: _1!) } public static func parse_inputSavedStarGiftUser(_ reader: BufferReader) -> InputSavedStarGift? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.InputSavedStarGift.inputSavedStarGiftUser(msgId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputSavedStarGift.inputSavedStarGiftUser(msgId: _1!) } } @@ -305,12 +283,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputSecureFile.inputSecureFile(id: _1!, accessHash: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputSecureFile.inputSecureFile(id: _1!, accessHash: _2!) } public static func parse_inputSecureFileUploaded(_ reader: BufferReader) -> InputSecureFile? { var _1: Int64? @@ -328,12 +303,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.InputSecureFile.inputSecureFileUploaded(id: _1!, parts: _2!, md5Checksum: _3!, fileHash: _4!, secret: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.InputSecureFile.inputSecureFileUploaded(id: _1!, parts: _2!, md5Checksum: _3!, fileHash: _4!, secret: _5!) } } @@ -420,12 +395,16 @@ public extension Api { let _c7 = (Int(_1!) & Int(1 << 6) == 0) || _7 != nil let _c8 = (Int(_1!) & Int(1 << 4) == 0) || _8 != nil let _c9 = (Int(_1!) & Int(1 << 5) == 0) || _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.InputSecureValue.inputSecureValue(flags: _1!, type: _2!, data: _3, frontSide: _4, reverseSide: _5, selfie: _6, translation: _7, files: _8, plainData: _9) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + return Api.InputSecureValue.inputSecureValue(flags: _1!, type: _2!, data: _3, frontSide: _4, reverseSide: _5, selfie: _6, translation: _7, files: _8, plainData: _9) } } @@ -480,12 +459,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.InputSingleMedia.inputSingleMedia(flags: _1!, media: _2!, randomId: _3!, message: _4!, entities: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.InputSingleMedia.inputSingleMedia(flags: _1!, media: _2!, randomId: _3!, message: _4!, entities: _5) } } @@ -525,23 +504,15 @@ public extension Api { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.InputStarGiftAuction.inputStarGiftAuction(giftId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputStarGiftAuction.inputStarGiftAuction(giftId: _1!) } public static func parse_inputStarGiftAuctionSlug(_ reader: BufferReader) -> InputStarGiftAuction? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.InputStarGiftAuction.inputStarGiftAuctionSlug(slug: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputStarGiftAuction.inputStarGiftAuctionSlug(slug: _1!) } } @@ -576,12 +547,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputStarsTransaction.inputStarsTransaction(flags: _1!, id: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputStarsTransaction.inputStarsTransaction(flags: _1!, id: _2!) } } @@ -718,12 +686,8 @@ public extension Api { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.InputStickerSet.inputStickerSetDice(emoticon: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputStickerSet.inputStickerSetDice(emoticon: _1!) } public static func parse_inputStickerSetEmojiChannelDefaultStatuses(_ reader: BufferReader) -> InputStickerSet? { return Api.InputStickerSet.inputStickerSetEmojiChannelDefaultStatuses @@ -747,12 +711,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputStickerSet.inputStickerSetID(id: _1!, accessHash: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputStickerSet.inputStickerSetID(id: _1!, accessHash: _2!) } public static func parse_inputStickerSetPremiumGifts(_ reader: BufferReader) -> InputStickerSet? { return Api.InputStickerSet.inputStickerSetPremiumGifts @@ -761,12 +722,8 @@ public extension Api { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.InputStickerSet.inputStickerSetShortName(shortName: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputStickerSet.inputStickerSetShortName(shortName: _1!) } public static func parse_inputStickerSetTonGifts(_ reader: BufferReader) -> InputStickerSet? { return Api.InputStickerSet.inputStickerSetTonGifts @@ -820,12 +777,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.InputStickerSetItem.inputStickerSetItem(flags: _1!, document: _2!, emoji: _3!, maskCoords: _4, keywords: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.InputStickerSetItem.inputStickerSetItem(flags: _1!, document: _2!, emoji: _3!, maskCoords: _4, keywords: _5) } } @@ -867,12 +824,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.InputDocument } let _c1 = _1 != nil - if _c1 { - return Api.InputStickeredMedia.inputStickeredMediaDocument(id: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputStickeredMedia.inputStickeredMediaDocument(id: _1!) } public static func parse_inputStickeredMediaPhoto(_ reader: BufferReader) -> InputStickeredMedia? { var _1: Api.InputPhoto? @@ -880,12 +833,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.InputPhoto } let _c1 = _1 != nil - if _c1 { - return Api.InputStickeredMedia.inputStickeredMediaPhoto(id: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputStickeredMedia.inputStickeredMediaPhoto(id: _1!) } } @@ -1047,12 +996,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.InputStorePaymentPurpose.inputStorePaymentAuthCode(flags: _1!, phoneNumber: _2!, phoneCodeHash: _3!, currency: _4!, amount: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.InputStorePaymentPurpose.inputStorePaymentAuthCode(flags: _1!, phoneNumber: _2!, phoneCodeHash: _3!, currency: _4!, amount: _5!) } public static func parse_inputStorePaymentGiftPremium(_ reader: BufferReader) -> InputStorePaymentPurpose? { var _1: Api.InputUser? @@ -1066,12 +1015,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.InputStorePaymentPurpose.inputStorePaymentGiftPremium(userId: _1!, currency: _2!, amount: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.InputStorePaymentPurpose.inputStorePaymentGiftPremium(userId: _1!, currency: _2!, amount: _3!) } public static func parse_inputStorePaymentPremiumGiftCode(_ reader: BufferReader) -> InputStorePaymentPurpose? { var _1: Int32? @@ -1098,12 +1045,13 @@ public extension Api { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.InputStorePaymentPurpose.inputStorePaymentPremiumGiftCode(flags: _1!, users: _2!, boostPeer: _3, currency: _4!, amount: _5!, message: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.InputStorePaymentPurpose.inputStorePaymentPremiumGiftCode(flags: _1!, users: _2!, boostPeer: _3, currency: _4!, amount: _5!, message: _6) } public static func parse_inputStorePaymentPremiumGiveaway(_ reader: BufferReader) -> InputStorePaymentPurpose? { var _1: Int32? @@ -1139,23 +1087,23 @@ public extension Api { let _c7 = _7 != nil let _c8 = _8 != nil let _c9 = _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.InputStorePaymentPurpose.inputStorePaymentPremiumGiveaway(flags: _1!, boostPeer: _2!, additionalPeers: _3, countriesIso2: _4, prizeDescription: _5, randomId: _6!, untilDate: _7!, currency: _8!, amount: _9!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + return Api.InputStorePaymentPurpose.inputStorePaymentPremiumGiveaway(flags: _1!, boostPeer: _2!, additionalPeers: _3, countriesIso2: _4, prizeDescription: _5, randomId: _6!, untilDate: _7!, currency: _8!, amount: _9!) } public static func parse_inputStorePaymentPremiumSubscription(_ reader: BufferReader) -> InputStorePaymentPurpose? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.InputStorePaymentPurpose.inputStorePaymentPremiumSubscription(flags: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputStorePaymentPurpose.inputStorePaymentPremiumSubscription(flags: _1!) } public static func parse_inputStorePaymentStarsGift(_ reader: BufferReader) -> InputStorePaymentPurpose? { var _1: Api.InputUser? @@ -1172,12 +1120,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputStorePaymentPurpose.inputStorePaymentStarsGift(userId: _1!, stars: _2!, currency: _3!, amount: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.InputStorePaymentPurpose.inputStorePaymentStarsGift(userId: _1!, stars: _2!, currency: _3!, amount: _4!) } public static func parse_inputStorePaymentStarsGiveaway(_ reader: BufferReader) -> InputStorePaymentPurpose? { var _1: Int32? @@ -1219,12 +1166,18 @@ public extension Api { let _c9 = _9 != nil let _c10 = _10 != nil let _c11 = _11 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 { - return Api.InputStorePaymentPurpose.inputStorePaymentStarsGiveaway(flags: _1!, stars: _2!, boostPeer: _3!, additionalPeers: _4, countriesIso2: _5, prizeDescription: _6, randomId: _7!, untilDate: _8!, currency: _9!, amount: _10!, users: _11!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + return Api.InputStorePaymentPurpose.inputStorePaymentStarsGiveaway(flags: _1!, stars: _2!, boostPeer: _3!, additionalPeers: _4, countriesIso2: _5, prizeDescription: _6, randomId: _7!, untilDate: _8!, currency: _9!, amount: _10!, users: _11!) } public static func parse_inputStorePaymentStarsTopup(_ reader: BufferReader) -> InputStorePaymentPurpose? { var _1: Int32? @@ -1244,12 +1197,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.InputStorePaymentPurpose.inputStorePaymentStarsTopup(flags: _1!, stars: _2!, currency: _3!, amount: _4!, spendPurposePeer: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.InputStorePaymentPurpose.inputStorePaymentStarsTopup(flags: _1!, stars: _2!, currency: _3!, amount: _4!, spendPurposePeer: _5) } } diff --git a/submodules/TelegramApi/Sources/Api14.swift b/submodules/TelegramApi/Sources/Api14.swift index 5f436a06..4b917cf6 100644 --- a/submodules/TelegramApi/Sources/Api14.swift +++ b/submodules/TelegramApi/Sources/Api14.swift @@ -37,23 +37,16 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputTheme.inputTheme(id: _1!, accessHash: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputTheme.inputTheme(id: _1!, accessHash: _2!) } public static func parse_inputThemeSlug(_ reader: BufferReader) -> InputTheme? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.InputTheme.inputThemeSlug(slug: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputTheme.inputThemeSlug(slug: _1!) } } @@ -120,12 +113,14 @@ public extension Api { let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.InputThemeSettings.inputThemeSettings(flags: _1!, baseTheme: _2!, accentColor: _3!, outboxAccentColor: _4, messageColors: _5, wallpaper: _6, wallpaperSettings: _7) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.InputThemeSettings.inputThemeSettings(flags: _1!, baseTheme: _2!, accentColor: _3!, outboxAccentColor: _4, messageColors: _5, wallpaper: _6, wallpaperSettings: _7) } } @@ -189,12 +184,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputUser.inputUser(userId: _1!, accessHash: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputUser.inputUser(userId: _1!, accessHash: _2!) } public static func parse_inputUserEmpty(_ reader: BufferReader) -> InputUser? { return Api.InputUser.inputUserEmpty @@ -211,12 +203,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.InputUser.inputUserFromMessage(peer: _1!, msgId: _2!, userId: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.InputUser.inputUserFromMessage(peer: _1!, msgId: _2!, userId: _3!) } public static func parse_inputUserSelf(_ reader: BufferReader) -> InputUser? { return Api.InputUser.inputUserSelf @@ -272,34 +262,23 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputWallPaper.inputWallPaper(id: _1!, accessHash: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputWallPaper.inputWallPaper(id: _1!, accessHash: _2!) } public static func parse_inputWallPaperNoFile(_ reader: BufferReader) -> InputWallPaper? { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.InputWallPaper.inputWallPaperNoFile(id: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputWallPaper.inputWallPaperNoFile(id: _1!) } public static func parse_inputWallPaperSlug(_ reader: BufferReader) -> InputWallPaper? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.InputWallPaper.inputWallPaperSlug(slug: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputWallPaper.inputWallPaperSlug(slug: _1!) } } @@ -348,12 +327,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputWebDocument.inputWebDocument(url: _1!, size: _2!, mimeType: _3!, attributes: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.InputWebDocument.inputWebDocument(url: _1!, size: _2!, mimeType: _3!, attributes: _4!) } } @@ -422,12 +400,11 @@ public extension Api { let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputWebFileLocation.inputWebFileAudioAlbumThumbLocation(flags: _1!, document: _2, title: _3, performer: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.InputWebFileLocation.inputWebFileAudioAlbumThumbLocation(flags: _1!, document: _2, title: _3, performer: _4) } public static func parse_inputWebFileGeoPointLocation(_ reader: BufferReader) -> InputWebFileLocation? { var _1: Api.InputGeoPoint? @@ -450,12 +427,13 @@ public extension Api { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.InputWebFileLocation.inputWebFileGeoPointLocation(geoPoint: _1!, accessHash: _2!, w: _3!, h: _4!, zoom: _5!, scale: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.InputWebFileLocation.inputWebFileGeoPointLocation(geoPoint: _1!, accessHash: _2!, w: _3!, h: _4!, zoom: _5!, scale: _6!) } public static func parse_inputWebFileLocation(_ reader: BufferReader) -> InputWebFileLocation? { var _1: String? @@ -464,12 +442,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputWebFileLocation.inputWebFileLocation(url: _1!, accessHash: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputWebFileLocation.inputWebFileLocation(url: _1!, accessHash: _2!) } } @@ -536,12 +511,14 @@ public extension Api { let _c5 = (Int(_1!) & Int(1 << 8) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 10) == 0) || _6 != nil let _c7 = (Int(_1!) & Int(1 << 11) == 0) || _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.Invoice.invoice(flags: _1!, currency: _2!, prices: _3!, maxTipAmount: _4, suggestedTipAmounts: _5, termsUrl: _6, subscriptionPeriod: _7) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.Invoice.invoice(flags: _1!, currency: _2!, prices: _3!, maxTipAmount: _4, suggestedTipAmounts: _5, termsUrl: _6, subscriptionPeriod: _7) } } @@ -578,12 +555,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.JSONObjectValue.jsonObjectValue(key: _1!, value: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.JSONObjectValue.jsonObjectValue(key: _1!, value: _2!) } } @@ -669,12 +643,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.JSONValue.self) } let _c1 = _1 != nil - if _c1 { - return Api.JSONValue.jsonArray(value: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.JSONValue.jsonArray(value: _1!) } public static func parse_jsonBool(_ reader: BufferReader) -> JSONValue? { var _1: Api.Bool? @@ -682,12 +652,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Bool } let _c1 = _1 != nil - if _c1 { - return Api.JSONValue.jsonBool(value: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.JSONValue.jsonBool(value: _1!) } public static func parse_jsonNull(_ reader: BufferReader) -> JSONValue? { return Api.JSONValue.jsonNull @@ -696,12 +662,8 @@ public extension Api { var _1: Double? _1 = reader.readDouble() let _c1 = _1 != nil - if _c1 { - return Api.JSONValue.jsonNumber(value: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.JSONValue.jsonNumber(value: _1!) } public static func parse_jsonObject(_ reader: BufferReader) -> JSONValue? { var _1: [Api.JSONObjectValue]? @@ -709,23 +671,15 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.JSONObjectValue.self) } let _c1 = _1 != nil - if _c1 { - return Api.JSONValue.jsonObject(value: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.JSONValue.jsonObject(value: _1!) } public static func parse_jsonString(_ reader: BufferReader) -> JSONValue? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.JSONValue.jsonString(value: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.JSONValue.jsonString(value: _1!) } } @@ -955,12 +909,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.KeyboardButton.inputKeyboardButtonRequestPeer(flags: _1!, text: _2!, buttonId: _3!, peerType: _4!, maxQuantity: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.KeyboardButton.inputKeyboardButtonRequestPeer(flags: _1!, text: _2!, buttonId: _3!, peerType: _4!, maxQuantity: _5!) } public static func parse_inputKeyboardButtonUrlAuth(_ reader: BufferReader) -> KeyboardButton? { var _1: Int32? @@ -980,12 +934,12 @@ public extension Api { let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.KeyboardButton.inputKeyboardButtonUrlAuth(flags: _1!, text: _2!, fwdText: _3, url: _4!, bot: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.KeyboardButton.inputKeyboardButtonUrlAuth(flags: _1!, text: _2!, fwdText: _3, url: _4!, bot: _5!) } public static func parse_inputKeyboardButtonUserProfile(_ reader: BufferReader) -> KeyboardButton? { var _1: String? @@ -996,34 +950,23 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.KeyboardButton.inputKeyboardButtonUserProfile(text: _1!, userId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.KeyboardButton.inputKeyboardButtonUserProfile(text: _1!, userId: _2!) } public static func parse_keyboardButton(_ reader: BufferReader) -> KeyboardButton? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.KeyboardButton.keyboardButton(text: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.KeyboardButton.keyboardButton(text: _1!) } public static func parse_keyboardButtonBuy(_ reader: BufferReader) -> KeyboardButton? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.KeyboardButton.keyboardButtonBuy(text: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.KeyboardButton.keyboardButtonBuy(text: _1!) } public static func parse_keyboardButtonCallback(_ reader: BufferReader) -> KeyboardButton? { var _1: Int32? @@ -1035,12 +978,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.KeyboardButton.keyboardButtonCallback(flags: _1!, text: _2!, data: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.KeyboardButton.keyboardButtonCallback(flags: _1!, text: _2!, data: _3!) } public static func parse_keyboardButtonCopy(_ reader: BufferReader) -> KeyboardButton? { var _1: String? @@ -1049,34 +990,23 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.KeyboardButton.keyboardButtonCopy(text: _1!, copyText: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.KeyboardButton.keyboardButtonCopy(text: _1!, copyText: _2!) } public static func parse_keyboardButtonGame(_ reader: BufferReader) -> KeyboardButton? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.KeyboardButton.keyboardButtonGame(text: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.KeyboardButton.keyboardButtonGame(text: _1!) } public static func parse_keyboardButtonRequestGeoLocation(_ reader: BufferReader) -> KeyboardButton? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.KeyboardButton.keyboardButtonRequestGeoLocation(text: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.KeyboardButton.keyboardButtonRequestGeoLocation(text: _1!) } public static func parse_keyboardButtonRequestPeer(_ reader: BufferReader) -> KeyboardButton? { var _1: String? @@ -1093,23 +1023,18 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.KeyboardButton.keyboardButtonRequestPeer(text: _1!, buttonId: _2!, peerType: _3!, maxQuantity: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.KeyboardButton.keyboardButtonRequestPeer(text: _1!, buttonId: _2!, peerType: _3!, maxQuantity: _4!) } public static func parse_keyboardButtonRequestPhone(_ reader: BufferReader) -> KeyboardButton? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.KeyboardButton.keyboardButtonRequestPhone(text: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.KeyboardButton.keyboardButtonRequestPhone(text: _1!) } public static func parse_keyboardButtonRequestPoll(_ reader: BufferReader) -> KeyboardButton? { var _1: Int32? @@ -1123,12 +1048,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.KeyboardButton.keyboardButtonRequestPoll(flags: _1!, quiz: _2, text: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.KeyboardButton.keyboardButtonRequestPoll(flags: _1!, quiz: _2, text: _3!) } public static func parse_keyboardButtonSimpleWebView(_ reader: BufferReader) -> KeyboardButton? { var _1: String? @@ -1137,12 +1060,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.KeyboardButton.keyboardButtonSimpleWebView(text: _1!, url: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.KeyboardButton.keyboardButtonSimpleWebView(text: _1!, url: _2!) } public static func parse_keyboardButtonSwitchInline(_ reader: BufferReader) -> KeyboardButton? { var _1: Int32? @@ -1159,12 +1079,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.KeyboardButton.keyboardButtonSwitchInline(flags: _1!, text: _2!, query: _3!, peerTypes: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.KeyboardButton.keyboardButtonSwitchInline(flags: _1!, text: _2!, query: _3!, peerTypes: _4) } public static func parse_keyboardButtonUrl(_ reader: BufferReader) -> KeyboardButton? { var _1: String? @@ -1173,12 +1092,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.KeyboardButton.keyboardButtonUrl(text: _1!, url: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.KeyboardButton.keyboardButtonUrl(text: _1!, url: _2!) } public static func parse_keyboardButtonUrlAuth(_ reader: BufferReader) -> KeyboardButton? { var _1: Int32? @@ -1196,12 +1112,12 @@ public extension Api { let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.KeyboardButton.keyboardButtonUrlAuth(flags: _1!, text: _2!, fwdText: _3, url: _4!, buttonId: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.KeyboardButton.keyboardButtonUrlAuth(flags: _1!, text: _2!, fwdText: _3, url: _4!, buttonId: _5!) } public static func parse_keyboardButtonUserProfile(_ reader: BufferReader) -> KeyboardButton? { var _1: String? @@ -1210,12 +1126,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.KeyboardButton.keyboardButtonUserProfile(text: _1!, userId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.KeyboardButton.keyboardButtonUserProfile(text: _1!, userId: _2!) } public static func parse_keyboardButtonWebView(_ reader: BufferReader) -> KeyboardButton? { var _1: String? @@ -1224,12 +1137,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.KeyboardButton.keyboardButtonWebView(text: _1!, url: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.KeyboardButton.keyboardButtonWebView(text: _1!, url: _2!) } } diff --git a/submodules/TelegramApi/Sources/Api15.swift b/submodules/TelegramApi/Sources/Api15.swift index 4bb0329f..6b606c80 100644 --- a/submodules/TelegramApi/Sources/Api15.swift +++ b/submodules/TelegramApi/Sources/Api15.swift @@ -30,12 +30,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.KeyboardButton.self) } let _c1 = _1 != nil - if _c1 { - return Api.KeyboardButtonRow.keyboardButtonRow(buttons: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.KeyboardButtonRow.keyboardButtonRow(buttons: _1!) } } @@ -70,12 +66,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.LabeledPrice.labeledPrice(label: _1!, amount: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.LabeledPrice.labeledPrice(label: _1!, amount: _2!) } } @@ -124,12 +117,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.LangPackDifference.langPackDifference(langCode: _1!, fromVersion: _2!, version: _3!, strings: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.LangPackDifference.langPackDifference(langCode: _1!, fromVersion: _2!, version: _3!, strings: _4!) } } @@ -192,12 +184,16 @@ public extension Api { let _c7 = _7 != nil let _c8 = _8 != nil let _c9 = _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.LangPackLanguage.langPackLanguage(flags: _1!, name: _2!, nativeName: _3!, langCode: _4!, baseLangCode: _5, pluralCode: _6!, stringsCount: _7!, translatedCount: _8!, translationsUrl: _9!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + return Api.LangPackLanguage.langPackLanguage(flags: _1!, name: _2!, nativeName: _3!, langCode: _4!, baseLangCode: _5, pluralCode: _6!, stringsCount: _7!, translatedCount: _8!, translationsUrl: _9!) } } @@ -257,23 +253,16 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.LangPackString.langPackString(key: _1!, value: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.LangPackString.langPackString(key: _1!, value: _2!) } public static func parse_langPackStringDeleted(_ reader: BufferReader) -> LangPackString? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.LangPackString.langPackStringDeleted(key: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.LangPackString.langPackStringDeleted(key: _1!) } public static func parse_langPackStringPluralized(_ reader: BufferReader) -> LangPackString? { var _1: Int32? @@ -300,12 +289,15 @@ public extension Api { let _c6 = (Int(_1!) & Int(1 << 3) == 0) || _6 != nil let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil let _c8 = _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.LangPackString.langPackStringPluralized(flags: _1!, key: _2!, zeroValue: _3, oneValue: _4, twoValue: _5, fewValue: _6, manyValue: _7, otherValue: _8!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.LangPackString.langPackStringPluralized(flags: _1!, key: _2!, zeroValue: _3, oneValue: _4, twoValue: _5, fewValue: _6, manyValue: _7, otherValue: _8!) } } @@ -348,12 +340,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.MaskCoords.maskCoords(n: _1!, x: _2!, y: _3!, zoom: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.MaskCoords.maskCoords(n: _1!, x: _2!, y: _3!, zoom: _4!) } } @@ -488,12 +479,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.MediaArea.inputMediaAreaChannelPost(coordinates: _1!, channel: _2!, msgId: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.MediaArea.inputMediaAreaChannelPost(coordinates: _1!, channel: _2!, msgId: _3!) } public static func parse_inputMediaAreaVenue(_ reader: BufferReader) -> MediaArea? { var _1: Api.MediaAreaCoordinates? @@ -507,12 +496,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.MediaArea.inputMediaAreaVenue(coordinates: _1!, queryId: _2!, resultId: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.MediaArea.inputMediaAreaVenue(coordinates: _1!, queryId: _2!, resultId: _3!) } public static func parse_mediaAreaChannelPost(_ reader: BufferReader) -> MediaArea? { var _1: Api.MediaAreaCoordinates? @@ -526,12 +513,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.MediaArea.mediaAreaChannelPost(coordinates: _1!, channelId: _2!, msgId: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.MediaArea.mediaAreaChannelPost(coordinates: _1!, channelId: _2!, msgId: _3!) } public static func parse_mediaAreaGeoPoint(_ reader: BufferReader) -> MediaArea? { var _1: Int32? @@ -552,12 +537,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.MediaArea.mediaAreaGeoPoint(flags: _1!, coordinates: _2!, geo: _3!, address: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.MediaArea.mediaAreaGeoPoint(flags: _1!, coordinates: _2!, geo: _3!, address: _4) } public static func parse_mediaAreaStarGift(_ reader: BufferReader) -> MediaArea? { var _1: Api.MediaAreaCoordinates? @@ -568,12 +552,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MediaArea.mediaAreaStarGift(coordinates: _1!, slug: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MediaArea.mediaAreaStarGift(coordinates: _1!, slug: _2!) } public static func parse_mediaAreaSuggestedReaction(_ reader: BufferReader) -> MediaArea? { var _1: Int32? @@ -589,12 +570,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.MediaArea.mediaAreaSuggestedReaction(flags: _1!, coordinates: _2!, reaction: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.MediaArea.mediaAreaSuggestedReaction(flags: _1!, coordinates: _2!, reaction: _3!) } public static func parse_mediaAreaUrl(_ reader: BufferReader) -> MediaArea? { var _1: Api.MediaAreaCoordinates? @@ -605,12 +584,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MediaArea.mediaAreaUrl(coordinates: _1!, url: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MediaArea.mediaAreaUrl(coordinates: _1!, url: _2!) } public static func parse_mediaAreaVenue(_ reader: BufferReader) -> MediaArea? { var _1: Api.MediaAreaCoordinates? @@ -638,12 +614,14 @@ public extension Api { let _c5 = _5 != nil let _c6 = _6 != nil let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.MediaArea.mediaAreaVenue(coordinates: _1!, geo: _2!, title: _3!, address: _4!, provider: _5!, venueId: _6!, venueType: _7!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.MediaArea.mediaAreaVenue(coordinates: _1!, geo: _2!, title: _3!, address: _4!, provider: _5!, venueId: _6!, venueType: _7!) } public static func parse_mediaAreaWeather(_ reader: BufferReader) -> MediaArea? { var _1: Api.MediaAreaCoordinates? @@ -660,12 +638,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.MediaArea.mediaAreaWeather(coordinates: _1!, emoji: _2!, temperatureC: _3!, color: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.MediaArea.mediaAreaWeather(coordinates: _1!, emoji: _2!, temperatureC: _3!, color: _4!) } } @@ -720,27 +697,29 @@ public extension Api { let _c5 = _5 != nil let _c6 = _6 != nil let _c7 = (Int(_1!) & Int(1 << 0) == 0) || _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.MediaAreaCoordinates.mediaAreaCoordinates(flags: _1!, x: _2!, y: _3!, w: _4!, h: _5!, rotation: _6!, radius: _7) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.MediaAreaCoordinates.mediaAreaCoordinates(flags: _1!, x: _2!, y: _3!, w: _4!, h: _5!, rotation: _6!, radius: _7) } } } public extension Api { indirect enum Message: TypeConstructorDescription { - case message(flags: Int32, flags2: Int32, id: Int32, fromId: Api.Peer?, fromBoostsApplied: Int32?, peerId: Api.Peer, savedPeerId: Api.Peer?, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int64?, viaBusinessBotId: Int64?, replyTo: Api.MessageReplyHeader?, date: Int32, message: String, media: Api.MessageMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, views: Int32?, forwards: Int32?, replies: Api.MessageReplies?, editDate: Int32?, postAuthor: String?, groupedId: Int64?, reactions: Api.MessageReactions?, restrictionReason: [Api.RestrictionReason]?, ttlPeriod: Int32?, quickReplyShortcutId: Int32?, effect: Int64?, factcheck: Api.FactCheck?, reportDeliveryUntilDate: Int32?, paidMessageStars: Int64?, suggestedPost: Api.SuggestedPost?, scheduleRepeatPeriod: Int32?) + case message(flags: Int32, flags2: Int32, id: Int32, fromId: Api.Peer?, fromBoostsApplied: Int32?, peerId: Api.Peer, savedPeerId: Api.Peer?, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int64?, viaBusinessBotId: Int64?, replyTo: Api.MessageReplyHeader?, date: Int32, message: String, media: Api.MessageMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, views: Int32?, forwards: Int32?, replies: Api.MessageReplies?, editDate: Int32?, postAuthor: String?, groupedId: Int64?, reactions: Api.MessageReactions?, restrictionReason: [Api.RestrictionReason]?, ttlPeriod: Int32?, quickReplyShortcutId: Int32?, effect: Int64?, factcheck: Api.FactCheck?, reportDeliveryUntilDate: Int32?, paidMessageStars: Int64?, suggestedPost: Api.SuggestedPost?, scheduleRepeatPeriod: Int32?, summaryFromLanguage: String?) case messageEmpty(flags: Int32, id: Int32, peerId: Api.Peer?) case messageService(flags: Int32, id: Int32, fromId: Api.Peer?, peerId: Api.Peer, savedPeerId: Api.Peer?, replyTo: Api.MessageReplyHeader?, date: Int32, action: Api.MessageAction, reactions: Api.MessageReactions?, ttlPeriod: Int32?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .message(let flags, let flags2, let id, let fromId, let fromBoostsApplied, let peerId, let savedPeerId, let fwdFrom, let viaBotId, let viaBusinessBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod, let quickReplyShortcutId, let effect, let factcheck, let reportDeliveryUntilDate, let paidMessageStars, let suggestedPost, let scheduleRepeatPeriod): + case .message(let flags, let flags2, let id, let fromId, let fromBoostsApplied, let peerId, let savedPeerId, let fwdFrom, let viaBotId, let viaBusinessBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod, let quickReplyShortcutId, let effect, let factcheck, let reportDeliveryUntilDate, let paidMessageStars, let suggestedPost, let scheduleRepeatPeriod, let summaryFromLanguage): if boxed { - buffer.appendInt32(-1188071729) + buffer.appendInt32(-1665888023) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags2, buffer: buffer, boxed: false) @@ -782,6 +761,7 @@ public extension Api { if Int(flags2) & Int(1 << 6) != 0 {serializeInt64(paidMessageStars!, buffer: buffer, boxed: false)} if Int(flags2) & Int(1 << 7) != 0 {suggestedPost!.serialize(buffer, true)} if Int(flags2) & Int(1 << 10) != 0 {serializeInt32(scheduleRepeatPeriod!, buffer: buffer, boxed: false)} + if Int(flags2) & Int(1 << 11) != 0 {serializeString(summaryFromLanguage!, buffer: buffer, boxed: false)} break case .messageEmpty(let flags, let id, let peerId): if boxed { @@ -811,8 +791,8 @@ public extension Api { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .message(let flags, let flags2, let id, let fromId, let fromBoostsApplied, let peerId, let savedPeerId, let fwdFrom, let viaBotId, let viaBusinessBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod, let quickReplyShortcutId, let effect, let factcheck, let reportDeliveryUntilDate, let paidMessageStars, let suggestedPost, let scheduleRepeatPeriod): - return ("message", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("fromId", fromId as Any), ("fromBoostsApplied", fromBoostsApplied as Any), ("peerId", peerId as Any), ("savedPeerId", savedPeerId as Any), ("fwdFrom", fwdFrom as Any), ("viaBotId", viaBotId as Any), ("viaBusinessBotId", viaBusinessBotId as Any), ("replyTo", replyTo as Any), ("date", date as Any), ("message", message as Any), ("media", media as Any), ("replyMarkup", replyMarkup as Any), ("entities", entities as Any), ("views", views as Any), ("forwards", forwards as Any), ("replies", replies as Any), ("editDate", editDate as Any), ("postAuthor", postAuthor as Any), ("groupedId", groupedId as Any), ("reactions", reactions as Any), ("restrictionReason", restrictionReason as Any), ("ttlPeriod", ttlPeriod as Any), ("quickReplyShortcutId", quickReplyShortcutId as Any), ("effect", effect as Any), ("factcheck", factcheck as Any), ("reportDeliveryUntilDate", reportDeliveryUntilDate as Any), ("paidMessageStars", paidMessageStars as Any), ("suggestedPost", suggestedPost as Any), ("scheduleRepeatPeriod", scheduleRepeatPeriod as Any)]) + case .message(let flags, let flags2, let id, let fromId, let fromBoostsApplied, let peerId, let savedPeerId, let fwdFrom, let viaBotId, let viaBusinessBotId, let replyTo, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let replies, let editDate, let postAuthor, let groupedId, let reactions, let restrictionReason, let ttlPeriod, let quickReplyShortcutId, let effect, let factcheck, let reportDeliveryUntilDate, let paidMessageStars, let suggestedPost, let scheduleRepeatPeriod, let summaryFromLanguage): + return ("message", [("flags", flags as Any), ("flags2", flags2 as Any), ("id", id as Any), ("fromId", fromId as Any), ("fromBoostsApplied", fromBoostsApplied as Any), ("peerId", peerId as Any), ("savedPeerId", savedPeerId as Any), ("fwdFrom", fwdFrom as Any), ("viaBotId", viaBotId as Any), ("viaBusinessBotId", viaBusinessBotId as Any), ("replyTo", replyTo as Any), ("date", date as Any), ("message", message as Any), ("media", media as Any), ("replyMarkup", replyMarkup as Any), ("entities", entities as Any), ("views", views as Any), ("forwards", forwards as Any), ("replies", replies as Any), ("editDate", editDate as Any), ("postAuthor", postAuthor as Any), ("groupedId", groupedId as Any), ("reactions", reactions as Any), ("restrictionReason", restrictionReason as Any), ("ttlPeriod", ttlPeriod as Any), ("quickReplyShortcutId", quickReplyShortcutId as Any), ("effect", effect as Any), ("factcheck", factcheck as Any), ("reportDeliveryUntilDate", reportDeliveryUntilDate as Any), ("paidMessageStars", paidMessageStars as Any), ("suggestedPost", suggestedPost as Any), ("scheduleRepeatPeriod", scheduleRepeatPeriod as Any), ("summaryFromLanguage", summaryFromLanguage as Any)]) case .messageEmpty(let flags, let id, let peerId): return ("messageEmpty", [("flags", flags as Any), ("id", id as Any), ("peerId", peerId as Any)]) case .messageService(let flags, let id, let fromId, let peerId, let savedPeerId, let replyTo, let date, let action, let reactions, let ttlPeriod): @@ -911,6 +891,8 @@ public extension Api { } } var _32: Int32? if Int(_2!) & Int(1 << 10) != 0 {_32 = reader.readInt32() } + var _33: String? + if Int(_2!) & Int(1 << 11) != 0 {_33 = parseString(reader) } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil @@ -943,12 +925,41 @@ public extension Api { let _c30 = (Int(_2!) & Int(1 << 6) == 0) || _30 != nil let _c31 = (Int(_2!) & Int(1 << 7) == 0) || _31 != nil let _c32 = (Int(_2!) & Int(1 << 10) == 0) || _32 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 && _c28 && _c29 && _c30 && _c31 && _c32 { - return Api.Message.message(flags: _1!, flags2: _2!, id: _3!, fromId: _4, fromBoostsApplied: _5, peerId: _6!, savedPeerId: _7, fwdFrom: _8, viaBotId: _9, viaBusinessBotId: _10, replyTo: _11, date: _12!, message: _13!, media: _14, replyMarkup: _15, entities: _16, views: _17, forwards: _18, replies: _19, editDate: _20, postAuthor: _21, groupedId: _22, reactions: _23, restrictionReason: _24, ttlPeriod: _25, quickReplyShortcutId: _26, effect: _27, factcheck: _28, reportDeliveryUntilDate: _29, paidMessageStars: _30, suggestedPost: _31, scheduleRepeatPeriod: _32) - } - else { - return nil - } + let _c33 = (Int(_2!) & Int(1 << 11) == 0) || _33 != nil + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + if !_c13 { return nil } + if !_c14 { return nil } + if !_c15 { return nil } + if !_c16 { return nil } + if !_c17 { return nil } + if !_c18 { return nil } + if !_c19 { return nil } + if !_c20 { return nil } + if !_c21 { return nil } + if !_c22 { return nil } + if !_c23 { return nil } + if !_c24 { return nil } + if !_c25 { return nil } + if !_c26 { return nil } + if !_c27 { return nil } + if !_c28 { return nil } + if !_c29 { return nil } + if !_c30 { return nil } + if !_c31 { return nil } + if !_c32 { return nil } + if !_c33 { return nil } + return Api.Message.message(flags: _1!, flags2: _2!, id: _3!, fromId: _4, fromBoostsApplied: _5, peerId: _6!, savedPeerId: _7, fwdFrom: _8, viaBotId: _9, viaBusinessBotId: _10, replyTo: _11, date: _12!, message: _13!, media: _14, replyMarkup: _15, entities: _16, views: _17, forwards: _18, replies: _19, editDate: _20, postAuthor: _21, groupedId: _22, reactions: _23, restrictionReason: _24, ttlPeriod: _25, quickReplyShortcutId: _26, effect: _27, factcheck: _28, reportDeliveryUntilDate: _29, paidMessageStars: _30, suggestedPost: _31, scheduleRepeatPeriod: _32, summaryFromLanguage: _33) } public static func parse_messageEmpty(_ reader: BufferReader) -> Message? { var _1: Int32? @@ -962,12 +973,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.Message.messageEmpty(flags: _1!, id: _2!, peerId: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Message.messageEmpty(flags: _1!, id: _2!, peerId: _3) } public static func parse_messageService(_ reader: BufferReader) -> Message? { var _1: Int32? @@ -1012,12 +1021,17 @@ public extension Api { let _c8 = _8 != nil let _c9 = (Int(_1!) & Int(1 << 20) == 0) || _9 != nil let _c10 = (Int(_1!) & Int(1 << 25) == 0) || _10 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 { - return Api.Message.messageService(flags: _1!, id: _2!, fromId: _3, peerId: _4!, savedPeerId: _5, replyTo: _6, date: _7!, action: _8!, reactions: _9, ttlPeriod: _10) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + return Api.Message.messageService(flags: _1!, id: _2!, fromId: _3, peerId: _4!, savedPeerId: _5, replyTo: _6, date: _7!, action: _8!, reactions: _9, ttlPeriod: _10) } } @@ -1735,12 +1749,8 @@ public extension Api { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.MessageAction.messageActionBoostApply(boosts: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.MessageAction.messageActionBoostApply(boosts: _1!) } public static func parse_messageActionBotAllowed(_ reader: BufferReader) -> MessageAction? { var _1: Int32? @@ -1754,23 +1764,17 @@ public extension Api { let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.MessageAction.messageActionBotAllowed(flags: _1!, domain: _2, app: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.MessageAction.messageActionBotAllowed(flags: _1!, domain: _2, app: _3) } public static func parse_messageActionChannelCreate(_ reader: BufferReader) -> MessageAction? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.MessageAction.messageActionChannelCreate(title: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.MessageAction.messageActionChannelCreate(title: _1!) } public static func parse_messageActionChannelMigrateFrom(_ reader: BufferReader) -> MessageAction? { var _1: String? @@ -1779,12 +1783,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageAction.messageActionChannelMigrateFrom(title: _1!, chatId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageAction.messageActionChannelMigrateFrom(title: _1!, chatId: _2!) } public static func parse_messageActionChatAddUser(_ reader: BufferReader) -> MessageAction? { var _1: [Int64]? @@ -1792,12 +1793,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) } let _c1 = _1 != nil - if _c1 { - return Api.MessageAction.messageActionChatAddUser(users: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.MessageAction.messageActionChatAddUser(users: _1!) } public static func parse_messageActionChatCreate(_ reader: BufferReader) -> MessageAction? { var _1: String? @@ -1808,12 +1805,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageAction.messageActionChatCreate(title: _1!, users: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageAction.messageActionChatCreate(title: _1!, users: _2!) } public static func parse_messageActionChatDeletePhoto(_ reader: BufferReader) -> MessageAction? { return Api.MessageAction.messageActionChatDeletePhoto @@ -1822,12 +1816,8 @@ public extension Api { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.MessageAction.messageActionChatDeleteUser(userId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.MessageAction.messageActionChatDeleteUser(userId: _1!) } public static func parse_messageActionChatEditPhoto(_ reader: BufferReader) -> MessageAction? { var _1: Api.Photo? @@ -1835,34 +1825,22 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Photo } let _c1 = _1 != nil - if _c1 { - return Api.MessageAction.messageActionChatEditPhoto(photo: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.MessageAction.messageActionChatEditPhoto(photo: _1!) } public static func parse_messageActionChatEditTitle(_ reader: BufferReader) -> MessageAction? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.MessageAction.messageActionChatEditTitle(title: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.MessageAction.messageActionChatEditTitle(title: _1!) } public static func parse_messageActionChatJoinedByLink(_ reader: BufferReader) -> MessageAction? { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.MessageAction.messageActionChatJoinedByLink(inviterId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.MessageAction.messageActionChatJoinedByLink(inviterId: _1!) } public static func parse_messageActionChatJoinedByRequest(_ reader: BufferReader) -> MessageAction? { return Api.MessageAction.messageActionChatJoinedByRequest @@ -1871,12 +1849,8 @@ public extension Api { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.MessageAction.messageActionChatMigrateTo(channelId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.MessageAction.messageActionChatMigrateTo(channelId: _1!) } public static func parse_messageActionConferenceCall(_ reader: BufferReader) -> MessageAction? { var _1: Int32? @@ -1893,12 +1867,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 3) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.MessageAction.messageActionConferenceCall(flags: _1!, callId: _2!, duration: _3, otherParticipants: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.MessageAction.messageActionConferenceCall(flags: _1!, callId: _2!, duration: _3, otherParticipants: _4) } public static func parse_messageActionContactSignUp(_ reader: BufferReader) -> MessageAction? { return Api.MessageAction.messageActionContactSignUp @@ -1907,12 +1880,8 @@ public extension Api { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.MessageAction.messageActionCustomAction(message: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.MessageAction.messageActionCustomAction(message: _1!) } public static func parse_messageActionEmpty(_ reader: BufferReader) -> MessageAction? { return Api.MessageAction.messageActionEmpty @@ -1924,12 +1893,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageAction.messageActionGameScore(gameId: _1!, score: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageAction.messageActionGameScore(gameId: _1!, score: _2!) } public static func parse_messageActionGeoProximityReached(_ reader: BufferReader) -> MessageAction? { var _1: Api.Peer? @@ -1945,12 +1911,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.MessageAction.messageActionGeoProximityReached(fromId: _1!, toId: _2!, distance: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.MessageAction.messageActionGeoProximityReached(fromId: _1!, toId: _2!, distance: _3!) } public static func parse_messageActionGiftCode(_ reader: BufferReader) -> MessageAction? { var _1: Int32? @@ -1984,12 +1948,16 @@ public extension Api { let _c7 = (Int(_1!) & Int(1 << 3) == 0) || _7 != nil let _c8 = (Int(_1!) & Int(1 << 3) == 0) || _8 != nil let _c9 = (Int(_1!) & Int(1 << 4) == 0) || _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.MessageAction.messageActionGiftCode(flags: _1!, boostPeer: _2, days: _3!, slug: _4!, currency: _5, amount: _6, cryptoCurrency: _7, cryptoAmount: _8, message: _9) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + return Api.MessageAction.messageActionGiftCode(flags: _1!, boostPeer: _2, days: _3!, slug: _4!, currency: _5, amount: _6, cryptoCurrency: _7, cryptoAmount: _8, message: _9) } public static func parse_messageActionGiftPremium(_ reader: BufferReader) -> MessageAction? { var _1: Int32? @@ -2015,12 +1983,14 @@ public extension Api { let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.MessageAction.messageActionGiftPremium(flags: _1!, currency: _2!, amount: _3!, days: _4!, cryptoCurrency: _5, cryptoAmount: _6, message: _7) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.MessageAction.messageActionGiftPremium(flags: _1!, currency: _2!, amount: _3!, days: _4!, cryptoCurrency: _5, cryptoAmount: _6, message: _7) } public static func parse_messageActionGiftStars(_ reader: BufferReader) -> MessageAction? { var _1: Int32? @@ -2044,12 +2014,14 @@ public extension Api { let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.MessageAction.messageActionGiftStars(flags: _1!, currency: _2!, amount: _3!, stars: _4!, cryptoCurrency: _5, cryptoAmount: _6, transactionId: _7) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.MessageAction.messageActionGiftStars(flags: _1!, currency: _2!, amount: _3!, stars: _4!, cryptoCurrency: _5, cryptoAmount: _6, transactionId: _7) } public static func parse_messageActionGiftTon(_ reader: BufferReader) -> MessageAction? { var _1: Int32? @@ -2070,12 +2042,13 @@ public extension Api { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.MessageAction.messageActionGiftTon(flags: _1!, currency: _2!, amount: _3!, cryptoCurrency: _4!, cryptoAmount: _5!, transactionId: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.MessageAction.messageActionGiftTon(flags: _1!, currency: _2!, amount: _3!, cryptoCurrency: _4!, cryptoAmount: _5!, transactionId: _6) } public static func parse_messageActionGiveawayLaunch(_ reader: BufferReader) -> MessageAction? { var _1: Int32? @@ -2084,12 +2057,9 @@ public extension Api { if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt64() } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - if _c1 && _c2 { - return Api.MessageAction.messageActionGiveawayLaunch(flags: _1!, stars: _2) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageAction.messageActionGiveawayLaunch(flags: _1!, stars: _2) } public static func parse_messageActionGiveawayResults(_ reader: BufferReader) -> MessageAction? { var _1: Int32? @@ -2101,12 +2071,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.MessageAction.messageActionGiveawayResults(flags: _1!, winnersCount: _2!, unclaimedCount: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.MessageAction.messageActionGiveawayResults(flags: _1!, winnersCount: _2!, unclaimedCount: _3!) } public static func parse_messageActionGroupCall(_ reader: BufferReader) -> MessageAction? { var _1: Int32? @@ -2120,12 +2088,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.MessageAction.messageActionGroupCall(flags: _1!, call: _2!, duration: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.MessageAction.messageActionGroupCall(flags: _1!, call: _2!, duration: _3) } public static func parse_messageActionGroupCallScheduled(_ reader: BufferReader) -> MessageAction? { var _1: Api.InputGroupCall? @@ -2136,12 +2102,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageAction.messageActionGroupCallScheduled(call: _1!, scheduleDate: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageAction.messageActionGroupCallScheduled(call: _1!, scheduleDate: _2!) } public static func parse_messageActionHistoryClear(_ reader: BufferReader) -> MessageAction? { return Api.MessageAction.messageActionHistoryClear @@ -2157,12 +2120,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageAction.messageActionInviteToGroupCall(call: _1!, users: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageAction.messageActionInviteToGroupCall(call: _1!, users: _2!) } public static func parse_messageActionPaidMessagesPrice(_ reader: BufferReader) -> MessageAction? { var _1: Int32? @@ -2171,12 +2131,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageAction.messageActionPaidMessagesPrice(flags: _1!, stars: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageAction.messageActionPaidMessagesPrice(flags: _1!, stars: _2!) } public static func parse_messageActionPaidMessagesRefunded(_ reader: BufferReader) -> MessageAction? { var _1: Int32? @@ -2185,12 +2142,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageAction.messageActionPaidMessagesRefunded(count: _1!, stars: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageAction.messageActionPaidMessagesRefunded(count: _1!, stars: _2!) } public static func parse_messageActionPaymentRefunded(_ reader: BufferReader) -> MessageAction? { var _1: Int32? @@ -2215,12 +2169,13 @@ public extension Api { let _c4 = _4 != nil let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.MessageAction.messageActionPaymentRefunded(flags: _1!, peer: _2!, currency: _3!, totalAmount: _4!, payload: _5, charge: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.MessageAction.messageActionPaymentRefunded(flags: _1!, peer: _2!, currency: _3!, totalAmount: _4!, payload: _5, charge: _6!) } public static func parse_messageActionPaymentSent(_ reader: BufferReader) -> MessageAction? { var _1: Int32? @@ -2238,12 +2193,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 4) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.MessageAction.messageActionPaymentSent(flags: _1!, currency: _2!, totalAmount: _3!, invoiceSlug: _4, subscriptionUntilDate: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.MessageAction.messageActionPaymentSent(flags: _1!, currency: _2!, totalAmount: _3!, invoiceSlug: _4, subscriptionUntilDate: _5) } public static func parse_messageActionPaymentSentMe(_ reader: BufferReader) -> MessageAction? { var _1: Int32? @@ -2274,12 +2229,15 @@ public extension Api { let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil let _c7 = _7 != nil let _c8 = (Int(_1!) & Int(1 << 4) == 0) || _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.MessageAction.messageActionPaymentSentMe(flags: _1!, currency: _2!, totalAmount: _3!, payload: _4!, info: _5, shippingOptionId: _6, charge: _7!, subscriptionUntilDate: _8) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.MessageAction.messageActionPaymentSentMe(flags: _1!, currency: _2!, totalAmount: _3!, payload: _4!, info: _5, shippingOptionId: _6, charge: _7!, subscriptionUntilDate: _8) } public static func parse_messageActionPhoneCall(_ reader: BufferReader) -> MessageAction? { var _1: Int32? @@ -2296,12 +2254,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.MessageAction.messageActionPhoneCall(flags: _1!, callId: _2!, reason: _3, duration: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.MessageAction.messageActionPhoneCall(flags: _1!, callId: _2!, reason: _3, duration: _4) } public static func parse_messageActionPinMessage(_ reader: BufferReader) -> MessageAction? { return Api.MessageAction.messageActionPinMessage @@ -2324,12 +2281,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.MessageAction.messageActionPrizeStars(flags: _1!, stars: _2!, transactionId: _3!, boostPeer: _4!, giveawayMsgId: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.MessageAction.messageActionPrizeStars(flags: _1!, stars: _2!, transactionId: _3!, boostPeer: _4!, giveawayMsgId: _5!) } public static func parse_messageActionRequestedPeer(_ reader: BufferReader) -> MessageAction? { var _1: Int32? @@ -2340,12 +2297,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageAction.messageActionRequestedPeer(buttonId: _1!, peers: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageAction.messageActionRequestedPeer(buttonId: _1!, peers: _2!) } public static func parse_messageActionRequestedPeerSentMe(_ reader: BufferReader) -> MessageAction? { var _1: Int32? @@ -2356,12 +2310,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageAction.messageActionRequestedPeerSentMe(buttonId: _1!, peers: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageAction.messageActionRequestedPeerSentMe(buttonId: _1!, peers: _2!) } public static func parse_messageActionScreenshotTaken(_ reader: BufferReader) -> MessageAction? { return Api.MessageAction.messageActionScreenshotTaken @@ -2372,12 +2323,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SecureValueType.self) } let _c1 = _1 != nil - if _c1 { - return Api.MessageAction.messageActionSecureValuesSent(types: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.MessageAction.messageActionSecureValuesSent(types: _1!) } public static func parse_messageActionSecureValuesSentMe(_ reader: BufferReader) -> MessageAction? { var _1: [Api.SecureValue]? @@ -2390,12 +2337,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageAction.messageActionSecureValuesSentMe(values: _1!, credentials: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageAction.messageActionSecureValuesSentMe(values: _1!, credentials: _2!) } public static func parse_messageActionSetChatTheme(_ reader: BufferReader) -> MessageAction? { var _1: Api.ChatTheme? @@ -2403,12 +2347,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.ChatTheme } let _c1 = _1 != nil - if _c1 { - return Api.MessageAction.messageActionSetChatTheme(theme: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.MessageAction.messageActionSetChatTheme(theme: _1!) } public static func parse_messageActionSetChatWallPaper(_ reader: BufferReader) -> MessageAction? { var _1: Int32? @@ -2419,12 +2359,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageAction.messageActionSetChatWallPaper(flags: _1!, wallpaper: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageAction.messageActionSetChatWallPaper(flags: _1!, wallpaper: _2!) } public static func parse_messageActionSetMessagesTTL(_ reader: BufferReader) -> MessageAction? { var _1: Int32? @@ -2436,12 +2373,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.MessageAction.messageActionSetMessagesTTL(flags: _1!, period: _2!, autoSettingFrom: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.MessageAction.messageActionSetMessagesTTL(flags: _1!, period: _2!, autoSettingFrom: _3) } public static func parse_messageActionStarGift(_ reader: BufferReader) -> MessageAction? { var _1: Int32? @@ -2493,12 +2428,20 @@ public extension Api { let _c11 = (Int(_1!) & Int(1 << 15) == 0) || _11 != nil let _c12 = (Int(_1!) & Int(1 << 18) == 0) || _12 != nil let _c13 = (Int(_1!) & Int(1 << 19) == 0) || _13 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 { - return Api.MessageAction.messageActionStarGift(flags: _1!, gift: _2!, message: _3, convertStars: _4, upgradeMsgId: _5, upgradeStars: _6, fromId: _7, peer: _8, savedId: _9, prepaidUpgradeHash: _10, giftMsgId: _11, toId: _12, giftNum: _13) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + if !_c13 { return nil } + return Api.MessageAction.messageActionStarGift(flags: _1!, gift: _2!, message: _3, convertStars: _4, upgradeMsgId: _5, upgradeStars: _6, fromId: _7, peer: _8, savedId: _9, prepaidUpgradeHash: _10, giftMsgId: _11, toId: _12, giftNum: _13) } public static func parse_messageActionStarGiftPurchaseOffer(_ reader: BufferReader) -> MessageAction? { var _1: Int32? @@ -2517,12 +2460,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.MessageAction.messageActionStarGiftPurchaseOffer(flags: _1!, gift: _2!, price: _3!, expiresAt: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.MessageAction.messageActionStarGiftPurchaseOffer(flags: _1!, gift: _2!, price: _3!, expiresAt: _4!) } public static func parse_messageActionStarGiftPurchaseOfferDeclined(_ reader: BufferReader) -> MessageAction? { var _1: Int32? @@ -2538,12 +2480,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.MessageAction.messageActionStarGiftPurchaseOfferDeclined(flags: _1!, gift: _2!, price: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.MessageAction.messageActionStarGiftPurchaseOfferDeclined(flags: _1!, gift: _2!, price: _3!) } public static func parse_messageActionStarGiftUnique(_ reader: BufferReader) -> MessageAction? { var _1: Int32? @@ -2587,12 +2527,18 @@ public extension Api { let _c9 = (Int(_1!) & Int(1 << 9) == 0) || _9 != nil let _c10 = (Int(_1!) & Int(1 << 10) == 0) || _10 != nil let _c11 = (Int(_1!) & Int(1 << 12) == 0) || _11 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 { - return Api.MessageAction.messageActionStarGiftUnique(flags: _1!, gift: _2!, canExportAt: _3, transferStars: _4, fromId: _5, peer: _6, savedId: _7, resaleAmount: _8, canTransferAt: _9, canResellAt: _10, dropOriginalDetailsStars: _11) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + return Api.MessageAction.messageActionStarGiftUnique(flags: _1!, gift: _2!, canExportAt: _3, transferStars: _4, fromId: _5, peer: _6, savedId: _7, resaleAmount: _8, canTransferAt: _9, canResellAt: _10, dropOriginalDetailsStars: _11) } public static func parse_messageActionSuggestBirthday(_ reader: BufferReader) -> MessageAction? { var _1: Api.Birthday? @@ -2600,12 +2546,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Birthday } let _c1 = _1 != nil - if _c1 { - return Api.MessageAction.messageActionSuggestBirthday(birthday: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.MessageAction.messageActionSuggestBirthday(birthday: _1!) } public static func parse_messageActionSuggestProfilePhoto(_ reader: BufferReader) -> MessageAction? { var _1: Api.Photo? @@ -2613,12 +2555,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Photo } let _c1 = _1 != nil - if _c1 { - return Api.MessageAction.messageActionSuggestProfilePhoto(photo: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.MessageAction.messageActionSuggestProfilePhoto(photo: _1!) } public static func parse_messageActionSuggestedPostApproval(_ reader: BufferReader) -> MessageAction? { var _1: Int32? @@ -2635,23 +2573,18 @@ public extension Api { let _c2 = (Int(_1!) & Int(1 << 2) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 3) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 4) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.MessageAction.messageActionSuggestedPostApproval(flags: _1!, rejectComment: _2, scheduleDate: _3, price: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.MessageAction.messageActionSuggestedPostApproval(flags: _1!, rejectComment: _2, scheduleDate: _3, price: _4) } public static func parse_messageActionSuggestedPostRefund(_ reader: BufferReader) -> MessageAction? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.MessageAction.messageActionSuggestedPostRefund(flags: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.MessageAction.messageActionSuggestedPostRefund(flags: _1!) } public static func parse_messageActionSuggestedPostSuccess(_ reader: BufferReader) -> MessageAction? { var _1: Api.StarsAmount? @@ -2659,12 +2592,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.StarsAmount } let _c1 = _1 != nil - if _c1 { - return Api.MessageAction.messageActionSuggestedPostSuccess(price: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.MessageAction.messageActionSuggestedPostSuccess(price: _1!) } public static func parse_messageActionTodoAppendTasks(_ reader: BufferReader) -> MessageAction? { var _1: [Api.TodoItem]? @@ -2672,12 +2601,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.TodoItem.self) } let _c1 = _1 != nil - if _c1 { - return Api.MessageAction.messageActionTodoAppendTasks(list: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.MessageAction.messageActionTodoAppendTasks(list: _1!) } public static func parse_messageActionTodoCompletions(_ reader: BufferReader) -> MessageAction? { var _1: [Int32]? @@ -2690,12 +2615,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageAction.messageActionTodoCompletions(completed: _1!, incompleted: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageAction.messageActionTodoCompletions(completed: _1!, incompleted: _2!) } public static func parse_messageActionTopicCreate(_ reader: BufferReader) -> MessageAction? { var _1: Int32? @@ -2710,12 +2632,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.MessageAction.messageActionTopicCreate(flags: _1!, title: _2!, iconColor: _3!, iconEmojiId: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.MessageAction.messageActionTopicCreate(flags: _1!, title: _2!, iconColor: _3!, iconEmojiId: _4) } public static func parse_messageActionTopicEdit(_ reader: BufferReader) -> MessageAction? { var _1: Int32? @@ -2737,23 +2658,19 @@ public extension Api { let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.MessageAction.messageActionTopicEdit(flags: _1!, title: _2, iconEmojiId: _3, closed: _4, hidden: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.MessageAction.messageActionTopicEdit(flags: _1!, title: _2, iconEmojiId: _3, closed: _4, hidden: _5) } public static func parse_messageActionWebViewDataSent(_ reader: BufferReader) -> MessageAction? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.MessageAction.messageActionWebViewDataSent(text: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.MessageAction.messageActionWebViewDataSent(text: _1!) } public static func parse_messageActionWebViewDataSentMe(_ reader: BufferReader) -> MessageAction? { var _1: String? @@ -2762,12 +2679,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageAction.messageActionWebViewDataSentMe(text: _1!, data: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageAction.messageActionWebViewDataSentMe(text: _1!, data: _2!) } } diff --git a/submodules/TelegramApi/Sources/Api16.swift b/submodules/TelegramApi/Sources/Api16.swift index 90af1af4..1cec7d62 100644 --- a/submodules/TelegramApi/Sources/Api16.swift +++ b/submodules/TelegramApi/Sources/Api16.swift @@ -239,12 +239,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.MessageEntity.inputMessageEntityMentionName(offset: _1!, length: _2!, userId: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.MessageEntity.inputMessageEntityMentionName(offset: _1!, length: _2!, userId: _3!) } public static func parse_messageEntityBankCard(_ reader: BufferReader) -> MessageEntity? { var _1: Int32? @@ -253,12 +251,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageEntity.messageEntityBankCard(offset: _1!, length: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageEntity.messageEntityBankCard(offset: _1!, length: _2!) } public static func parse_messageEntityBlockquote(_ reader: BufferReader) -> MessageEntity? { var _1: Int32? @@ -270,12 +265,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.MessageEntity.messageEntityBlockquote(flags: _1!, offset: _2!, length: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.MessageEntity.messageEntityBlockquote(flags: _1!, offset: _2!, length: _3!) } public static func parse_messageEntityBold(_ reader: BufferReader) -> MessageEntity? { var _1: Int32? @@ -284,12 +277,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageEntity.messageEntityBold(offset: _1!, length: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageEntity.messageEntityBold(offset: _1!, length: _2!) } public static func parse_messageEntityBotCommand(_ reader: BufferReader) -> MessageEntity? { var _1: Int32? @@ -298,12 +288,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageEntity.messageEntityBotCommand(offset: _1!, length: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageEntity.messageEntityBotCommand(offset: _1!, length: _2!) } public static func parse_messageEntityCashtag(_ reader: BufferReader) -> MessageEntity? { var _1: Int32? @@ -312,12 +299,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageEntity.messageEntityCashtag(offset: _1!, length: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageEntity.messageEntityCashtag(offset: _1!, length: _2!) } public static func parse_messageEntityCode(_ reader: BufferReader) -> MessageEntity? { var _1: Int32? @@ -326,12 +310,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageEntity.messageEntityCode(offset: _1!, length: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageEntity.messageEntityCode(offset: _1!, length: _2!) } public static func parse_messageEntityCustomEmoji(_ reader: BufferReader) -> MessageEntity? { var _1: Int32? @@ -343,12 +324,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.MessageEntity.messageEntityCustomEmoji(offset: _1!, length: _2!, documentId: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.MessageEntity.messageEntityCustomEmoji(offset: _1!, length: _2!, documentId: _3!) } public static func parse_messageEntityEmail(_ reader: BufferReader) -> MessageEntity? { var _1: Int32? @@ -357,12 +336,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageEntity.messageEntityEmail(offset: _1!, length: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageEntity.messageEntityEmail(offset: _1!, length: _2!) } public static func parse_messageEntityHashtag(_ reader: BufferReader) -> MessageEntity? { var _1: Int32? @@ -371,12 +347,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageEntity.messageEntityHashtag(offset: _1!, length: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageEntity.messageEntityHashtag(offset: _1!, length: _2!) } public static func parse_messageEntityItalic(_ reader: BufferReader) -> MessageEntity? { var _1: Int32? @@ -385,12 +358,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageEntity.messageEntityItalic(offset: _1!, length: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageEntity.messageEntityItalic(offset: _1!, length: _2!) } public static func parse_messageEntityMention(_ reader: BufferReader) -> MessageEntity? { var _1: Int32? @@ -399,12 +369,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageEntity.messageEntityMention(offset: _1!, length: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageEntity.messageEntityMention(offset: _1!, length: _2!) } public static func parse_messageEntityMentionName(_ reader: BufferReader) -> MessageEntity? { var _1: Int32? @@ -416,12 +383,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.MessageEntity.messageEntityMentionName(offset: _1!, length: _2!, userId: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.MessageEntity.messageEntityMentionName(offset: _1!, length: _2!, userId: _3!) } public static func parse_messageEntityPhone(_ reader: BufferReader) -> MessageEntity? { var _1: Int32? @@ -430,12 +395,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageEntity.messageEntityPhone(offset: _1!, length: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageEntity.messageEntityPhone(offset: _1!, length: _2!) } public static func parse_messageEntityPre(_ reader: BufferReader) -> MessageEntity? { var _1: Int32? @@ -447,12 +409,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.MessageEntity.messageEntityPre(offset: _1!, length: _2!, language: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.MessageEntity.messageEntityPre(offset: _1!, length: _2!, language: _3!) } public static func parse_messageEntitySpoiler(_ reader: BufferReader) -> MessageEntity? { var _1: Int32? @@ -461,12 +421,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageEntity.messageEntitySpoiler(offset: _1!, length: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageEntity.messageEntitySpoiler(offset: _1!, length: _2!) } public static func parse_messageEntityStrike(_ reader: BufferReader) -> MessageEntity? { var _1: Int32? @@ -475,12 +432,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageEntity.messageEntityStrike(offset: _1!, length: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageEntity.messageEntityStrike(offset: _1!, length: _2!) } public static func parse_messageEntityTextUrl(_ reader: BufferReader) -> MessageEntity? { var _1: Int32? @@ -492,12 +446,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.MessageEntity.messageEntityTextUrl(offset: _1!, length: _2!, url: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.MessageEntity.messageEntityTextUrl(offset: _1!, length: _2!, url: _3!) } public static func parse_messageEntityUnderline(_ reader: BufferReader) -> MessageEntity? { var _1: Int32? @@ -506,12 +458,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageEntity.messageEntityUnderline(offset: _1!, length: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageEntity.messageEntityUnderline(offset: _1!, length: _2!) } public static func parse_messageEntityUnknown(_ reader: BufferReader) -> MessageEntity? { var _1: Int32? @@ -520,12 +469,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageEntity.messageEntityUnknown(offset: _1!, length: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageEntity.messageEntityUnknown(offset: _1!, length: _2!) } public static func parse_messageEntityUrl(_ reader: BufferReader) -> MessageEntity? { var _1: Int32? @@ -534,12 +480,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageEntity.messageEntityUrl(offset: _1!, length: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageEntity.messageEntityUrl(offset: _1!, length: _2!) } } @@ -585,12 +528,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.MessageMedia } let _c1 = _1 != nil - if _c1 { - return Api.MessageExtendedMedia.messageExtendedMedia(media: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.MessageExtendedMedia.messageExtendedMedia(media: _1!) } public static func parse_messageExtendedMediaPreview(_ reader: BufferReader) -> MessageExtendedMedia? { var _1: Int32? @@ -610,12 +549,12 @@ public extension Api { let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.MessageExtendedMedia.messageExtendedMediaPreview(flags: _1!, w: _2, h: _3, thumb: _4, videoDuration: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.MessageExtendedMedia.messageExtendedMediaPreview(flags: _1!, w: _2, h: _3, thumb: _4, videoDuration: _5) } } @@ -696,12 +635,19 @@ public extension Api { let _c10 = (Int(_1!) & Int(1 << 9) == 0) || _10 != nil let _c11 = (Int(_1!) & Int(1 << 10) == 0) || _11 != nil let _c12 = (Int(_1!) & Int(1 << 6) == 0) || _12 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 { - return Api.MessageFwdHeader.messageFwdHeader(flags: _1!, fromId: _2, fromName: _3, date: _4!, channelPost: _5, postAuthor: _6, savedFromPeer: _7, savedFromMsgId: _8, savedFromId: _9, savedFromName: _10, savedDate: _11, psaType: _12) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + return Api.MessageFwdHeader.messageFwdHeader(flags: _1!, fromId: _2, fromName: _3, date: _4!, channelPost: _5, postAuthor: _6, savedFromPeer: _7, savedFromMsgId: _8, savedFromId: _9, savedFromName: _10, savedDate: _11, psaType: _12) } } @@ -709,7 +655,7 @@ public extension Api { public extension Api { indirect enum MessageMedia: TypeConstructorDescription { case messageMediaContact(phoneNumber: String, firstName: String, lastName: String, vcard: String, userId: Int64) - case messageMediaDice(value: Int32, emoticon: String) + case messageMediaDice(flags: Int32, value: Int32, emoticon: String, gameOutcome: Api.messages.EmojiGameOutcome?) case messageMediaDocument(flags: Int32, document: Api.Document?, altDocuments: [Api.Document]?, videoCover: Api.Photo?, videoTimestamp: Int32?, ttlSeconds: Int32?) case messageMediaEmpty case messageMediaGame(game: Api.Game) @@ -740,12 +686,14 @@ public extension Api { serializeString(vcard, buffer: buffer, boxed: false) serializeInt64(userId, buffer: buffer, boxed: false) break - case .messageMediaDice(let value, let emoticon): + case .messageMediaDice(let flags, let value, let emoticon, let gameOutcome): if boxed { - buffer.appendInt32(1065280907) + buffer.appendInt32(147581959) } + serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(value, buffer: buffer, boxed: false) serializeString(emoticon, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {gameOutcome!.serialize(buffer, true)} break case .messageMediaDocument(let flags, let document, let altDocuments, let videoCover, let videoTimestamp, let ttlSeconds): if boxed { @@ -930,8 +878,8 @@ public extension Api { switch self { case .messageMediaContact(let phoneNumber, let firstName, let lastName, let vcard, let userId): return ("messageMediaContact", [("phoneNumber", phoneNumber as Any), ("firstName", firstName as Any), ("lastName", lastName as Any), ("vcard", vcard as Any), ("userId", userId as Any)]) - case .messageMediaDice(let value, let emoticon): - return ("messageMediaDice", [("value", value as Any), ("emoticon", emoticon as Any)]) + case .messageMediaDice(let flags, let value, let emoticon, let gameOutcome): + return ("messageMediaDice", [("flags", flags as Any), ("value", value as Any), ("emoticon", emoticon as Any), ("gameOutcome", gameOutcome as Any)]) case .messageMediaDocument(let flags, let document, let altDocuments, let videoCover, let videoTimestamp, let ttlSeconds): return ("messageMediaDocument", [("flags", flags as Any), ("document", document as Any), ("altDocuments", altDocuments as Any), ("videoCover", videoCover as Any), ("videoTimestamp", videoTimestamp as Any), ("ttlSeconds", ttlSeconds as Any)]) case .messageMediaEmpty: @@ -985,26 +933,33 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.MessageMedia.messageMediaContact(phoneNumber: _1!, firstName: _2!, lastName: _3!, vcard: _4!, userId: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.MessageMedia.messageMediaContact(phoneNumber: _1!, firstName: _2!, lastName: _3!, vcard: _4!, userId: _5!) } public static func parse_messageMediaDice(_ reader: BufferReader) -> MessageMedia? { var _1: Int32? _1 = reader.readInt32() - var _2: String? - _2 = parseString(reader) + var _2: Int32? + _2 = reader.readInt32() + var _3: String? + _3 = parseString(reader) + var _4: Api.messages.EmojiGameOutcome? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.messages.EmojiGameOutcome + } } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageMedia.messageMediaDice(value: _1!, emoticon: _2!) - } - else { - return nil - } + let _c3 = _3 != nil + let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.MessageMedia.messageMediaDice(flags: _1!, value: _2!, emoticon: _3!, gameOutcome: _4) } public static func parse_messageMediaDocument(_ reader: BufferReader) -> MessageMedia? { var _1: Int32? @@ -1031,12 +986,13 @@ public extension Api { let _c4 = (Int(_1!) & Int(1 << 9) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 10) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.MessageMedia.messageMediaDocument(flags: _1!, document: _2, altDocuments: _3, videoCover: _4, videoTimestamp: _5, ttlSeconds: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.MessageMedia.messageMediaDocument(flags: _1!, document: _2, altDocuments: _3, videoCover: _4, videoTimestamp: _5, ttlSeconds: _6) } public static func parse_messageMediaEmpty(_ reader: BufferReader) -> MessageMedia? { return Api.MessageMedia.messageMediaEmpty @@ -1047,12 +1003,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Game } let _c1 = _1 != nil - if _c1 { - return Api.MessageMedia.messageMediaGame(game: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.MessageMedia.messageMediaGame(game: _1!) } public static func parse_messageMediaGeo(_ reader: BufferReader) -> MessageMedia? { var _1: Api.GeoPoint? @@ -1060,12 +1012,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.GeoPoint } let _c1 = _1 != nil - if _c1 { - return Api.MessageMedia.messageMediaGeo(geo: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.MessageMedia.messageMediaGeo(geo: _1!) } public static func parse_messageMediaGeoLive(_ reader: BufferReader) -> MessageMedia? { var _1: Int32? @@ -1085,12 +1033,12 @@ public extension Api { let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = _4 != nil let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.MessageMedia.messageMediaGeoLive(flags: _1!, geo: _2!, heading: _3, period: _4!, proximityNotificationRadius: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.MessageMedia.messageMediaGeoLive(flags: _1!, geo: _2!, heading: _3, period: _4!, proximityNotificationRadius: _5) } public static func parse_messageMediaGiveaway(_ reader: BufferReader) -> MessageMedia? { var _1: Int32? @@ -1121,12 +1069,15 @@ public extension Api { let _c6 = (Int(_1!) & Int(1 << 4) == 0) || _6 != nil let _c7 = (Int(_1!) & Int(1 << 5) == 0) || _7 != nil let _c8 = _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.MessageMedia.messageMediaGiveaway(flags: _1!, channels: _2!, countriesIso2: _3, prizeDescription: _4, quantity: _5!, months: _6, stars: _7, untilDate: _8!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.MessageMedia.messageMediaGiveaway(flags: _1!, channels: _2!, countriesIso2: _3, prizeDescription: _4, quantity: _5!, months: _6, stars: _7, untilDate: _8!) } public static func parse_messageMediaGiveawayResults(_ reader: BufferReader) -> MessageMedia? { var _1: Int32? @@ -1164,12 +1115,18 @@ public extension Api { let _c9 = (Int(_1!) & Int(1 << 5) == 0) || _9 != nil let _c10 = (Int(_1!) & Int(1 << 1) == 0) || _10 != nil let _c11 = _11 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 { - return Api.MessageMedia.messageMediaGiveawayResults(flags: _1!, channelId: _2!, additionalPeersCount: _3, launchMsgId: _4!, winnersCount: _5!, unclaimedCount: _6!, winners: _7!, months: _8, stars: _9, prizeDescription: _10, untilDate: _11!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + return Api.MessageMedia.messageMediaGiveawayResults(flags: _1!, channelId: _2!, additionalPeersCount: _3, launchMsgId: _4!, winnersCount: _5!, unclaimedCount: _6!, winners: _7!, months: _8, stars: _9, prizeDescription: _10, untilDate: _11!) } public static func parse_messageMediaInvoice(_ reader: BufferReader) -> MessageMedia? { var _1: Int32? @@ -1203,12 +1160,16 @@ public extension Api { let _c7 = _7 != nil let _c8 = _8 != nil let _c9 = (Int(_1!) & Int(1 << 4) == 0) || _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.MessageMedia.messageMediaInvoice(flags: _1!, title: _2!, description: _3!, photo: _4, receiptMsgId: _5, currency: _6!, totalAmount: _7!, startParam: _8!, extendedMedia: _9) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + return Api.MessageMedia.messageMediaInvoice(flags: _1!, title: _2!, description: _3!, photo: _4, receiptMsgId: _5, currency: _6!, totalAmount: _7!, startParam: _8!, extendedMedia: _9) } public static func parse_messageMediaPaidMedia(_ reader: BufferReader) -> MessageMedia? { var _1: Int64? @@ -1219,12 +1180,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageMedia.messageMediaPaidMedia(starsAmount: _1!, extendedMedia: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageMedia.messageMediaPaidMedia(starsAmount: _1!, extendedMedia: _2!) } public static func parse_messageMediaPhoto(_ reader: BufferReader) -> MessageMedia? { var _1: Int32? @@ -1238,12 +1196,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.MessageMedia.messageMediaPhoto(flags: _1!, photo: _2, ttlSeconds: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.MessageMedia.messageMediaPhoto(flags: _1!, photo: _2, ttlSeconds: _3) } public static func parse_messageMediaPoll(_ reader: BufferReader) -> MessageMedia? { var _1: Api.Poll? @@ -1256,12 +1212,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageMedia.messageMediaPoll(poll: _1!, results: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageMedia.messageMediaPoll(poll: _1!, results: _2!) } public static func parse_messageMediaStory(_ reader: BufferReader) -> MessageMedia? { var _1: Int32? @@ -1280,12 +1233,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.MessageMedia.messageMediaStory(flags: _1!, peer: _2!, id: _3!, story: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.MessageMedia.messageMediaStory(flags: _1!, peer: _2!, id: _3!, story: _4) } public static func parse_messageMediaToDo(_ reader: BufferReader) -> MessageMedia? { var _1: Int32? @@ -1301,12 +1253,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.MessageMedia.messageMediaToDo(flags: _1!, todo: _2!, completions: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.MessageMedia.messageMediaToDo(flags: _1!, todo: _2!, completions: _3) } public static func parse_messageMediaUnsupported(_ reader: BufferReader) -> MessageMedia? { return Api.MessageMedia.messageMediaUnsupported @@ -1332,12 +1282,13 @@ public extension Api { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.MessageMedia.messageMediaVenue(geo: _1!, title: _2!, address: _3!, provider: _4!, venueId: _5!, venueType: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.MessageMedia.messageMediaVenue(geo: _1!, title: _2!, address: _3!, provider: _4!, venueId: _5!, venueType: _6!) } public static func parse_messageMediaVideoStream(_ reader: BufferReader) -> MessageMedia? { var _1: Int32? @@ -1348,12 +1299,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageMedia.messageMediaVideoStream(flags: _1!, call: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageMedia.messageMediaVideoStream(flags: _1!, call: _2!) } public static func parse_messageMediaWebPage(_ reader: BufferReader) -> MessageMedia? { var _1: Int32? @@ -1364,12 +1312,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageMedia.messageMediaWebPage(flags: _1!, webpage: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageMedia.messageMediaWebPage(flags: _1!, webpage: _2!) } } diff --git a/submodules/TelegramApi/Sources/Api17.swift b/submodules/TelegramApi/Sources/Api17.swift index abefb9f7..5cc52b37 100644 --- a/submodules/TelegramApi/Sources/Api17.swift +++ b/submodules/TelegramApi/Sources/Api17.swift @@ -40,12 +40,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.MessagePeerReaction.messagePeerReaction(flags: _1!, peerId: _2!, date: _3!, reaction: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.MessagePeerReaction.messagePeerReaction(flags: _1!, peerId: _2!, date: _3!, reaction: _4!) } } @@ -111,12 +110,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.MessagePeerVote.messagePeerVote(peer: _1!, option: _2!, date: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.MessagePeerVote.messagePeerVote(peer: _1!, option: _2!, date: _3!) } public static func parse_messagePeerVoteInputOption(_ reader: BufferReader) -> MessagePeerVote? { var _1: Api.Peer? @@ -127,12 +124,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessagePeerVote.messagePeerVoteInputOption(peer: _1!, date: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessagePeerVote.messagePeerVoteInputOption(peer: _1!, date: _2!) } public static func parse_messagePeerVoteMultiple(_ reader: BufferReader) -> MessagePeerVote? { var _1: Api.Peer? @@ -148,12 +142,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.MessagePeerVote.messagePeerVoteMultiple(peer: _1!, options: _2!, date: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.MessagePeerVote.messagePeerVoteMultiple(peer: _1!, options: _2!, date: _3!) } } @@ -188,12 +180,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageRange.messageRange(minId: _1!, maxId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageRange.messageRange(minId: _1!, maxId: _2!) } } @@ -254,12 +243,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 4) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.MessageReactions.messageReactions(flags: _1!, results: _2!, recentReactions: _3, topReactors: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.MessageReactions.messageReactions(flags: _1!, results: _2!, recentReactions: _3, topReactors: _4) } } @@ -300,12 +288,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 3) == 0) || _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.MessageReactor.messageReactor(flags: _1!, peerId: _2, count: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.MessageReactor.messageReactor(flags: _1!, peerId: _2, count: _3!) } } @@ -366,12 +352,14 @@ public extension Api { let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil let _c7 = (Int(_1!) & Int(1 << 3) == 0) || _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.MessageReplies.messageReplies(flags: _1!, replies: _2!, repliesPts: _3!, recentRepliers: _4, channelId: _5, maxId: _6, readMaxId: _7) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.MessageReplies.messageReplies(flags: _1!, replies: _2!, repliesPts: _3!, recentRepliers: _4, channelId: _5, maxId: _6, readMaxId: _7) } } @@ -460,12 +448,17 @@ public extension Api { let _c8 = (Int(_1!) & Int(1 << 7) == 0) || _8 != nil let _c9 = (Int(_1!) & Int(1 << 10) == 0) || _9 != nil let _c10 = (Int(_1!) & Int(1 << 11) == 0) || _10 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 { - return Api.MessageReplyHeader.messageReplyHeader(flags: _1!, replyToMsgId: _2, replyToPeerId: _3, replyFrom: _4, replyMedia: _5, replyToTopId: _6, quoteText: _7, quoteEntities: _8, quoteOffset: _9, todoItemId: _10) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + return Api.MessageReplyHeader.messageReplyHeader(flags: _1!, replyToMsgId: _2, replyToPeerId: _3, replyFrom: _4, replyMedia: _5, replyToTopId: _6, quoteText: _7, quoteEntities: _8, quoteOffset: _9, todoItemId: _10) } public static func parse_messageReplyStoryHeader(_ reader: BufferReader) -> MessageReplyHeader? { var _1: Api.Peer? @@ -476,12 +469,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageReplyHeader.messageReplyStoryHeader(peer: _1!, storyId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageReplyHeader.messageReplyStoryHeader(peer: _1!, storyId: _2!) } } @@ -516,12 +506,9 @@ public extension Api { _2 = parseBytes(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MessageReportOption.messageReportOption(text: _1!, option: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MessageReportOption.messageReportOption(text: _1!, option: _2!) } } @@ -566,12 +553,11 @@ public extension Api { let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.MessageViews.messageViews(flags: _1!, views: _2, forwards: _3, replies: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.MessageViews.messageViews(flags: _1!, views: _2, forwards: _3, replies: _4) } } @@ -770,12 +756,8 @@ public extension Api { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.MessagesFilter.inputMessagesFilterPhoneCalls(flags: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.MessagesFilter.inputMessagesFilterPhoneCalls(flags: _1!) } public static func parse_inputMessagesFilterPhotoVideo(_ reader: BufferReader) -> MessagesFilter? { return Api.MessagesFilter.inputMessagesFilterPhotoVideo @@ -834,12 +816,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.MissingInvitee.missingInvitee(flags: _1!, userId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.MissingInvitee.missingInvitee(flags: _1!, userId: _2!) } } @@ -892,12 +871,13 @@ public extension Api { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.MyBoost.myBoost(flags: _1!, slot: _2!, peer: _3, date: _4!, expires: _5!, cooldownUntilDate: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.MyBoost.myBoost(flags: _1!, slot: _2!, peer: _3, date: _4!, expires: _5!, cooldownUntilDate: _6) } } @@ -936,12 +916,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.NearestDc.nearestDc(country: _1!, thisDc: _2!, nearestDc: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.NearestDc.nearestDc(country: _1!, thisDc: _2!, nearestDc: _3!) } } diff --git a/submodules/TelegramApi/Sources/Api18.swift b/submodules/TelegramApi/Sources/Api18.swift index 11ea4e2a..f14c2c27 100644 --- a/submodules/TelegramApi/Sources/Api18.swift +++ b/submodules/TelegramApi/Sources/Api18.swift @@ -58,12 +58,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.NotificationSound.notificationSoundLocal(title: _1!, data: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.NotificationSound.notificationSoundLocal(title: _1!, data: _2!) } public static func parse_notificationSoundNone(_ reader: BufferReader) -> NotificationSound? { return Api.NotificationSound.notificationSoundNone @@ -72,12 +69,8 @@ public extension Api { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.NotificationSound.notificationSoundRingtone(id: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.NotificationSound.notificationSoundRingtone(id: _1!) } } @@ -156,12 +149,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.NotifyPeer.notifyForumTopic(peer: _1!, topMsgId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.NotifyPeer.notifyForumTopic(peer: _1!, topMsgId: _2!) } public static func parse_notifyPeer(_ reader: BufferReader) -> NotifyPeer? { var _1: Api.Peer? @@ -169,12 +159,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Peer } let _c1 = _1 != nil - if _c1 { - return Api.NotifyPeer.notifyPeer(peer: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.NotifyPeer.notifyPeer(peer: _1!) } public static func parse_notifyUsers(_ reader: BufferReader) -> NotifyPeer? { return Api.NotifyPeer.notifyUsers @@ -208,12 +194,8 @@ public extension Api { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.OutboxReadDate.outboxReadDate(date: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.OutboxReadDate.outboxReadDate(date: _1!) } } @@ -282,12 +264,13 @@ public extension Api { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = (Int(_1!) & Int(1 << 3) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.Page.page(flags: _1!, url: _2!, blocks: _3!, photos: _4!, documents: _5!, views: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.Page.page(flags: _1!, url: _2!, blocks: _3!, photos: _4!, documents: _5!, views: _6) } } @@ -636,12 +619,8 @@ public extension Api { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.PageBlock.pageBlockAnchor(name: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.PageBlock.pageBlockAnchor(name: _1!) } public static func parse_pageBlockAudio(_ reader: BufferReader) -> PageBlock? { var _1: Int64? @@ -652,12 +631,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.PageBlock.pageBlockAudio(audioId: _1!, caption: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.PageBlock.pageBlockAudio(audioId: _1!, caption: _2!) } public static func parse_pageBlockAuthorDate(_ reader: BufferReader) -> PageBlock? { var _1: Api.RichText? @@ -668,12 +644,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.PageBlock.pageBlockAuthorDate(author: _1!, publishedDate: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.PageBlock.pageBlockAuthorDate(author: _1!, publishedDate: _2!) } public static func parse_pageBlockBlockquote(_ reader: BufferReader) -> PageBlock? { var _1: Api.RichText? @@ -686,12 +659,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.PageBlock.pageBlockBlockquote(text: _1!, caption: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.PageBlock.pageBlockBlockquote(text: _1!, caption: _2!) } public static func parse_pageBlockChannel(_ reader: BufferReader) -> PageBlock? { var _1: Api.Chat? @@ -699,12 +669,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Chat } let _c1 = _1 != nil - if _c1 { - return Api.PageBlock.pageBlockChannel(channel: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.PageBlock.pageBlockChannel(channel: _1!) } public static func parse_pageBlockCollage(_ reader: BufferReader) -> PageBlock? { var _1: [Api.PageBlock]? @@ -717,12 +683,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.PageBlock.pageBlockCollage(items: _1!, caption: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.PageBlock.pageBlockCollage(items: _1!, caption: _2!) } public static func parse_pageBlockCover(_ reader: BufferReader) -> PageBlock? { var _1: Api.PageBlock? @@ -730,12 +693,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.PageBlock } let _c1 = _1 != nil - if _c1 { - return Api.PageBlock.pageBlockCover(cover: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.PageBlock.pageBlockCover(cover: _1!) } public static func parse_pageBlockDetails(_ reader: BufferReader) -> PageBlock? { var _1: Int32? @@ -751,12 +710,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.PageBlock.pageBlockDetails(flags: _1!, blocks: _2!, title: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.PageBlock.pageBlockDetails(flags: _1!, blocks: _2!, title: _3!) } public static func parse_pageBlockDivider(_ reader: BufferReader) -> PageBlock? { return Api.PageBlock.pageBlockDivider @@ -785,12 +742,14 @@ public extension Api { let _c5 = (Int(_1!) & Int(1 << 5) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 5) == 0) || _6 != nil let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.PageBlock.pageBlockEmbed(flags: _1!, url: _2, html: _3, posterPhotoId: _4, w: _5, h: _6, caption: _7!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.PageBlock.pageBlockEmbed(flags: _1!, url: _2, html: _3, posterPhotoId: _4, w: _5, h: _6, caption: _7!) } public static func parse_pageBlockEmbedPost(_ reader: BufferReader) -> PageBlock? { var _1: String? @@ -818,12 +777,14 @@ public extension Api { let _c5 = _5 != nil let _c6 = _6 != nil let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.PageBlock.pageBlockEmbedPost(url: _1!, webpageId: _2!, authorPhotoId: _3!, author: _4!, date: _5!, blocks: _6!, caption: _7!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.PageBlock.pageBlockEmbedPost(url: _1!, webpageId: _2!, authorPhotoId: _3!, author: _4!, date: _5!, blocks: _6!, caption: _7!) } public static func parse_pageBlockFooter(_ reader: BufferReader) -> PageBlock? { var _1: Api.RichText? @@ -831,12 +792,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.RichText } let _c1 = _1 != nil - if _c1 { - return Api.PageBlock.pageBlockFooter(text: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.PageBlock.pageBlockFooter(text: _1!) } public static func parse_pageBlockHeader(_ reader: BufferReader) -> PageBlock? { var _1: Api.RichText? @@ -844,12 +801,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.RichText } let _c1 = _1 != nil - if _c1 { - return Api.PageBlock.pageBlockHeader(text: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.PageBlock.pageBlockHeader(text: _1!) } public static func parse_pageBlockKicker(_ reader: BufferReader) -> PageBlock? { var _1: Api.RichText? @@ -857,12 +810,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.RichText } let _c1 = _1 != nil - if _c1 { - return Api.PageBlock.pageBlockKicker(text: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.PageBlock.pageBlockKicker(text: _1!) } public static func parse_pageBlockList(_ reader: BufferReader) -> PageBlock? { var _1: [Api.PageListItem]? @@ -870,12 +819,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PageListItem.self) } let _c1 = _1 != nil - if _c1 { - return Api.PageBlock.pageBlockList(items: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.PageBlock.pageBlockList(items: _1!) } public static func parse_pageBlockMap(_ reader: BufferReader) -> PageBlock? { var _1: Api.GeoPoint? @@ -897,12 +842,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.PageBlock.pageBlockMap(geo: _1!, zoom: _2!, w: _3!, h: _4!, caption: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.PageBlock.pageBlockMap(geo: _1!, zoom: _2!, w: _3!, h: _4!, caption: _5!) } public static func parse_pageBlockOrderedList(_ reader: BufferReader) -> PageBlock? { var _1: [Api.PageListOrderedItem]? @@ -910,12 +855,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PageListOrderedItem.self) } let _c1 = _1 != nil - if _c1 { - return Api.PageBlock.pageBlockOrderedList(items: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.PageBlock.pageBlockOrderedList(items: _1!) } public static func parse_pageBlockParagraph(_ reader: BufferReader) -> PageBlock? { var _1: Api.RichText? @@ -923,12 +864,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.RichText } let _c1 = _1 != nil - if _c1 { - return Api.PageBlock.pageBlockParagraph(text: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.PageBlock.pageBlockParagraph(text: _1!) } public static func parse_pageBlockPhoto(_ reader: BufferReader) -> PageBlock? { var _1: Int32? @@ -948,12 +885,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.PageBlock.pageBlockPhoto(flags: _1!, photoId: _2!, caption: _3!, url: _4, webpageId: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.PageBlock.pageBlockPhoto(flags: _1!, photoId: _2!, caption: _3!, url: _4, webpageId: _5) } public static func parse_pageBlockPreformatted(_ reader: BufferReader) -> PageBlock? { var _1: Api.RichText? @@ -964,12 +901,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.PageBlock.pageBlockPreformatted(text: _1!, language: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.PageBlock.pageBlockPreformatted(text: _1!, language: _2!) } public static func parse_pageBlockPullquote(_ reader: BufferReader) -> PageBlock? { var _1: Api.RichText? @@ -982,12 +916,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.PageBlock.pageBlockPullquote(text: _1!, caption: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.PageBlock.pageBlockPullquote(text: _1!, caption: _2!) } public static func parse_pageBlockRelatedArticles(_ reader: BufferReader) -> PageBlock? { var _1: Api.RichText? @@ -1000,12 +931,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.PageBlock.pageBlockRelatedArticles(title: _1!, articles: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.PageBlock.pageBlockRelatedArticles(title: _1!, articles: _2!) } public static func parse_pageBlockSlideshow(_ reader: BufferReader) -> PageBlock? { var _1: [Api.PageBlock]? @@ -1018,12 +946,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.PageBlock.pageBlockSlideshow(items: _1!, caption: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.PageBlock.pageBlockSlideshow(items: _1!, caption: _2!) } public static func parse_pageBlockSubheader(_ reader: BufferReader) -> PageBlock? { var _1: Api.RichText? @@ -1031,12 +956,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.RichText } let _c1 = _1 != nil - if _c1 { - return Api.PageBlock.pageBlockSubheader(text: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.PageBlock.pageBlockSubheader(text: _1!) } public static func parse_pageBlockSubtitle(_ reader: BufferReader) -> PageBlock? { var _1: Api.RichText? @@ -1044,12 +965,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.RichText } let _c1 = _1 != nil - if _c1 { - return Api.PageBlock.pageBlockSubtitle(text: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.PageBlock.pageBlockSubtitle(text: _1!) } public static func parse_pageBlockTable(_ reader: BufferReader) -> PageBlock? { var _1: Int32? @@ -1065,12 +982,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.PageBlock.pageBlockTable(flags: _1!, title: _2!, rows: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.PageBlock.pageBlockTable(flags: _1!, title: _2!, rows: _3!) } public static func parse_pageBlockTitle(_ reader: BufferReader) -> PageBlock? { var _1: Api.RichText? @@ -1078,12 +993,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.RichText } let _c1 = _1 != nil - if _c1 { - return Api.PageBlock.pageBlockTitle(text: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.PageBlock.pageBlockTitle(text: _1!) } public static func parse_pageBlockUnsupported(_ reader: BufferReader) -> PageBlock? { return Api.PageBlock.pageBlockUnsupported @@ -1100,12 +1011,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.PageBlock.pageBlockVideo(flags: _1!, videoId: _2!, caption: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.PageBlock.pageBlockVideo(flags: _1!, videoId: _2!, caption: _3!) } } diff --git a/submodules/TelegramApi/Sources/Api19.swift b/submodules/TelegramApi/Sources/Api19.swift index 2cb2a538..76f9fd8e 100644 --- a/submodules/TelegramApi/Sources/Api19.swift +++ b/submodules/TelegramApi/Sources/Api19.swift @@ -32,12 +32,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.PageCaption.pageCaption(text: _1!, credit: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.PageCaption.pageCaption(text: _1!, credit: _2!) } } @@ -83,12 +80,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PageBlock.self) } let _c1 = _1 != nil - if _c1 { - return Api.PageListItem.pageListItemBlocks(blocks: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.PageListItem.pageListItemBlocks(blocks: _1!) } public static func parse_pageListItemText(_ reader: BufferReader) -> PageListItem? { var _1: Api.RichText? @@ -96,12 +89,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.RichText } let _c1 = _1 != nil - if _c1 { - return Api.PageListItem.pageListItemText(text: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.PageListItem.pageListItemText(text: _1!) } } @@ -152,12 +141,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.PageListOrderedItem.pageListOrderedItemBlocks(num: _1!, blocks: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.PageListOrderedItem.pageListOrderedItemBlocks(num: _1!, blocks: _2!) } public static func parse_pageListOrderedItemText(_ reader: BufferReader) -> PageListOrderedItem? { var _1: String? @@ -168,12 +154,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.PageListOrderedItem.pageListOrderedItemText(num: _1!, text: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.PageListOrderedItem.pageListOrderedItemText(num: _1!, text: _2!) } } @@ -232,12 +215,15 @@ public extension Api { let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil let _c7 = (Int(_1!) & Int(1 << 3) == 0) || _7 != nil let _c8 = (Int(_1!) & Int(1 << 4) == 0) || _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.PageRelatedArticle.pageRelatedArticle(flags: _1!, url: _2!, webpageId: _3!, title: _4, description: _5, photoId: _6, author: _7, publishedDate: _8) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.PageRelatedArticle.pageRelatedArticle(flags: _1!, url: _2!, webpageId: _3!, title: _4, description: _5, photoId: _6, author: _7, publishedDate: _8) } } @@ -282,12 +268,11 @@ public extension Api { let _c2 = (Int(_1!) & Int(1 << 7) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.PageTableCell.pageTableCell(flags: _1!, text: _2, colspan: _3, rowspan: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.PageTableCell.pageTableCell(flags: _1!, text: _2, colspan: _3, rowspan: _4) } } @@ -324,12 +309,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PageTableCell.self) } let _c1 = _1 != nil - if _c1 { - return Api.PageTableRow.pageTableRow(cells: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.PageTableRow.pageTableRow(cells: _1!) } } @@ -386,12 +367,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.InputPeer } let _c1 = _1 != nil - if _c1 { - return Api.PaidReactionPrivacy.paidReactionPrivacyPeer(peer: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.PaidReactionPrivacy.paidReactionPrivacyPeer(peer: _1!) } } @@ -442,12 +419,13 @@ public extension Api { let _c4 = _4 != nil let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.Passkey.passkey(flags: _1!, id: _2!, name: _3!, date: _4!, softwareEmojiId: _5, lastUsageDate: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.Passkey.passkey(flags: _1!, id: _2!, name: _3!, date: _4!, softwareEmojiId: _5, lastUsageDate: _6) } } @@ -499,12 +477,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.PasswordKdfAlgo.passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow(salt1: _1!, salt2: _2!, g: _3!, p: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.PasswordKdfAlgo.passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow(salt1: _1!, salt2: _2!, g: _3!, p: _4!) } public static func parse_passwordKdfAlgoUnknown(_ reader: BufferReader) -> PasswordKdfAlgo? { return Api.PasswordKdfAlgo.passwordKdfAlgoUnknown @@ -542,12 +519,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.PaymentCharge.paymentCharge(id: _1!, providerChargeId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.PaymentCharge.paymentCharge(id: _1!, providerChargeId: _2!) } } @@ -582,12 +556,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.PaymentFormMethod.paymentFormMethod(url: _1!, title: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.PaymentFormMethod.paymentFormMethod(url: _1!, title: _2!) } } @@ -636,12 +607,12 @@ public extension Api { let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.PaymentRequestedInfo.paymentRequestedInfo(flags: _1!, name: _2, phone: _3, email: _4, shippingAddress: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.PaymentRequestedInfo.paymentRequestedInfo(flags: _1!, name: _2, phone: _3, email: _4, shippingAddress: _5) } } @@ -676,12 +647,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.PaymentSavedCredentials.paymentSavedCredentialsCard(id: _1!, title: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.PaymentSavedCredentials.paymentSavedCredentialsCard(id: _1!, title: _2!) } } @@ -730,34 +698,22 @@ public extension Api { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.Peer.peerChannel(channelId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Peer.peerChannel(channelId: _1!) } public static func parse_peerChat(_ reader: BufferReader) -> Peer? { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.Peer.peerChat(chatId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Peer.peerChat(chatId: _1!) } public static func parse_peerUser(_ reader: BufferReader) -> Peer? { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.Peer.peerUser(userId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Peer.peerUser(userId: _1!) } } @@ -794,12 +750,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.PeerBlocked.peerBlocked(peerId: _1!, date: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.PeerBlocked.peerBlocked(peerId: _1!, date: _2!) } } @@ -865,12 +818,8 @@ public extension Api { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.PeerColor.inputPeerColorCollectible(collectibleId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.PeerColor.inputPeerColorCollectible(collectibleId: _1!) } public static func parse_peerColor(_ reader: BufferReader) -> PeerColor? { var _1: Int32? @@ -882,12 +831,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.PeerColor.peerColor(flags: _1!, color: _2, backgroundEmojiId: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.PeerColor.peerColor(flags: _1!, color: _2, backgroundEmojiId: _3) } public static func parse_peerColorCollectible(_ reader: BufferReader) -> PeerColor? { var _1: Int32? @@ -918,12 +865,15 @@ public extension Api { let _c6 = _6 != nil let _c7 = (Int(_1!) & Int(1 << 0) == 0) || _7 != nil let _c8 = (Int(_1!) & Int(1 << 1) == 0) || _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.PeerColor.peerColorCollectible(flags: _1!, collectibleId: _2!, giftEmojiId: _3!, backgroundEmojiId: _4!, accentColor: _5!, colors: _6!, darkAccentColor: _7, darkColors: _8) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.PeerColor.peerColorCollectible(flags: _1!, collectibleId: _2!, giftEmojiId: _3!, backgroundEmojiId: _4!, accentColor: _5!, colors: _6!, darkAccentColor: _7, darkColors: _8) } } @@ -973,23 +923,17 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.PeerLocated.peerLocated(peer: _1!, expires: _2!, distance: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.PeerLocated.peerLocated(peer: _1!, expires: _2!, distance: _3!) } public static func parse_peerSelfLocated(_ reader: BufferReader) -> PeerLocated? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.PeerLocated.peerSelfLocated(expires: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.PeerLocated.peerSelfLocated(expires: _1!) } } @@ -1084,12 +1028,19 @@ public extension Api { let _c10 = (Int(_1!) & Int(1 << 8) == 0) || _10 != nil let _c11 = (Int(_1!) & Int(1 << 9) == 0) || _11 != nil let _c12 = (Int(_1!) & Int(1 << 10) == 0) || _12 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 { - return Api.PeerNotifySettings.peerNotifySettings(flags: _1!, showPreviews: _2, silent: _3, muteUntil: _4, iosSound: _5, androidSound: _6, otherSound: _7, storiesMuted: _8, storiesHideSender: _9, storiesIosSound: _10, storiesAndroidSound: _11, storiesOtherSound: _12) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + return Api.PeerNotifySettings.peerNotifySettings(flags: _1!, showPreviews: _2, silent: _3, muteUntil: _4, iosSound: _5, androidSound: _6, otherSound: _7, storiesMuted: _8, storiesHideSender: _9, storiesIosSound: _10, storiesAndroidSound: _11, storiesOtherSound: _12) } } @@ -1160,12 +1111,18 @@ public extension Api { let _c9 = (Int(_1!) & Int(1 << 16) == 0) || _9 != nil let _c10 = (Int(_1!) & Int(1 << 17) == 0) || _10 != nil let _c11 = (Int(_1!) & Int(1 << 18) == 0) || _11 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 { - return Api.PeerSettings.peerSettings(flags: _1!, geoDistance: _2, requestChatTitle: _3, requestChatDate: _4, businessBotId: _5, businessBotManageUrl: _6, chargePaidMessageStars: _7, registrationMonth: _8, phoneCountry: _9, nameChangeDate: _10, photoChangeDate: _11) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + return Api.PeerSettings.peerSettings(flags: _1!, geoDistance: _2, requestChatTitle: _3, requestChatDate: _4, businessBotId: _5, businessBotManageUrl: _6, chargePaidMessageStars: _7, registrationMonth: _8, phoneCountry: _9, nameChangeDate: _10, photoChangeDate: _11) } } @@ -1216,12 +1173,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.PeerStories.peerStories(flags: _1!, peer: _2!, maxReadId: _3, stories: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.PeerStories.peerStories(flags: _1!, peer: _2!, maxReadId: _3, stories: _4!) } } @@ -1268,12 +1224,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.PendingSuggestion.pendingSuggestion(suggestion: _1!, title: _2!, description: _3!, url: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.PendingSuggestion.pendingSuggestion(suggestion: _1!, title: _2!, description: _3!, url: _4!) } } @@ -1427,12 +1382,19 @@ public extension Api { let _c10 = _10 != nil let _c11 = _11 != nil let _c12 = (Int(_1!) & Int(1 << 7) == 0) || _12 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 { - return Api.PhoneCall.phoneCall(flags: _1!, id: _2!, accessHash: _3!, date: _4!, adminId: _5!, participantId: _6!, gAOrB: _7!, keyFingerprint: _8!, protocol: _9!, connections: _10!, startDate: _11!, customParameters: _12) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + return Api.PhoneCall.phoneCall(flags: _1!, id: _2!, accessHash: _3!, date: _4!, adminId: _5!, participantId: _6!, gAOrB: _7!, keyFingerprint: _8!, protocol: _9!, connections: _10!, startDate: _11!, customParameters: _12) } public static func parse_phoneCallAccepted(_ reader: BufferReader) -> PhoneCall? { var _1: Int32? @@ -1461,12 +1423,15 @@ public extension Api { let _c6 = _6 != nil let _c7 = _7 != nil let _c8 = _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.PhoneCall.phoneCallAccepted(flags: _1!, id: _2!, accessHash: _3!, date: _4!, adminId: _5!, participantId: _6!, gB: _7!, protocol: _8!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.PhoneCall.phoneCallAccepted(flags: _1!, id: _2!, accessHash: _3!, date: _4!, adminId: _5!, participantId: _6!, gB: _7!, protocol: _8!) } public static func parse_phoneCallDiscarded(_ reader: BufferReader) -> PhoneCall? { var _1: Int32? @@ -1483,23 +1448,18 @@ public extension Api { let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.PhoneCall.phoneCallDiscarded(flags: _1!, id: _2!, reason: _3, duration: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.PhoneCall.phoneCallDiscarded(flags: _1!, id: _2!, reason: _3, duration: _4) } public static func parse_phoneCallEmpty(_ reader: BufferReader) -> PhoneCall? { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.PhoneCall.phoneCallEmpty(id: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.PhoneCall.phoneCallEmpty(id: _1!) } public static func parse_phoneCallRequested(_ reader: BufferReader) -> PhoneCall? { var _1: Int32? @@ -1528,12 +1488,15 @@ public extension Api { let _c6 = _6 != nil let _c7 = _7 != nil let _c8 = _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.PhoneCall.phoneCallRequested(flags: _1!, id: _2!, accessHash: _3!, date: _4!, adminId: _5!, participantId: _6!, gAHash: _7!, protocol: _8!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.PhoneCall.phoneCallRequested(flags: _1!, id: _2!, accessHash: _3!, date: _4!, adminId: _5!, participantId: _6!, gAHash: _7!, protocol: _8!) } public static func parse_phoneCallWaiting(_ reader: BufferReader) -> PhoneCall? { var _1: Int32? @@ -1562,12 +1525,15 @@ public extension Api { let _c6 = _6 != nil let _c7 = _7 != nil let _c8 = (Int(_1!) & Int(1 << 0) == 0) || _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.PhoneCall.phoneCallWaiting(flags: _1!, id: _2!, accessHash: _3!, date: _4!, adminId: _5!, participantId: _6!, protocol: _7!, receiveDate: _8) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.PhoneCall.phoneCallWaiting(flags: _1!, id: _2!, accessHash: _3!, date: _4!, adminId: _5!, participantId: _6!, protocol: _7!, receiveDate: _8) } } diff --git a/submodules/TelegramApi/Sources/Api2.swift b/submodules/TelegramApi/Sources/Api2.swift index 2c7da602..3bc728b1 100644 --- a/submodules/TelegramApi/Sources/Api2.swift +++ b/submodules/TelegramApi/Sources/Api2.swift @@ -46,12 +46,13 @@ public extension Api { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.BotBusinessConnection.botBusinessConnection(flags: _1!, connectionId: _2!, userId: _3!, dcId: _4!, date: _5!, rights: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.BotBusinessConnection.botBusinessConnection(flags: _1!, connectionId: _2!, userId: _3!, dcId: _4!, date: _5!, rights: _6) } } @@ -86,12 +87,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.BotCommand.botCommand(command: _1!, description: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.BotCommand.botCommand(command: _1!, description: _2!) } } @@ -188,12 +186,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.InputPeer } let _c1 = _1 != nil - if _c1 { - return Api.BotCommandScope.botCommandScopePeer(peer: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.BotCommandScope.botCommandScopePeer(peer: _1!) } public static func parse_botCommandScopePeerAdmins(_ reader: BufferReader) -> BotCommandScope? { var _1: Api.InputPeer? @@ -201,12 +195,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.InputPeer } let _c1 = _1 != nil - if _c1 { - return Api.BotCommandScope.botCommandScopePeerAdmins(peer: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.BotCommandScope.botCommandScopePeerAdmins(peer: _1!) } public static func parse_botCommandScopePeerUser(_ reader: BufferReader) -> BotCommandScope? { var _1: Api.InputPeer? @@ -219,12 +209,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.BotCommandScope.botCommandScopePeerUser(peer: _1!, userId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.BotCommandScope.botCommandScopePeerUser(peer: _1!, userId: _2!) } public static func parse_botCommandScopeUsers(_ reader: BufferReader) -> BotCommandScope? { return Api.BotCommandScope.botCommandScopeUsers @@ -310,12 +297,17 @@ public extension Api { let _c8 = (Int(_1!) & Int(1 << 7) == 0) || _8 != nil let _c9 = (Int(_1!) & Int(1 << 8) == 0) || _9 != nil let _c10 = (Int(_1!) & Int(1 << 9) == 0) || _10 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 { - return Api.BotInfo.botInfo(flags: _1!, userId: _2, description: _3, descriptionPhoto: _4, descriptionDocument: _5, commands: _6, menuButton: _7, privacyPolicyUrl: _8, appSettings: _9, verifierSettings: _10) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + return Api.BotInfo.botInfo(flags: _1!, userId: _2, description: _3, descriptionPhoto: _4, descriptionDocument: _5, commands: _6, menuButton: _7, privacyPolicyUrl: _8, appSettings: _9, verifierSettings: _10) } } @@ -458,12 +450,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.BotInlineMessage.botInlineMessageMediaAuto(flags: _1!, message: _2!, entities: _3, replyMarkup: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.BotInlineMessage.botInlineMessageMediaAuto(flags: _1!, message: _2!, entities: _3, replyMarkup: _4) } public static func parse_botInlineMessageMediaContact(_ reader: BufferReader) -> BotInlineMessage? { var _1: Int32? @@ -486,12 +477,13 @@ public extension Api { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.BotInlineMessage.botInlineMessageMediaContact(flags: _1!, phoneNumber: _2!, firstName: _3!, lastName: _4!, vcard: _5!, replyMarkup: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.BotInlineMessage.botInlineMessageMediaContact(flags: _1!, phoneNumber: _2!, firstName: _3!, lastName: _4!, vcard: _5!, replyMarkup: _6) } public static func parse_botInlineMessageMediaGeo(_ reader: BufferReader) -> BotInlineMessage? { var _1: Int32? @@ -516,12 +508,13 @@ public extension Api { let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.BotInlineMessage.botInlineMessageMediaGeo(flags: _1!, geo: _2!, heading: _3, period: _4, proximityNotificationRadius: _5, replyMarkup: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.BotInlineMessage.botInlineMessageMediaGeo(flags: _1!, geo: _2!, heading: _3, period: _4, proximityNotificationRadius: _5, replyMarkup: _6) } public static func parse_botInlineMessageMediaInvoice(_ reader: BufferReader) -> BotInlineMessage? { var _1: Int32? @@ -549,12 +542,14 @@ public extension Api { let _c5 = _5 != nil let _c6 = _6 != nil let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.BotInlineMessage.botInlineMessageMediaInvoice(flags: _1!, title: _2!, description: _3!, photo: _4, currency: _5!, totalAmount: _6!, replyMarkup: _7) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.BotInlineMessage.botInlineMessageMediaInvoice(flags: _1!, title: _2!, description: _3!, photo: _4, currency: _5!, totalAmount: _6!, replyMarkup: _7) } public static func parse_botInlineMessageMediaVenue(_ reader: BufferReader) -> BotInlineMessage? { var _1: Int32? @@ -585,12 +580,15 @@ public extension Api { let _c6 = _6 != nil let _c7 = _7 != nil let _c8 = (Int(_1!) & Int(1 << 2) == 0) || _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.BotInlineMessage.botInlineMessageMediaVenue(flags: _1!, geo: _2!, title: _3!, address: _4!, provider: _5!, venueId: _6!, venueType: _7!, replyMarkup: _8) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.BotInlineMessage.botInlineMessageMediaVenue(flags: _1!, geo: _2!, title: _3!, address: _4!, provider: _5!, venueId: _6!, venueType: _7!, replyMarkup: _8) } public static func parse_botInlineMessageMediaWebPage(_ reader: BufferReader) -> BotInlineMessage? { var _1: Int32? @@ -612,12 +610,12 @@ public extension Api { let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil let _c4 = _4 != nil let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.BotInlineMessage.botInlineMessageMediaWebPage(flags: _1!, message: _2!, entities: _3, url: _4!, replyMarkup: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.BotInlineMessage.botInlineMessageMediaWebPage(flags: _1!, message: _2!, entities: _3, url: _4!, replyMarkup: _5) } public static func parse_botInlineMessageText(_ reader: BufferReader) -> BotInlineMessage? { var _1: Int32? @@ -636,12 +634,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.BotInlineMessage.botInlineMessageText(flags: _1!, message: _2!, entities: _3, replyMarkup: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.BotInlineMessage.botInlineMessageText(flags: _1!, message: _2!, entities: _3, replyMarkup: _4) } } @@ -723,12 +720,15 @@ public extension Api { let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil let _c7 = (Int(_1!) & Int(1 << 3) == 0) || _7 != nil let _c8 = _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.BotInlineResult.botInlineMediaResult(flags: _1!, id: _2!, type: _3!, photo: _4, document: _5, title: _6, description: _7, sendMessage: _8!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.BotInlineResult.botInlineMediaResult(flags: _1!, id: _2!, type: _3!, photo: _4, document: _5, title: _6, description: _7, sendMessage: _8!) } public static func parse_botInlineResult(_ reader: BufferReader) -> BotInlineResult? { var _1: Int32? @@ -764,12 +764,16 @@ public extension Api { let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil let _c8 = (Int(_1!) & Int(1 << 5) == 0) || _8 != nil let _c9 = _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.BotInlineResult.botInlineResult(flags: _1!, id: _2!, type: _3!, title: _4, description: _5, url: _6, thumb: _7, content: _8, sendMessage: _9!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + return Api.BotInlineResult.botInlineResult(flags: _1!, id: _2!, type: _3!, title: _4, description: _5, url: _6, thumb: _7, content: _8, sendMessage: _9!) } } @@ -822,12 +826,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.BotMenuButton.botMenuButton(text: _1!, url: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.BotMenuButton.botMenuButton(text: _1!, url: _2!) } public static func parse_botMenuButtonCommands(_ reader: BufferReader) -> BotMenuButton? { return Api.BotMenuButton.botMenuButtonCommands @@ -870,12 +871,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.BotPreviewMedia.botPreviewMedia(date: _1!, media: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.BotPreviewMedia.botPreviewMedia(date: _1!, media: _2!) } } @@ -914,12 +912,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.BotVerification.botVerification(botId: _1!, icon: _2!, description: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.BotVerification.botVerification(botId: _1!, icon: _2!, description: _3!) } } @@ -962,12 +958,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.BotVerifierSettings.botVerifierSettings(flags: _1!, icon: _2!, company: _3!, customDescription: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.BotVerifierSettings.botVerifierSettings(flags: _1!, icon: _2!, company: _3!, customDescription: _4) } } @@ -1014,12 +1009,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.BusinessAwayMessage.businessAwayMessage(flags: _1!, shortcutId: _2!, schedule: _3!, recipients: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.BusinessAwayMessage.businessAwayMessage(flags: _1!, shortcutId: _2!, schedule: _3!, recipients: _4!) } } @@ -1075,12 +1069,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.BusinessAwayMessageSchedule.businessAwayMessageScheduleCustom(startDate: _1!, endDate: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.BusinessAwayMessageSchedule.businessAwayMessageScheduleCustom(startDate: _1!, endDate: _2!) } public static func parse_businessAwayMessageScheduleOutsideWorkHours(_ reader: BufferReader) -> BusinessAwayMessageSchedule? { return Api.BusinessAwayMessageSchedule.businessAwayMessageScheduleOutsideWorkHours @@ -1134,12 +1125,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 4) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 6) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.BusinessBotRecipients.businessBotRecipients(flags: _1!, users: _2, excludeUsers: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.BusinessBotRecipients.businessBotRecipients(flags: _1!, users: _2, excludeUsers: _3) } } @@ -1170,12 +1159,8 @@ public extension Api { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.BusinessBotRights.businessBotRights(flags: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.BusinessBotRights.businessBotRights(flags: _1!) } } @@ -1232,12 +1217,13 @@ public extension Api { let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.BusinessChatLink.businessChatLink(flags: _1!, link: _2!, message: _3!, entities: _4, title: _5, views: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.BusinessChatLink.businessChatLink(flags: _1!, link: _2!, message: _3!, entities: _4, title: _5, views: _6!) } } diff --git a/submodules/TelegramApi/Sources/Api20.swift b/submodules/TelegramApi/Sources/Api20.swift index 27e9c338..fff136f0 100644 --- a/submodules/TelegramApi/Sources/Api20.swift +++ b/submodules/TelegramApi/Sources/Api20.swift @@ -69,12 +69,8 @@ public extension Api { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.PhoneCallDiscardReason.phoneCallDiscardReasonMigrateConferenceCall(slug: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.PhoneCallDiscardReason.phoneCallDiscardReasonMigrateConferenceCall(slug: _1!) } public static func parse_phoneCallDiscardReasonMissed(_ reader: BufferReader) -> PhoneCallDiscardReason? { return Api.PhoneCallDiscardReason.phoneCallDiscardReasonMissed @@ -126,12 +122,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.PhoneCallProtocol.phoneCallProtocol(flags: _1!, minLayer: _2!, maxLayer: _3!, libraryVersions: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.PhoneCallProtocol.phoneCallProtocol(flags: _1!, minLayer: _2!, maxLayer: _3!, libraryVersions: _4!) } } @@ -197,12 +192,13 @@ public extension Api { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.PhoneConnection.phoneConnection(flags: _1!, id: _2!, ip: _3!, ipv6: _4!, port: _5!, peerTag: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.PhoneConnection.phoneConnection(flags: _1!, id: _2!, ip: _3!, ipv6: _4!, port: _5!, peerTag: _6!) } public static func parse_phoneConnectionWebrtc(_ reader: BufferReader) -> PhoneConnection? { var _1: Int32? @@ -226,12 +222,14 @@ public extension Api { let _c5 = _5 != nil let _c6 = _6 != nil let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.PhoneConnection.phoneConnectionWebrtc(flags: _1!, id: _2!, ip: _3!, ipv6: _4!, port: _5!, username: _6!, password: _7!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.PhoneConnection.phoneConnectionWebrtc(flags: _1!, id: _2!, ip: _3!, ipv6: _4!, port: _5!, username: _6!, password: _7!) } } @@ -311,23 +309,22 @@ public extension Api { let _c6 = _6 != nil let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil let _c8 = _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.Photo.photo(flags: _1!, id: _2!, accessHash: _3!, fileReference: _4!, date: _5!, sizes: _6!, videoSizes: _7, dcId: _8!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.Photo.photo(flags: _1!, id: _2!, accessHash: _3!, fileReference: _4!, date: _5!, sizes: _6!, videoSizes: _7, dcId: _8!) } public static func parse_photoEmpty(_ reader: BufferReader) -> Photo? { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.Photo.photoEmpty(id: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Photo.photoEmpty(id: _1!) } } @@ -427,12 +424,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.PhotoSize.photoCachedSize(type: _1!, w: _2!, h: _3!, bytes: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.PhotoSize.photoCachedSize(type: _1!, w: _2!, h: _3!, bytes: _4!) } public static func parse_photoPathSize(_ reader: BufferReader) -> PhotoSize? { var _1: String? @@ -441,12 +437,9 @@ public extension Api { _2 = parseBytes(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.PhotoSize.photoPathSize(type: _1!, bytes: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.PhotoSize.photoPathSize(type: _1!, bytes: _2!) } public static func parse_photoSize(_ reader: BufferReader) -> PhotoSize? { var _1: String? @@ -461,23 +454,18 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.PhotoSize.photoSize(type: _1!, w: _2!, h: _3!, size: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.PhotoSize.photoSize(type: _1!, w: _2!, h: _3!, size: _4!) } public static func parse_photoSizeEmpty(_ reader: BufferReader) -> PhotoSize? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.PhotoSize.photoSizeEmpty(type: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.PhotoSize.photoSizeEmpty(type: _1!) } public static func parse_photoSizeProgressive(_ reader: BufferReader) -> PhotoSize? { var _1: String? @@ -494,12 +482,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.PhotoSize.photoSizeProgressive(type: _1!, w: _2!, h: _3!, sizes: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.PhotoSize.photoSizeProgressive(type: _1!, w: _2!, h: _3!, sizes: _4!) } public static func parse_photoStrippedSize(_ reader: BufferReader) -> PhotoSize? { var _1: String? @@ -508,12 +495,9 @@ public extension Api { _2 = parseBytes(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.PhotoSize.photoStrippedSize(type: _1!, bytes: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.PhotoSize.photoStrippedSize(type: _1!, bytes: _2!) } } @@ -572,12 +556,13 @@ public extension Api { let _c4 = _4 != nil let _c5 = (Int(_2!) & Int(1 << 4) == 0) || _5 != nil let _c6 = (Int(_2!) & Int(1 << 5) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.Poll.poll(id: _1!, flags: _2!, question: _3!, answers: _4!, closePeriod: _5, closeDate: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.Poll.poll(id: _1!, flags: _2!, question: _3!, answers: _4!, closePeriod: _5, closeDate: _6) } } @@ -614,12 +599,9 @@ public extension Api { _2 = parseBytes(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.PollAnswer.pollAnswer(text: _1!, option: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.PollAnswer.pollAnswer(text: _1!, option: _2!) } } @@ -658,12 +640,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.PollAnswerVoters.pollAnswerVoters(flags: _1!, option: _2!, voters: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.PollAnswerVoters.pollAnswerVoters(flags: _1!, option: _2!, voters: _3!) } } @@ -732,12 +712,13 @@ public extension Api { let _c4 = (Int(_1!) & Int(1 << 3) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 4) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 4) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.PollResults.pollResults(flags: _1!, results: _2, totalVoters: _3, recentVoters: _4, solution: _5, solutionEntities: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.PollResults.pollResults(flags: _1!, results: _2, totalVoters: _3, recentVoters: _4, solution: _5, solutionEntities: _6) } } @@ -772,12 +753,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.PopularContact.popularContact(clientId: _1!, importers: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.PopularContact.popularContact(clientId: _1!, importers: _2!) } } @@ -828,12 +806,13 @@ public extension Api { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.PostAddress.postAddress(streetLine1: _1!, streetLine2: _2!, city: _3!, state: _4!, countryIso2: _5!, postCode: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.PostAddress.postAddress(streetLine1: _1!, streetLine2: _2!, city: _3!, state: _4!, countryIso2: _5!, postCode: _6!) } } @@ -888,12 +867,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.PostInteractionCounters.postInteractionCountersMessage(msgId: _1!, views: _2!, forwards: _3!, reactions: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.PostInteractionCounters.postInteractionCountersMessage(msgId: _1!, views: _2!, forwards: _3!, reactions: _4!) } public static func parse_postInteractionCountersStory(_ reader: BufferReader) -> PostInteractionCounters? { var _1: Int32? @@ -908,12 +886,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.PostInteractionCounters.postInteractionCountersStory(storyId: _1!, views: _2!, forwards: _3!, reactions: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.PostInteractionCounters.postInteractionCountersStory(storyId: _1!, views: _2!, forwards: _3!, reactions: _4!) } } @@ -968,12 +945,14 @@ public extension Api { let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil let _c6 = _6 != nil let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.PremiumGiftCodeOption.premiumGiftCodeOption(flags: _1!, users: _2!, months: _3!, storeProduct: _4, storeQuantity: _5, currency: _6!, amount: _7!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.PremiumGiftCodeOption.premiumGiftCodeOption(flags: _1!, users: _2!, months: _3!, storeProduct: _4, storeQuantity: _5, currency: _6!, amount: _7!) } } @@ -1028,12 +1007,14 @@ public extension Api { let _c5 = _5 != nil let _c6 = _6 != nil let _c7 = (Int(_1!) & Int(1 << 0) == 0) || _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.PremiumSubscriptionOption.premiumSubscriptionOption(flags: _1!, transaction: _2, months: _3!, currency: _4!, amount: _5!, botUrl: _6!, storeProduct: _7) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.PremiumSubscriptionOption.premiumSubscriptionOption(flags: _1!, transaction: _2, months: _3!, currency: _4!, amount: _5!, botUrl: _6!, storeProduct: _7) } } @@ -1089,12 +1070,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.PrepaidGiveaway.prepaidGiveaway(id: _1!, months: _2!, quantity: _3!, date: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.PrepaidGiveaway.prepaidGiveaway(id: _1!, months: _2!, quantity: _3!, date: _4!) } public static func parse_prepaidStarsGiveaway(_ reader: BufferReader) -> PrepaidGiveaway? { var _1: Int64? @@ -1112,12 +1092,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.PrepaidGiveaway.prepaidStarsGiveaway(id: _1!, stars: _2!, quantity: _3!, boosts: _4!, date: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.PrepaidGiveaway.prepaidStarsGiveaway(id: _1!, stars: _2!, quantity: _3!, boosts: _4!, date: _5!) } } diff --git a/submodules/TelegramApi/Sources/Api21.swift b/submodules/TelegramApi/Sources/Api21.swift index 9d4e9060..a40db16a 100644 --- a/submodules/TelegramApi/Sources/Api21.swift +++ b/submodules/TelegramApi/Sources/Api21.swift @@ -147,12 +147,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) } let _c1 = _1 != nil - if _c1 { - return Api.PrivacyRule.privacyValueAllowChatParticipants(chats: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.PrivacyRule.privacyValueAllowChatParticipants(chats: _1!) } public static func parse_privacyValueAllowCloseFriends(_ reader: BufferReader) -> PrivacyRule? { return Api.PrivacyRule.privacyValueAllowCloseFriends @@ -169,12 +165,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) } let _c1 = _1 != nil - if _c1 { - return Api.PrivacyRule.privacyValueAllowUsers(users: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.PrivacyRule.privacyValueAllowUsers(users: _1!) } public static func parse_privacyValueDisallowAll(_ reader: BufferReader) -> PrivacyRule? { return Api.PrivacyRule.privacyValueDisallowAll @@ -188,12 +180,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) } let _c1 = _1 != nil - if _c1 { - return Api.PrivacyRule.privacyValueDisallowChatParticipants(chats: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.PrivacyRule.privacyValueDisallowChatParticipants(chats: _1!) } public static func parse_privacyValueDisallowContacts(_ reader: BufferReader) -> PrivacyRule? { return Api.PrivacyRule.privacyValueDisallowContacts @@ -204,12 +192,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) } let _c1 = _1 != nil - if _c1 { - return Api.PrivacyRule.privacyValueDisallowUsers(users: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.PrivacyRule.privacyValueDisallowUsers(users: _1!) } } @@ -364,12 +348,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Message } let _c1 = _1 != nil - if _c1 { - return Api.PublicForward.publicForwardMessage(message: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.PublicForward.publicForwardMessage(message: _1!) } public static func parse_publicForwardStory(_ reader: BufferReader) -> PublicForward? { var _1: Api.Peer? @@ -382,12 +362,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.PublicForward.publicForwardStory(peer: _1!, story: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.PublicForward.publicForwardStory(peer: _1!, story: _2!) } } @@ -430,12 +407,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.QuickReply.quickReply(shortcutId: _1!, shortcut: _2!, topMessage: _3!, count: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.QuickReply.quickReply(shortcutId: _1!, shortcut: _2!, topMessage: _3!, count: _4!) } } @@ -493,23 +469,15 @@ public extension Api { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.Reaction.reactionCustomEmoji(documentId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Reaction.reactionCustomEmoji(documentId: _1!) } public static func parse_reactionEmoji(_ reader: BufferReader) -> Reaction? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.Reaction.reactionEmoji(emoticon: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Reaction.reactionEmoji(emoticon: _1!) } public static func parse_reactionEmpty(_ reader: BufferReader) -> Reaction? { return Api.Reaction.reactionEmpty @@ -560,12 +528,11 @@ public extension Api { let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.ReactionCount.reactionCount(flags: _1!, chosenOrder: _2, reaction: _3!, count: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.ReactionCount.reactionCount(flags: _1!, chosenOrder: _2, reaction: _3!, count: _4!) } } @@ -660,12 +627,12 @@ public extension Api { let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.ReactionsNotifySettings.reactionsNotifySettings(flags: _1!, messagesNotifyFrom: _2, storiesNotifyFrom: _3, sound: _4!, showPreviews: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.ReactionsNotifySettings.reactionsNotifySettings(flags: _1!, messagesNotifyFrom: _2, storiesNotifyFrom: _3, sound: _4!, showPreviews: _5!) } } @@ -700,12 +667,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ReadParticipantDate.readParticipantDate(userId: _1!, date: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ReadParticipantDate.readParticipantDate(userId: _1!, date: _2!) } } diff --git a/submodules/TelegramApi/Sources/Api22.swift b/submodules/TelegramApi/Sources/Api22.swift index db61ed18..31bf060e 100644 --- a/submodules/TelegramApi/Sources/Api22.swift +++ b/submodules/TelegramApi/Sources/Api22.swift @@ -28,12 +28,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ReceivedNotifyMessage.receivedNotifyMessage(id: _1!, flags: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ReceivedNotifyMessage.receivedNotifyMessage(id: _1!, flags: _2!) } } @@ -107,12 +104,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.RecentMeUrl.recentMeUrlChat(url: _1!, chatId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.RecentMeUrl.recentMeUrlChat(url: _1!, chatId: _2!) } public static func parse_recentMeUrlChatInvite(_ reader: BufferReader) -> RecentMeUrl? { var _1: String? @@ -123,12 +117,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.RecentMeUrl.recentMeUrlChatInvite(url: _1!, chatInvite: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.RecentMeUrl.recentMeUrlChatInvite(url: _1!, chatInvite: _2!) } public static func parse_recentMeUrlStickerSet(_ reader: BufferReader) -> RecentMeUrl? { var _1: String? @@ -139,23 +130,16 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.RecentMeUrl.recentMeUrlStickerSet(url: _1!, set: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.RecentMeUrl.recentMeUrlStickerSet(url: _1!, set: _2!) } public static func parse_recentMeUrlUnknown(_ reader: BufferReader) -> RecentMeUrl? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.RecentMeUrl.recentMeUrlUnknown(url: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.RecentMeUrl.recentMeUrlUnknown(url: _1!) } public static func parse_recentMeUrlUser(_ reader: BufferReader) -> RecentMeUrl? { var _1: String? @@ -164,12 +148,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.RecentMeUrl.recentMeUrlUser(url: _1!, userId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.RecentMeUrl.recentMeUrlUser(url: _1!, userId: _2!) } } @@ -204,12 +185,9 @@ public extension Api { if Int(_1!) & Int(1 << 1) != 0 {_2 = reader.readInt32() } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil - if _c1 && _c2 { - return Api.RecentStory.recentStory(flags: _1!, maxId: _2) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.RecentStory.recentStory(flags: _1!, maxId: _2) } } @@ -280,12 +258,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.KeyboardButtonRow.self) } let _c1 = _1 != nil - if _c1 { - return Api.ReplyMarkup.replyInlineMarkup(rows: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ReplyMarkup.replyInlineMarkup(rows: _1!) } public static func parse_replyKeyboardForceReply(_ reader: BufferReader) -> ReplyMarkup? { var _1: Int32? @@ -294,23 +268,16 @@ public extension Api { if Int(_1!) & Int(1 << 3) != 0 {_2 = parseString(reader) } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 3) == 0) || _2 != nil - if _c1 && _c2 { - return Api.ReplyMarkup.replyKeyboardForceReply(flags: _1!, placeholder: _2) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ReplyMarkup.replyKeyboardForceReply(flags: _1!, placeholder: _2) } public static func parse_replyKeyboardHide(_ reader: BufferReader) -> ReplyMarkup? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.ReplyMarkup.replyKeyboardHide(flags: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ReplyMarkup.replyKeyboardHide(flags: _1!) } public static func parse_replyKeyboardMarkup(_ reader: BufferReader) -> ReplyMarkup? { var _1: Int32? @@ -324,12 +291,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 3) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.ReplyMarkup.replyKeyboardMarkup(flags: _1!, rows: _2!, placeholder: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.ReplyMarkup.replyKeyboardMarkup(flags: _1!, rows: _2!, placeholder: _3) } } @@ -523,12 +488,9 @@ public extension Api { _2 = parseBytes(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ReportResult.reportResultAddComment(flags: _1!, option: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ReportResult.reportResultAddComment(flags: _1!, option: _2!) } public static func parse_reportResultChooseOption(_ reader: BufferReader) -> ReportResult? { var _1: String? @@ -539,12 +501,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ReportResult.reportResultChooseOption(title: _1!, options: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ReportResult.reportResultChooseOption(title: _1!, options: _2!) } public static func parse_reportResultReported(_ reader: BufferReader) -> ReportResult? { return Api.ReportResult.reportResultReported @@ -620,12 +579,11 @@ public extension Api { let _c2 = (Int(_1!) & Int(1 << 3) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.RequestPeerType.requestPeerTypeBroadcast(flags: _1!, hasUsername: _2, userAdminRights: _3, botAdminRights: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.RequestPeerType.requestPeerTypeBroadcast(flags: _1!, hasUsername: _2, userAdminRights: _3, botAdminRights: _4) } public static func parse_requestPeerTypeChat(_ reader: BufferReader) -> RequestPeerType? { var _1: Int32? @@ -651,12 +609,12 @@ public extension Api { let _c3 = (Int(_1!) & Int(1 << 4) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.RequestPeerType.requestPeerTypeChat(flags: _1!, hasUsername: _2, forum: _3, userAdminRights: _4, botAdminRights: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.RequestPeerType.requestPeerTypeChat(flags: _1!, hasUsername: _2, forum: _3, userAdminRights: _4, botAdminRights: _5) } public static func parse_requestPeerTypeUser(_ reader: BufferReader) -> RequestPeerType? { var _1: Int32? @@ -672,12 +630,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.RequestPeerType.requestPeerTypeUser(flags: _1!, bot: _2, premium: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.RequestPeerType.requestPeerTypeUser(flags: _1!, bot: _2, premium: _3) } } @@ -752,12 +708,12 @@ public extension Api { let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.RequestedPeer.requestedPeerChannel(flags: _1!, channelId: _2!, title: _3, username: _4, photo: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.RequestedPeer.requestedPeerChannel(flags: _1!, channelId: _2!, title: _3, username: _4, photo: _5) } public static func parse_requestedPeerChat(_ reader: BufferReader) -> RequestedPeer? { var _1: Int32? @@ -774,12 +730,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.RequestedPeer.requestedPeerChat(flags: _1!, chatId: _2!, title: _3, photo: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.RequestedPeer.requestedPeerChat(flags: _1!, chatId: _2!, title: _3, photo: _4) } public static func parse_requestedPeerUser(_ reader: BufferReader) -> RequestedPeer? { var _1: Int32? @@ -802,12 +757,13 @@ public extension Api { let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.RequestedPeer.requestedPeerUser(flags: _1!, userId: _2!, firstName: _3, lastName: _4, username: _5, photo: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.RequestedPeer.requestedPeerUser(flags: _1!, userId: _2!, firstName: _3, lastName: _4, username: _5, photo: _6) } } @@ -859,12 +815,8 @@ public extension Api { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.RequirementToContact.requirementToContactPaidMessages(starsAmount: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.RequirementToContact.requirementToContactPaidMessages(starsAmount: _1!) } public static func parse_requirementToContactPremium(_ reader: BufferReader) -> RequirementToContact? { return Api.RequirementToContact.requirementToContactPremium diff --git a/submodules/TelegramApi/Sources/Api23.swift b/submodules/TelegramApi/Sources/Api23.swift index de67bb1f..84004762 100644 --- a/submodules/TelegramApi/Sources/Api23.swift +++ b/submodules/TelegramApi/Sources/Api23.swift @@ -32,12 +32,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.RestrictionReason.restrictionReason(platform: _1!, reason: _2!, text: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.RestrictionReason.restrictionReason(platform: _1!, reason: _2!, text: _3!) } } @@ -219,12 +217,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.RichText.textAnchor(text: _1!, name: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.RichText.textAnchor(text: _1!, name: _2!) } public static func parse_textBold(_ reader: BufferReader) -> RichText? { var _1: Api.RichText? @@ -232,12 +227,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.RichText } let _c1 = _1 != nil - if _c1 { - return Api.RichText.textBold(text: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.RichText.textBold(text: _1!) } public static func parse_textConcat(_ reader: BufferReader) -> RichText? { var _1: [Api.RichText]? @@ -245,12 +236,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RichText.self) } let _c1 = _1 != nil - if _c1 { - return Api.RichText.textConcat(texts: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.RichText.textConcat(texts: _1!) } public static func parse_textEmail(_ reader: BufferReader) -> RichText? { var _1: Api.RichText? @@ -261,12 +248,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.RichText.textEmail(text: _1!, email: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.RichText.textEmail(text: _1!, email: _2!) } public static func parse_textEmpty(_ reader: BufferReader) -> RichText? { return Api.RichText.textEmpty @@ -277,12 +261,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.RichText } let _c1 = _1 != nil - if _c1 { - return Api.RichText.textFixed(text: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.RichText.textFixed(text: _1!) } public static func parse_textImage(_ reader: BufferReader) -> RichText? { var _1: Int64? @@ -294,12 +274,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.RichText.textImage(documentId: _1!, w: _2!, h: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.RichText.textImage(documentId: _1!, w: _2!, h: _3!) } public static func parse_textItalic(_ reader: BufferReader) -> RichText? { var _1: Api.RichText? @@ -307,12 +285,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.RichText } let _c1 = _1 != nil - if _c1 { - return Api.RichText.textItalic(text: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.RichText.textItalic(text: _1!) } public static func parse_textMarked(_ reader: BufferReader) -> RichText? { var _1: Api.RichText? @@ -320,12 +294,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.RichText } let _c1 = _1 != nil - if _c1 { - return Api.RichText.textMarked(text: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.RichText.textMarked(text: _1!) } public static func parse_textPhone(_ reader: BufferReader) -> RichText? { var _1: Api.RichText? @@ -336,23 +306,16 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.RichText.textPhone(text: _1!, phone: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.RichText.textPhone(text: _1!, phone: _2!) } public static func parse_textPlain(_ reader: BufferReader) -> RichText? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.RichText.textPlain(text: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.RichText.textPlain(text: _1!) } public static func parse_textStrike(_ reader: BufferReader) -> RichText? { var _1: Api.RichText? @@ -360,12 +323,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.RichText } let _c1 = _1 != nil - if _c1 { - return Api.RichText.textStrike(text: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.RichText.textStrike(text: _1!) } public static func parse_textSubscript(_ reader: BufferReader) -> RichText? { var _1: Api.RichText? @@ -373,12 +332,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.RichText } let _c1 = _1 != nil - if _c1 { - return Api.RichText.textSubscript(text: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.RichText.textSubscript(text: _1!) } public static func parse_textSuperscript(_ reader: BufferReader) -> RichText? { var _1: Api.RichText? @@ -386,12 +341,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.RichText } let _c1 = _1 != nil - if _c1 { - return Api.RichText.textSuperscript(text: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.RichText.textSuperscript(text: _1!) } public static func parse_textUnderline(_ reader: BufferReader) -> RichText? { var _1: Api.RichText? @@ -399,12 +350,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.RichText } let _c1 = _1 != nil - if _c1 { - return Api.RichText.textUnderline(text: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.RichText.textUnderline(text: _1!) } public static func parse_textUrl(_ reader: BufferReader) -> RichText? { var _1: Api.RichText? @@ -418,12 +365,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.RichText.textUrl(text: _1!, url: _2!, webpageId: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.RichText.textUrl(text: _1!, url: _2!, webpageId: _3!) } } @@ -466,12 +411,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.SavedContact.savedPhoneContact(phone: _1!, firstName: _2!, lastName: _3!, date: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.SavedContact.savedPhoneContact(phone: _1!, firstName: _2!, lastName: _3!, date: _4!) } } @@ -545,12 +489,15 @@ public extension Api { let _c6 = _6 != nil let _c7 = _7 != nil let _c8 = (Int(_1!) & Int(1 << 1) == 0) || _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.SavedDialog.monoForumDialog(flags: _1!, peer: _2!, topMessage: _3!, readInboxMaxId: _4!, readOutboxMaxId: _5!, unreadCount: _6!, unreadReactionsCount: _7!, draft: _8) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.SavedDialog.monoForumDialog(flags: _1!, peer: _2!, topMessage: _3!, readInboxMaxId: _4!, readOutboxMaxId: _5!, unreadCount: _6!, unreadReactionsCount: _7!, draft: _8) } public static func parse_savedDialog(_ reader: BufferReader) -> SavedDialog? { var _1: Int32? @@ -564,12 +511,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.SavedDialog.savedDialog(flags: _1!, peer: _2!, topMessage: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.SavedDialog.savedDialog(flags: _1!, peer: _2!, topMessage: _3!) } } @@ -614,12 +559,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.SavedReactionTag.savedReactionTag(flags: _1!, reaction: _2!, title: _3, count: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.SavedReactionTag.savedReactionTag(flags: _1!, reaction: _2!, title: _3, count: _4!) } } @@ -726,12 +670,24 @@ public extension Api { let _c15 = (Int(_1!) & Int(1 << 16) == 0) || _15 != nil let _c16 = (Int(_1!) & Int(1 << 18) == 0) || _16 != nil let _c17 = (Int(_1!) & Int(1 << 19) == 0) || _17 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 { - return Api.SavedStarGift.savedStarGift(flags: _1!, fromId: _2, date: _3!, gift: _4!, message: _5, msgId: _6, savedId: _7, convertStars: _8, upgradeStars: _9, canExportAt: _10, transferStars: _11, canTransferAt: _12, canResellAt: _13, collectionId: _14, prepaidUpgradeHash: _15, dropOriginalDetailsStars: _16, giftNum: _17) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + if !_c13 { return nil } + if !_c14 { return nil } + if !_c15 { return nil } + if !_c16 { return nil } + if !_c17 { return nil } + return Api.SavedStarGift.savedStarGift(flags: _1!, fromId: _2, date: _3!, gift: _4!, message: _5, msgId: _6, savedId: _7, convertStars: _8, upgradeStars: _9, canExportAt: _10, transferStars: _11, canTransferAt: _12, canResellAt: _13, collectionId: _14, prepaidUpgradeHash: _15, dropOriginalDetailsStars: _16, giftNum: _17) } } @@ -778,12 +734,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.SearchPostsFlood.searchPostsFlood(flags: _1!, totalDaily: _2!, remains: _3!, waitTill: _4, starsAmount: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.SearchPostsFlood.searchPostsFlood(flags: _1!, totalDaily: _2!, remains: _3!, waitTill: _4, starsAmount: _5!) } } @@ -826,12 +782,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.SearchResultsCalendarPeriod.searchResultsCalendarPeriod(date: _1!, minMsgId: _2!, maxMsgId: _3!, count: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.SearchResultsCalendarPeriod.searchResultsCalendarPeriod(date: _1!, minMsgId: _2!, maxMsgId: _3!, count: _4!) } } @@ -870,12 +825,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.SearchResultsPosition.searchResultPosition(msgId: _1!, date: _2!, offset: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.SearchResultsPosition.searchResultPosition(msgId: _1!, date: _2!, offset: _3!) } } @@ -914,12 +867,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.SecureCredentialsEncrypted.secureCredentialsEncrypted(data: _1!, hash: _2!, secret: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.SecureCredentialsEncrypted.secureCredentialsEncrypted(data: _1!, hash: _2!, secret: _3!) } } @@ -958,12 +909,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.SecureData.secureData(data: _1!, dataHash: _2!, secret: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.SecureData.secureData(data: _1!, dataHash: _2!, secret: _3!) } } @@ -1027,12 +976,14 @@ public extension Api { let _c5 = _5 != nil let _c6 = _6 != nil let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.SecureFile.secureFile(id: _1!, accessHash: _2!, size: _3!, dcId: _4!, date: _5!, fileHash: _6!, secret: _7!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.SecureFile.secureFile(id: _1!, accessHash: _2!, size: _3!, dcId: _4!, date: _5!, fileHash: _6!, secret: _7!) } public static func parse_secureFileEmpty(_ reader: BufferReader) -> SecureFile? { return Api.SecureFile.secureFileEmpty @@ -1084,23 +1035,15 @@ public extension Api { var _1: Buffer? _1 = parseBytes(reader) let _c1 = _1 != nil - if _c1 { - return Api.SecurePasswordKdfAlgo.securePasswordKdfAlgoPBKDF2HMACSHA512iter100000(salt: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.SecurePasswordKdfAlgo.securePasswordKdfAlgoPBKDF2HMACSHA512iter100000(salt: _1!) } public static func parse_securePasswordKdfAlgoSHA512(_ reader: BufferReader) -> SecurePasswordKdfAlgo? { var _1: Buffer? _1 = parseBytes(reader) let _c1 = _1 != nil - if _c1 { - return Api.SecurePasswordKdfAlgo.securePasswordKdfAlgoSHA512(salt: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.SecurePasswordKdfAlgo.securePasswordKdfAlgoSHA512(salt: _1!) } public static func parse_securePasswordKdfAlgoUnknown(_ reader: BufferReader) -> SecurePasswordKdfAlgo? { return Api.SecurePasswordKdfAlgo.securePasswordKdfAlgoUnknown diff --git a/submodules/TelegramApi/Sources/Api24.swift b/submodules/TelegramApi/Sources/Api24.swift index 22a2ae99..77fe9cc5 100644 --- a/submodules/TelegramApi/Sources/Api24.swift +++ b/submodules/TelegramApi/Sources/Api24.swift @@ -33,23 +33,15 @@ public extension Api { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.SecurePlainData.securePlainEmail(email: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.SecurePlainData.securePlainEmail(email: _1!) } public static func parse_securePlainPhone(_ reader: BufferReader) -> SecurePlainData? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.SecurePlainData.securePlainPhone(phone: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.SecurePlainData.securePlainPhone(phone: _1!) } } @@ -99,12 +91,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.SecureRequiredType.secureRequiredType(flags: _1!, type: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.SecureRequiredType.secureRequiredType(flags: _1!, type: _2!) } public static func parse_secureRequiredTypeOneOf(_ reader: BufferReader) -> SecureRequiredType? { var _1: [Api.SecureRequiredType]? @@ -112,12 +101,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SecureRequiredType.self) } let _c1 = _1 != nil - if _c1 { - return Api.SecureRequiredType.secureRequiredTypeOneOf(types: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.SecureRequiredType.secureRequiredTypeOneOf(types: _1!) } } @@ -158,12 +143,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.SecureSecretSettings.secureSecretSettings(secureAlgo: _1!, secureSecret: _2!, secureSecretId: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.SecureSecretSettings.secureSecretSettings(secureAlgo: _1!, secureSecret: _2!, secureSecretId: _3!) } } @@ -254,12 +237,17 @@ public extension Api { let _c8 = (Int(_1!) & Int(1 << 4) == 0) || _8 != nil let _c9 = (Int(_1!) & Int(1 << 5) == 0) || _9 != nil let _c10 = _10 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 { - return Api.SecureValue.secureValue(flags: _1!, type: _2!, data: _3, frontSide: _4, reverseSide: _5, selfie: _6, translation: _7, files: _8, plainData: _9, hash: _10!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + return Api.SecureValue.secureValue(flags: _1!, type: _2!, data: _3, frontSide: _4, reverseSide: _5, selfie: _6, translation: _7, files: _8, plainData: _9, hash: _10!) } } @@ -397,12 +385,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.SecureValueError.secureValueError(type: _1!, hash: _2!, text: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.SecureValueError.secureValueError(type: _1!, hash: _2!, text: _3!) } public static func parse_secureValueErrorData(_ reader: BufferReader) -> SecureValueError? { var _1: Api.SecureValueType? @@ -419,12 +405,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.SecureValueError.secureValueErrorData(type: _1!, dataHash: _2!, field: _3!, text: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.SecureValueError.secureValueErrorData(type: _1!, dataHash: _2!, field: _3!, text: _4!) } public static func parse_secureValueErrorFile(_ reader: BufferReader) -> SecureValueError? { var _1: Api.SecureValueType? @@ -438,12 +423,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.SecureValueError.secureValueErrorFile(type: _1!, fileHash: _2!, text: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.SecureValueError.secureValueErrorFile(type: _1!, fileHash: _2!, text: _3!) } public static func parse_secureValueErrorFiles(_ reader: BufferReader) -> SecureValueError? { var _1: Api.SecureValueType? @@ -459,12 +442,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.SecureValueError.secureValueErrorFiles(type: _1!, fileHash: _2!, text: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.SecureValueError.secureValueErrorFiles(type: _1!, fileHash: _2!, text: _3!) } public static func parse_secureValueErrorFrontSide(_ reader: BufferReader) -> SecureValueError? { var _1: Api.SecureValueType? @@ -478,12 +459,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.SecureValueError.secureValueErrorFrontSide(type: _1!, fileHash: _2!, text: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.SecureValueError.secureValueErrorFrontSide(type: _1!, fileHash: _2!, text: _3!) } public static func parse_secureValueErrorReverseSide(_ reader: BufferReader) -> SecureValueError? { var _1: Api.SecureValueType? @@ -497,12 +476,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.SecureValueError.secureValueErrorReverseSide(type: _1!, fileHash: _2!, text: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.SecureValueError.secureValueErrorReverseSide(type: _1!, fileHash: _2!, text: _3!) } public static func parse_secureValueErrorSelfie(_ reader: BufferReader) -> SecureValueError? { var _1: Api.SecureValueType? @@ -516,12 +493,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.SecureValueError.secureValueErrorSelfie(type: _1!, fileHash: _2!, text: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.SecureValueError.secureValueErrorSelfie(type: _1!, fileHash: _2!, text: _3!) } public static func parse_secureValueErrorTranslationFile(_ reader: BufferReader) -> SecureValueError? { var _1: Api.SecureValueType? @@ -535,12 +510,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.SecureValueError.secureValueErrorTranslationFile(type: _1!, fileHash: _2!, text: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.SecureValueError.secureValueErrorTranslationFile(type: _1!, fileHash: _2!, text: _3!) } public static func parse_secureValueErrorTranslationFiles(_ reader: BufferReader) -> SecureValueError? { var _1: Api.SecureValueType? @@ -556,12 +529,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.SecureValueError.secureValueErrorTranslationFiles(type: _1!, fileHash: _2!, text: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.SecureValueError.secureValueErrorTranslationFiles(type: _1!, fileHash: _2!, text: _3!) } } @@ -598,12 +569,9 @@ public extension Api { _2 = parseBytes(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.SecureValueHash.secureValueHash(type: _1!, hash: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.SecureValueHash.secureValueHash(type: _1!, hash: _2!) } } @@ -812,12 +780,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.SendAsPeer.sendAsPeer(flags: _1!, peer: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.SendAsPeer.sendAsPeer(flags: _1!, peer: _2!) } } @@ -1030,23 +995,17 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.SendMessageAction.sendMessageEmojiInteraction(emoticon: _1!, msgId: _2!, interaction: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.SendMessageAction.sendMessageEmojiInteraction(emoticon: _1!, msgId: _2!, interaction: _3!) } public static func parse_sendMessageEmojiInteractionSeen(_ reader: BufferReader) -> SendMessageAction? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.SendMessageAction.sendMessageEmojiInteractionSeen(emoticon: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.SendMessageAction.sendMessageEmojiInteractionSeen(emoticon: _1!) } public static func parse_sendMessageGamePlayAction(_ reader: BufferReader) -> SendMessageAction? { return Api.SendMessageAction.sendMessageGamePlayAction @@ -1058,12 +1017,8 @@ public extension Api { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.SendMessageAction.sendMessageHistoryImportAction(progress: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.SendMessageAction.sendMessageHistoryImportAction(progress: _1!) } public static func parse_sendMessageRecordAudioAction(_ reader: BufferReader) -> SendMessageAction? { return Api.SendMessageAction.sendMessageRecordAudioAction @@ -1083,12 +1038,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.SendMessageAction.sendMessageTextDraftAction(randomId: _1!, text: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.SendMessageAction.sendMessageTextDraftAction(randomId: _1!, text: _2!) } public static func parse_sendMessageTypingAction(_ reader: BufferReader) -> SendMessageAction? { return Api.SendMessageAction.sendMessageTypingAction @@ -1097,56 +1049,36 @@ public extension Api { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.SendMessageAction.sendMessageUploadAudioAction(progress: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.SendMessageAction.sendMessageUploadAudioAction(progress: _1!) } public static func parse_sendMessageUploadDocumentAction(_ reader: BufferReader) -> SendMessageAction? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.SendMessageAction.sendMessageUploadDocumentAction(progress: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.SendMessageAction.sendMessageUploadDocumentAction(progress: _1!) } public static func parse_sendMessageUploadPhotoAction(_ reader: BufferReader) -> SendMessageAction? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.SendMessageAction.sendMessageUploadPhotoAction(progress: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.SendMessageAction.sendMessageUploadPhotoAction(progress: _1!) } public static func parse_sendMessageUploadRoundAction(_ reader: BufferReader) -> SendMessageAction? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.SendMessageAction.sendMessageUploadRoundAction(progress: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.SendMessageAction.sendMessageUploadRoundAction(progress: _1!) } public static func parse_sendMessageUploadVideoAction(_ reader: BufferReader) -> SendMessageAction? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.SendMessageAction.sendMessageUploadVideoAction(progress: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.SendMessageAction.sendMessageUploadVideoAction(progress: _1!) } public static func parse_speakingInGroupCallAction(_ reader: BufferReader) -> SendMessageAction? { return Api.SendMessageAction.speakingInGroupCallAction diff --git a/submodules/TelegramApi/Sources/Api25.swift b/submodules/TelegramApi/Sources/Api25.swift index 5abc7a1d..08d8aafe 100644 --- a/submodules/TelegramApi/Sources/Api25.swift +++ b/submodules/TelegramApi/Sources/Api25.swift @@ -38,12 +38,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.ShippingOption.shippingOption(id: _1!, title: _2!, prices: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.ShippingOption.shippingOption(id: _1!, title: _2!, prices: _3!) } } @@ -82,12 +80,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.SmsJob.smsJob(jobId: _1!, phoneNumber: _2!, text: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.SmsJob.smsJob(jobId: _1!, phoneNumber: _2!, text: _3!) } } @@ -182,12 +178,21 @@ public extension Api { let _c12 = (Int(_1!) & Int(1 << 8) == 0) || _12 != nil let _c13 = (Int(_1!) & Int(1 << 15) == 0) || _13 != nil let _c14 = (Int(_1!) & Int(1 << 15) == 0) || _14 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 { - return Api.SponsoredMessage.sponsoredMessage(flags: _1!, randomId: _2!, url: _3!, title: _4!, message: _5!, entities: _6, photo: _7, media: _8, color: _9, buttonText: _10!, sponsorInfo: _11, additionalInfo: _12, minDisplayDuration: _13, maxDisplayDuration: _14) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + if !_c13 { return nil } + if !_c14 { return nil } + return Api.SponsoredMessage.sponsoredMessage(flags: _1!, randomId: _2!, url: _3!, title: _4!, message: _5!, entities: _6, photo: _7, media: _8, color: _9, buttonText: _10!, sponsorInfo: _11, additionalInfo: _12, minDisplayDuration: _13, maxDisplayDuration: _14) } } @@ -222,12 +227,9 @@ public extension Api { _2 = parseBytes(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.SponsoredMessageReportOption.sponsoredMessageReportOption(text: _1!, option: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.SponsoredMessageReportOption.sponsoredMessageReportOption(text: _1!, option: _2!) } } @@ -276,12 +278,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.SponsoredPeer.sponsoredPeer(flags: _1!, randomId: _2!, peer: _3!, sponsorInfo: _4, additionalInfo: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.SponsoredPeer.sponsoredPeer(flags: _1!, randomId: _2!, peer: _3!, sponsorInfo: _4, additionalInfo: _5) } } @@ -440,12 +442,29 @@ public extension Api { let _c20 = (Int(_1!) & Int(1 << 11) == 0) || _20 != nil let _c21 = (Int(_1!) & Int(1 << 12) == 0) || _21 != nil let _c22 = (Int(_1!) & Int(1 << 13) == 0) || _22 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 { - return Api.StarGift.starGift(flags: _1!, id: _2!, sticker: _3!, stars: _4!, availabilityRemains: _5, availabilityTotal: _6, availabilityResale: _7, convertStars: _8!, firstSaleDate: _9, lastSaleDate: _10, upgradeStars: _11, resellMinStars: _12, title: _13, releasedBy: _14, perUserTotal: _15, perUserRemains: _16, lockedUntilDate: _17, auctionSlug: _18, giftsPerRound: _19, auctionStartDate: _20, upgradeVariants: _21, background: _22) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + if !_c13 { return nil } + if !_c14 { return nil } + if !_c15 { return nil } + if !_c16 { return nil } + if !_c17 { return nil } + if !_c18 { return nil } + if !_c19 { return nil } + if !_c20 { return nil } + if !_c21 { return nil } + if !_c22 { return nil } + return Api.StarGift.starGift(flags: _1!, id: _2!, sticker: _3!, stars: _4!, availabilityRemains: _5, availabilityTotal: _6, availabilityResale: _7, convertStars: _8!, firstSaleDate: _9, lastSaleDate: _10, upgradeStars: _11, resellMinStars: _12, title: _13, releasedBy: _14, perUserTotal: _15, perUserRemains: _16, lockedUntilDate: _17, auctionSlug: _18, giftsPerRound: _19, auctionStartDate: _20, upgradeVariants: _21, background: _22) } public static func parse_starGiftUnique(_ reader: BufferReader) -> StarGift? { var _1: Int32? @@ -528,12 +547,29 @@ public extension Api { let _c20 = (Int(_1!) & Int(1 << 11) == 0) || _20 != nil let _c21 = (Int(_1!) & Int(1 << 12) == 0) || _21 != nil let _c22 = (Int(_1!) & Int(1 << 13) == 0) || _22 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 { - return Api.StarGift.starGiftUnique(flags: _1!, id: _2!, giftId: _3!, title: _4!, slug: _5!, num: _6!, ownerId: _7, ownerName: _8, ownerAddress: _9, attributes: _10!, availabilityIssued: _11!, availabilityTotal: _12!, giftAddress: _13, resellAmount: _14, releasedBy: _15, valueAmount: _16, valueCurrency: _17, valueUsdAmount: _18, themePeer: _19, peerColor: _20, hostId: _21, offerMinStars: _22) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + if !_c13 { return nil } + if !_c14 { return nil } + if !_c15 { return nil } + if !_c16 { return nil } + if !_c17 { return nil } + if !_c18 { return nil } + if !_c19 { return nil } + if !_c20 { return nil } + if !_c21 { return nil } + if !_c22 { return nil } + return Api.StarGift.starGiftUnique(flags: _1!, id: _2!, giftId: _3!, title: _4!, slug: _5!, num: _6!, ownerId: _7, ownerName: _8, ownerAddress: _9, attributes: _10!, availabilityIssued: _11!, availabilityTotal: _12!, giftAddress: _13, resellAmount: _14, releasedBy: _15, valueAmount: _16, valueCurrency: _17, valueUsdAmount: _18, themePeer: _19, peerColor: _20, hostId: _21, offerMinStars: _22) } } @@ -578,12 +614,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.StarGiftActiveAuctionState.starGiftActiveAuctionState(gift: _1!, state: _2!, userState: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.StarGiftActiveAuctionState.starGiftActiveAuctionState(gift: _1!, state: _2!, userState: _3!) } } @@ -673,12 +707,14 @@ public extension Api { let _c5 = _5 != nil let _c6 = _6 != nil let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.StarGiftAttribute.starGiftAttributeBackdrop(name: _1!, backdropId: _2!, centerColor: _3!, edgeColor: _4!, patternColor: _5!, textColor: _6!, rarityPermille: _7!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.StarGiftAttribute.starGiftAttributeBackdrop(name: _1!, backdropId: _2!, centerColor: _3!, edgeColor: _4!, patternColor: _5!, textColor: _6!, rarityPermille: _7!) } public static func parse_starGiftAttributeModel(_ reader: BufferReader) -> StarGiftAttribute? { var _1: String? @@ -692,12 +728,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.StarGiftAttribute.starGiftAttributeModel(name: _1!, document: _2!, rarityPermille: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.StarGiftAttribute.starGiftAttributeModel(name: _1!, document: _2!, rarityPermille: _3!) } public static func parse_starGiftAttributeOriginalDetails(_ reader: BufferReader) -> StarGiftAttribute? { var _1: Int32? @@ -721,12 +755,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.StarGiftAttribute.starGiftAttributeOriginalDetails(flags: _1!, senderId: _2, recipientId: _3!, date: _4!, message: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.StarGiftAttribute.starGiftAttributeOriginalDetails(flags: _1!, senderId: _2, recipientId: _3!, date: _4!, message: _5) } public static func parse_starGiftAttributePattern(_ reader: BufferReader) -> StarGiftAttribute? { var _1: String? @@ -740,12 +774,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.StarGiftAttribute.starGiftAttributePattern(name: _1!, document: _2!, rarityPermille: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.StarGiftAttribute.starGiftAttributePattern(name: _1!, document: _2!, rarityPermille: _3!) } } @@ -782,12 +814,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StarGiftAttributeCounter.starGiftAttributeCounter(attribute: _1!, count: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.StarGiftAttributeCounter.starGiftAttributeCounter(attribute: _1!, count: _2!) } } @@ -836,34 +865,22 @@ public extension Api { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.StarGiftAttributeId.starGiftAttributeIdBackdrop(backdropId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.StarGiftAttributeId.starGiftAttributeIdBackdrop(backdropId: _1!) } public static func parse_starGiftAttributeIdModel(_ reader: BufferReader) -> StarGiftAttributeId? { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.StarGiftAttributeId.starGiftAttributeIdModel(documentId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.StarGiftAttributeId.starGiftAttributeIdModel(documentId: _1!) } public static func parse_starGiftAttributeIdPattern(_ reader: BufferReader) -> StarGiftAttributeId? { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.StarGiftAttributeId.starGiftAttributeIdPattern(documentId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.StarGiftAttributeId.starGiftAttributeIdPattern(documentId: _1!) } } @@ -926,12 +943,15 @@ public extension Api { let _c6 = _6 != nil let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil let _c8 = (Int(_1!) & Int(1 << 2) == 0) || _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.StarGiftAuctionAcquiredGift.starGiftAuctionAcquiredGift(flags: _1!, peer: _2!, date: _3!, bidAmount: _4!, round: _5!, pos: _6!, message: _7, giftNum: _8) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.StarGiftAuctionAcquiredGift.starGiftAuctionAcquiredGift(flags: _1!, peer: _2!, date: _3!, bidAmount: _4!, round: _5!, pos: _6!, message: _7, giftNum: _8) } } @@ -978,12 +998,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StarGiftAuctionRound.starGiftAuctionRound(num: _1!, duration: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.StarGiftAuctionRound.starGiftAuctionRound(num: _1!, duration: _2!) } public static func parse_starGiftAuctionRoundExtendable(_ reader: BufferReader) -> StarGiftAuctionRound? { var _1: Int32? @@ -998,12 +1015,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.StarGiftAuctionRound.starGiftAuctionRoundExtendable(num: _1!, duration: _2!, extendTop: _3!, extendWindow: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.StarGiftAuctionRound.starGiftAuctionRoundExtendable(num: _1!, duration: _2!, extendTop: _3!, extendWindow: _4!) } } @@ -1120,12 +1136,19 @@ public extension Api { let _c10 = _10 != nil let _c11 = _11 != nil let _c12 = _12 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 { - return Api.StarGiftAuctionState.starGiftAuctionState(version: _1!, startDate: _2!, endDate: _3!, minBidAmount: _4!, bidLevels: _5!, topBidders: _6!, nextRoundAt: _7!, lastGiftNum: _8!, giftsLeft: _9!, currentRound: _10!, totalRounds: _11!, rounds: _12!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + return Api.StarGiftAuctionState.starGiftAuctionState(version: _1!, startDate: _2!, endDate: _3!, minBidAmount: _4!, bidLevels: _5!, topBidders: _6!, nextRoundAt: _7!, lastGiftNum: _8!, giftsLeft: _9!, currentRound: _10!, totalRounds: _11!, rounds: _12!) } public static func parse_starGiftAuctionStateFinished(_ reader: BufferReader) -> StarGiftAuctionState? { var _1: Int32? @@ -1149,12 +1172,14 @@ public extension Api { let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.StarGiftAuctionState.starGiftAuctionStateFinished(flags: _1!, startDate: _2!, endDate: _3!, averagePrice: _4!, listedCount: _5, fragmentListedCount: _6, fragmentListedUrl: _7) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.StarGiftAuctionState.starGiftAuctionStateFinished(flags: _1!, startDate: _2!, endDate: _3!, averagePrice: _4!, listedCount: _5, fragmentListedCount: _6, fragmentListedUrl: _7) } public static func parse_starGiftAuctionStateNotModified(_ reader: BufferReader) -> StarGiftAuctionState? { return Api.StarGiftAuctionState.starGiftAuctionStateNotModified @@ -1210,12 +1235,13 @@ public extension Api { let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.StarGiftAuctionUserState.starGiftAuctionUserState(flags: _1!, bidAmount: _2, bidDate: _3, minBidAmount: _4, bidPeer: _5, acquiredCount: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.StarGiftAuctionUserState.starGiftAuctionUserState(flags: _1!, bidAmount: _2, bidDate: _3, minBidAmount: _4, bidPeer: _5, acquiredCount: _6!) } } @@ -1254,12 +1280,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.StarGiftBackground.starGiftBackground(centerColor: _1!, edgeColor: _2!, textColor: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.StarGiftBackground.starGiftBackground(centerColor: _1!, edgeColor: _2!, textColor: _3!) } } @@ -1312,12 +1336,13 @@ public extension Api { let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil let _c5 = _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.StarGiftCollection.starGiftCollection(flags: _1!, collectionId: _2!, title: _3!, icon: _4, giftsCount: _5!, hash: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.StarGiftCollection.starGiftCollection(flags: _1!, collectionId: _2!, title: _3!, icon: _4, giftsCount: _5!, hash: _6!) } } @@ -1352,12 +1377,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StarGiftUpgradePrice.starGiftUpgradePrice(date: _1!, upgradeStars: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.StarGiftUpgradePrice.starGiftUpgradePrice(date: _1!, upgradeStars: _2!) } } @@ -1410,12 +1432,13 @@ public extension Api { let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.StarRefProgram.starRefProgram(flags: _1!, botId: _2!, commissionPermille: _3!, durationMonths: _4, endDate: _5, dailyRevenuePerUser: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.StarRefProgram.starRefProgram(flags: _1!, botId: _2!, commissionPermille: _3!, durationMonths: _4, endDate: _5, dailyRevenuePerUser: _6) } } @@ -1459,23 +1482,16 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StarsAmount.starsAmount(amount: _1!, nanos: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.StarsAmount.starsAmount(amount: _1!, nanos: _2!) } public static func parse_starsTonAmount(_ reader: BufferReader) -> StarsAmount? { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.StarsAmount.starsTonAmount(amount: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.StarsAmount.starsTonAmount(amount: _1!) } } @@ -1522,12 +1538,12 @@ public extension Api { let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.StarsGiftOption.starsGiftOption(flags: _1!, stars: _2!, storeProduct: _3, currency: _4!, amount: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.StarsGiftOption.starsGiftOption(flags: _1!, stars: _2!, storeProduct: _3, currency: _4!, amount: _5!) } } @@ -1588,12 +1604,14 @@ public extension Api { let _c5 = _5 != nil let _c6 = _6 != nil let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.StarsGiveawayOption.starsGiveawayOption(flags: _1!, stars: _2!, yearlyBoosts: _3!, storeProduct: _4, currency: _5!, amount: _6!, winners: _7!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.StarsGiveawayOption.starsGiveawayOption(flags: _1!, stars: _2!, yearlyBoosts: _3!, storeProduct: _4, currency: _5!, amount: _6!, winners: _7!) } } @@ -1632,12 +1650,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.StarsGiveawayWinnersOption.starsGiveawayWinnersOption(flags: _1!, users: _2!, perUserStars: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.StarsGiveawayWinnersOption.starsGiveawayWinnersOption(flags: _1!, users: _2!, perUserStars: _3!) } } diff --git a/submodules/TelegramApi/Sources/Api26.swift b/submodules/TelegramApi/Sources/Api26.swift index fd08d27e..4a5c08c4 100644 --- a/submodules/TelegramApi/Sources/Api26.swift +++ b/submodules/TelegramApi/Sources/Api26.swift @@ -40,12 +40,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.StarsRating.starsRating(flags: _1!, level: _2!, currentLevelStars: _3!, stars: _4!, nextLevelStars: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.StarsRating.starsRating(flags: _1!, level: _2!, currentLevelStars: _3!, stars: _4!, nextLevelStars: _5) } } @@ -98,12 +98,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.StarsRevenueStatus.starsRevenueStatus(flags: _1!, currentBalance: _2!, availableBalance: _3!, overallRevenue: _4!, nextWithdrawalAt: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.StarsRevenueStatus.starsRevenueStatus(flags: _1!, currentBalance: _2!, availableBalance: _3!, overallRevenue: _4!, nextWithdrawalAt: _5) } } @@ -172,12 +172,16 @@ public extension Api { let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil let _c8 = (Int(_1!) & Int(1 << 5) == 0) || _8 != nil let _c9 = (Int(_1!) & Int(1 << 6) == 0) || _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.StarsSubscription.starsSubscription(flags: _1!, id: _2!, peer: _3!, untilDate: _4!, pricing: _5!, chatInviteHash: _6, title: _7, photo: _8, invoiceSlug: _9) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + return Api.StarsSubscription.starsSubscription(flags: _1!, id: _2!, peer: _3!, untilDate: _4!, pricing: _5!, chatInviteHash: _6, title: _7, photo: _8, invoiceSlug: _9) } } @@ -212,12 +216,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StarsSubscriptionPricing.starsSubscriptionPricing(period: _1!, amount: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.StarsSubscriptionPricing.starsSubscriptionPricing(period: _1!, amount: _2!) } } @@ -264,12 +265,12 @@ public extension Api { let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.StarsTopupOption.starsTopupOption(flags: _1!, stars: _2!, storeProduct: _3, currency: _4!, amount: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.StarsTopupOption.starsTopupOption(flags: _1!, stars: _2!, storeProduct: _3, currency: _4!, amount: _5!) } } @@ -410,12 +411,31 @@ public extension Api { let _c22 = (Int(_1!) & Int(1 << 20) == 0) || _22 != nil let _c23 = (Int(_1!) & Int(1 << 23) == 0) || _23 != nil let _c24 = (Int(_1!) & Int(1 << 23) == 0) || _24 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 { - return Api.StarsTransaction.starsTransaction(flags: _1!, id: _2!, amount: _3!, date: _4!, peer: _5!, title: _6, description: _7, photo: _8, transactionDate: _9, transactionUrl: _10, botPayload: _11, msgId: _12, extendedMedia: _13, subscriptionPeriod: _14, giveawayPostId: _15, stargift: _16, floodskipNumber: _17, starrefCommissionPermille: _18, starrefPeer: _19, starrefAmount: _20, paidMessages: _21, premiumGiftMonths: _22, adsProceedsFromDate: _23, adsProceedsToDate: _24) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + if !_c13 { return nil } + if !_c14 { return nil } + if !_c15 { return nil } + if !_c16 { return nil } + if !_c17 { return nil } + if !_c18 { return nil } + if !_c19 { return nil } + if !_c20 { return nil } + if !_c21 { return nil } + if !_c22 { return nil } + if !_c23 { return nil } + if !_c24 { return nil } + return Api.StarsTransaction.starsTransaction(flags: _1!, id: _2!, amount: _3!, date: _4!, peer: _5!, title: _6, description: _7, photo: _8, transactionDate: _9, transactionUrl: _10, botPayload: _11, msgId: _12, extendedMedia: _13, subscriptionPeriod: _14, giveawayPostId: _15, stargift: _16, floodskipNumber: _17, starrefCommissionPermille: _18, starrefPeer: _19, starrefAmount: _20, paidMessages: _21, premiumGiftMonths: _22, adsProceedsFromDate: _23, adsProceedsToDate: _24) } } @@ -511,12 +531,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Peer } let _c1 = _1 != nil - if _c1 { - return Api.StarsTransactionPeer.starsTransactionPeer(peer: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.StarsTransactionPeer.starsTransactionPeer(peer: _1!) } public static func parse_starsTransactionPeerAPI(_ reader: BufferReader) -> StarsTransactionPeer? { return Api.StarsTransactionPeer.starsTransactionPeerAPI @@ -572,12 +588,9 @@ public extension Api { _2 = reader.readDouble() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StatsAbsValueAndPrev.statsAbsValueAndPrev(current: _1!, previous: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.StatsAbsValueAndPrev.statsAbsValueAndPrev(current: _1!, previous: _2!) } } @@ -612,12 +625,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StatsDateRangeDays.statsDateRangeDays(minDate: _1!, maxDate: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.StatsDateRangeDays.statsDateRangeDays(minDate: _1!, maxDate: _2!) } } @@ -676,34 +686,24 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.StatsGraph.statsGraph(flags: _1!, json: _2!, zoomToken: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.StatsGraph.statsGraph(flags: _1!, json: _2!, zoomToken: _3) } public static func parse_statsGraphAsync(_ reader: BufferReader) -> StatsGraph? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.StatsGraph.statsGraphAsync(token: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.StatsGraph.statsGraphAsync(token: _1!) } public static func parse_statsGraphError(_ reader: BufferReader) -> StatsGraph? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.StatsGraph.statsGraphError(error: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.StatsGraph.statsGraphError(error: _1!) } } @@ -746,12 +746,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.StatsGroupTopAdmin.statsGroupTopAdmin(userId: _1!, deleted: _2!, kicked: _3!, banned: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.StatsGroupTopAdmin.statsGroupTopAdmin(userId: _1!, deleted: _2!, kicked: _3!, banned: _4!) } } @@ -786,12 +785,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StatsGroupTopInviter.statsGroupTopInviter(userId: _1!, invitations: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.StatsGroupTopInviter.statsGroupTopInviter(userId: _1!, invitations: _2!) } } @@ -830,12 +826,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.StatsGroupTopPoster.statsGroupTopPoster(userId: _1!, messages: _2!, avgChars: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.StatsGroupTopPoster.statsGroupTopPoster(userId: _1!, messages: _2!, avgChars: _3!) } } @@ -870,12 +864,9 @@ public extension Api { _2 = reader.readDouble() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StatsPercentValue.statsPercentValue(part: _1!, total: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.StatsPercentValue.statsPercentValue(part: _1!, total: _2!) } } @@ -906,12 +897,8 @@ public extension Api { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.StatsURL.statsURL(url: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.StatsURL.statsURL(url: _1!) } } @@ -952,12 +939,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StickerKeyword.stickerKeyword(documentId: _1!, keyword: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.StickerKeyword.stickerKeyword(documentId: _1!, keyword: _2!) } } @@ -998,12 +982,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StickerPack.stickerPack(emoticon: _1!, documents: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.StickerPack.stickerPack(emoticon: _1!, documents: _2!) } } @@ -1084,12 +1065,19 @@ public extension Api { let _c10 = (Int(_1!) & Int(1 << 8) == 0) || _10 != nil let _c11 = _11 != nil let _c12 = _12 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 { - return Api.StickerSet.stickerSet(flags: _1!, installedDate: _2, id: _3!, accessHash: _4!, title: _5!, shortName: _6!, thumbs: _7, thumbDcId: _8, thumbVersion: _9, thumbDocumentId: _10, count: _11!, hash: _12!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + return Api.StickerSet.stickerSet(flags: _1!, installedDate: _2, id: _3!, accessHash: _4!, title: _5!, shortName: _6!, thumbs: _7, thumbDcId: _8, thumbVersion: _9, thumbDocumentId: _10, count: _11!, hash: _12!) } } @@ -1175,12 +1163,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StickerSetCovered.stickerSetCovered(set: _1!, cover: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.StickerSetCovered.stickerSetCovered(set: _1!, cover: _2!) } public static func parse_stickerSetFullCovered(_ reader: BufferReader) -> StickerSetCovered? { var _1: Api.StickerSet? @@ -1203,12 +1188,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.StickerSetCovered.stickerSetFullCovered(set: _1!, packs: _2!, keywords: _3!, documents: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.StickerSetCovered.stickerSetFullCovered(set: _1!, packs: _2!, keywords: _3!, documents: _4!) } public static func parse_stickerSetMultiCovered(_ reader: BufferReader) -> StickerSetCovered? { var _1: Api.StickerSet? @@ -1221,12 +1205,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StickerSetCovered.stickerSetMultiCovered(set: _1!, covers: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.StickerSetCovered.stickerSetMultiCovered(set: _1!, covers: _2!) } public static func parse_stickerSetNoCovered(_ reader: BufferReader) -> StickerSetCovered? { var _1: Api.StickerSet? @@ -1234,12 +1215,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.StickerSet } let _c1 = _1 != nil - if _c1 { - return Api.StickerSetCovered.stickerSetNoCovered(set: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.StickerSetCovered.stickerSetNoCovered(set: _1!) } } @@ -1278,12 +1255,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.StoriesStealthMode.storiesStealthMode(flags: _1!, activeUntilDate: _2, cooldownUntilDate: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.StoriesStealthMode.storiesStealthMode(flags: _1!, activeUntilDate: _2, cooldownUntilDate: _3) } } diff --git a/submodules/TelegramApi/Sources/Api27.swift b/submodules/TelegramApi/Sources/Api27.swift index 075ddc82..12cbc688 100644 --- a/submodules/TelegramApi/Sources/Api27.swift +++ b/submodules/TelegramApi/Sources/Api27.swift @@ -44,12 +44,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.StoryAlbum.storyAlbum(flags: _1!, albumId: _2!, title: _3!, iconPhoto: _4, iconVideo: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.StoryAlbum.storyAlbum(flags: _1!, albumId: _2!, title: _3!, iconPhoto: _4, iconVideo: _5) } } @@ -94,12 +94,11 @@ public extension Api { let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.StoryFwdHeader.storyFwdHeader(flags: _1!, from: _2, fromName: _3, storyId: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.StoryFwdHeader.storyFwdHeader(flags: _1!, from: _2, fromName: _3, storyId: _4) } } @@ -237,23 +236,28 @@ public extension Api { let _c12 = (Int(_1!) & Int(1 << 3) == 0) || _12 != nil let _c13 = (Int(_1!) & Int(1 << 15) == 0) || _13 != nil let _c14 = (Int(_1!) & Int(1 << 19) == 0) || _14 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 { - return Api.StoryItem.storyItem(flags: _1!, id: _2!, date: _3!, fromId: _4, fwdFrom: _5, expireDate: _6!, caption: _7, entities: _8, media: _9!, mediaAreas: _10, privacy: _11, views: _12, sentReaction: _13, albums: _14) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + if !_c13 { return nil } + if !_c14 { return nil } + return Api.StoryItem.storyItem(flags: _1!, id: _2!, date: _3!, fromId: _4, fwdFrom: _5, expireDate: _6!, caption: _7, entities: _8, media: _9!, mediaAreas: _10, privacy: _11, views: _12, sentReaction: _13, albums: _14) } public static func parse_storyItemDeleted(_ reader: BufferReader) -> StoryItem? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.StoryItem.storyItemDeleted(id: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.StoryItem.storyItemDeleted(id: _1!) } public static func parse_storyItemSkipped(_ reader: BufferReader) -> StoryItem? { var _1: Int32? @@ -268,12 +272,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.StoryItem.storyItemSkipped(flags: _1!, id: _2!, date: _3!, expireDate: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.StoryItem.storyItemSkipped(flags: _1!, id: _2!, date: _3!, expireDate: _4!) } } @@ -335,12 +338,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.StoryReaction.storyReaction(peerId: _1!, date: _2!, reaction: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.StoryReaction.storyReaction(peerId: _1!, date: _2!, reaction: _3!) } public static func parse_storyReactionPublicForward(_ reader: BufferReader) -> StoryReaction? { var _1: Api.Message? @@ -348,12 +349,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Message } let _c1 = _1 != nil - if _c1 { - return Api.StoryReaction.storyReactionPublicForward(message: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.StoryReaction.storyReactionPublicForward(message: _1!) } public static func parse_storyReactionPublicRepost(_ reader: BufferReader) -> StoryReaction? { var _1: Api.Peer? @@ -366,12 +363,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StoryReaction.storyReactionPublicRepost(peerId: _1!, story: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.StoryReaction.storyReactionPublicRepost(peerId: _1!, story: _2!) } } @@ -437,12 +431,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.StoryView.storyView(flags: _1!, userId: _2!, date: _3!, reaction: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.StoryView.storyView(flags: _1!, userId: _2!, date: _3!, reaction: _4) } public static func parse_storyViewPublicForward(_ reader: BufferReader) -> StoryView? { var _1: Int32? @@ -453,12 +446,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StoryView.storyViewPublicForward(flags: _1!, message: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.StoryView.storyViewPublicForward(flags: _1!, message: _2!) } public static func parse_storyViewPublicRepost(_ reader: BufferReader) -> StoryView? { var _1: Int32? @@ -474,12 +464,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.StoryView.storyViewPublicRepost(flags: _1!, peerId: _2!, story: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.StoryView.storyViewPublicRepost(flags: _1!, peerId: _2!, story: _3!) } } @@ -542,12 +530,13 @@ public extension Api { let _c4 = (Int(_1!) & Int(1 << 3) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 4) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.StoryViews.storyViews(flags: _1!, viewsCount: _2!, forwardsCount: _3, reactions: _4, reactionsCount: _5, recentViewers: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.StoryViews.storyViews(flags: _1!, viewsCount: _2!, forwardsCount: _3, reactions: _4, reactionsCount: _5, recentViewers: _6) } } @@ -588,12 +577,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 3) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.SuggestedPost.suggestedPost(flags: _1!, price: _2, scheduleDate: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.SuggestedPost.suggestedPost(flags: _1!, price: _2, scheduleDate: _3) } } @@ -634,12 +621,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.TextWithEntities.textWithEntities(text: _1!, entities: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.TextWithEntities.textWithEntities(text: _1!, entities: _2!) } } @@ -710,12 +694,16 @@ public extension Api { let _c7 = (Int(_1!) & Int(1 << 3) == 0) || _7 != nil let _c8 = (Int(_1!) & Int(1 << 6) == 0) || _8 != nil let _c9 = (Int(_1!) & Int(1 << 4) == 0) || _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.Theme.theme(flags: _1!, id: _2!, accessHash: _3!, slug: _4!, title: _5!, document: _6, settings: _7, emoticon: _8, installsCount: _9) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + return Api.Theme.theme(flags: _1!, id: _2!, accessHash: _3!, slug: _4!, title: _5!, document: _6, settings: _7, emoticon: _8, installsCount: _9) } } @@ -776,12 +764,13 @@ public extension Api { let _c4 = (Int(_1!) & Int(1 << 3) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.ThemeSettings.themeSettings(flags: _1!, baseTheme: _2!, accentColor: _3!, outboxAccentColor: _4, messageColors: _5, wallpaper: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.ThemeSettings.themeSettings(flags: _1!, baseTheme: _2!, accentColor: _3!, outboxAccentColor: _4, messageColors: _5, wallpaper: _6) } } @@ -820,12 +809,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Timezone.timezone(id: _1!, name: _2!, utcOffset: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Timezone.timezone(id: _1!, name: _2!, utcOffset: _3!) } } @@ -866,12 +853,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.TodoCompletion.todoCompletion(id: _1!, completedBy: _2!, date: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.TodoCompletion.todoCompletion(id: _1!, completedBy: _2!, date: _3!) } } @@ -908,12 +893,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.TodoItem.todoItem(id: _1!, title: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.TodoItem.todoItem(id: _1!, title: _2!) } } @@ -960,12 +942,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.TodoList.todoList(flags: _1!, title: _2!, list: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.TodoList.todoList(flags: _1!, title: _2!, list: _3!) } } @@ -1002,12 +982,9 @@ public extension Api { _2 = reader.readDouble() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.TopPeer.topPeer(peer: _1!, rating: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.TopPeer.topPeer(peer: _1!, rating: _2!) } } @@ -1178,12 +1155,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.TopPeerCategoryPeers.topPeerCategoryPeers(category: _1!, count: _2!, peers: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.TopPeerCategoryPeers.topPeerCategoryPeers(category: _1!, count: _2!, peers: _3!) } } @@ -1247,6 +1222,7 @@ public extension Api { case updateDraftMessage(flags: Int32, peer: Api.Peer, topMsgId: Int32?, savedPeerId: Api.Peer?, draft: Api.DraftMessage) case updateEditChannelMessage(message: Api.Message, pts: Int32, ptsCount: Int32) case updateEditMessage(message: Api.Message, pts: Int32, ptsCount: Int32) + case updateEmojiGameInfo(info: Api.messages.EmojiGameInfo) case updateEncryptedChatTyping(chatId: Int32) case updateEncryptedMessagesRead(chatId: Int32, maxDate: Int32, date: Int32) case updateEncryption(chat: Api.EncryptedChat, date: Int32) @@ -1887,6 +1863,12 @@ public extension Api { serializeInt32(pts, buffer: buffer, boxed: false) serializeInt32(ptsCount, buffer: buffer, boxed: false) break + case .updateEmojiGameInfo(let info): + if boxed { + buffer.appendInt32(-73640838) + } + info.serialize(buffer, true) + break case .updateEncryptedChatTyping(let chatId): if boxed { buffer.appendInt32(386986326) @@ -2774,6 +2756,8 @@ public extension Api { return ("updateEditChannelMessage", [("message", message as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) case .updateEditMessage(let message, let pts, let ptsCount): return ("updateEditMessage", [("message", message as Any), ("pts", pts as Any), ("ptsCount", ptsCount as Any)]) + case .updateEmojiGameInfo(let info): + return ("updateEmojiGameInfo", [("info", info as Any)]) case .updateEncryptedChatTyping(let chatId): return ("updateEncryptedChatTyping", [("chatId", chatId as Any)]) case .updateEncryptedMessagesRead(let chatId, let maxDate, let date): @@ -2978,12 +2962,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateBotBusinessConnect(connection: _1!, qts: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updateBotBusinessConnect(connection: _1!, qts: _2!) } public static func parse_updateBotCallbackQuery(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -3012,12 +2993,15 @@ public extension Api { let _c6 = _6 != nil let _c7 = (Int(_1!) & Int(1 << 0) == 0) || _7 != nil let _c8 = (Int(_1!) & Int(1 << 1) == 0) || _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.Update.updateBotCallbackQuery(flags: _1!, queryId: _2!, userId: _3!, peer: _4!, msgId: _5!, chatInstance: _6!, data: _7, gameShortName: _8) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.Update.updateBotCallbackQuery(flags: _1!, queryId: _2!, userId: _3!, peer: _4!, msgId: _5!, chatInstance: _6!, data: _7, gameShortName: _8) } public static func parse_updateBotChatBoost(_ reader: BufferReader) -> Update? { var _1: Api.Peer? @@ -3033,12 +3017,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateBotChatBoost(peer: _1!, boost: _2!, qts: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateBotChatBoost(peer: _1!, boost: _2!, qts: _3!) } public static func parse_updateBotChatInviteRequester(_ reader: BufferReader) -> Update? { var _1: Api.Peer? @@ -3063,12 +3045,13 @@ public extension Api { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.Update.updateBotChatInviteRequester(peer: _1!, date: _2!, userId: _3!, about: _4!, invite: _5!, qts: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.Update.updateBotChatInviteRequester(peer: _1!, date: _2!, userId: _3!, about: _4!, invite: _5!, qts: _6!) } public static func parse_updateBotCommands(_ reader: BufferReader) -> Update? { var _1: Api.Peer? @@ -3084,12 +3067,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateBotCommands(peer: _1!, botId: _2!, commands: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateBotCommands(peer: _1!, botId: _2!, commands: _3!) } public static func parse_updateBotDeleteBusinessMessage(_ reader: BufferReader) -> Update? { var _1: String? @@ -3108,12 +3089,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.Update.updateBotDeleteBusinessMessage(connectionId: _1!, peer: _2!, messages: _3!, qts: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.Update.updateBotDeleteBusinessMessage(connectionId: _1!, peer: _2!, messages: _3!, qts: _4!) } public static func parse_updateBotEditBusinessMessage(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -3135,12 +3115,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.Update.updateBotEditBusinessMessage(flags: _1!, connectionId: _2!, message: _3!, replyToMessage: _4, qts: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.Update.updateBotEditBusinessMessage(flags: _1!, connectionId: _2!, message: _3!, replyToMessage: _4, qts: _5!) } public static func parse_updateBotInlineQuery(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -3168,12 +3148,14 @@ public extension Api { let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.Update.updateBotInlineQuery(flags: _1!, queryId: _2!, userId: _3!, query: _4!, geo: _5, peerType: _6, offset: _7!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.Update.updateBotInlineQuery(flags: _1!, queryId: _2!, userId: _3!, query: _4!, geo: _5, peerType: _6, offset: _7!) } public static func parse_updateBotInlineSend(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -3198,12 +3180,13 @@ public extension Api { let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil let _c5 = _5 != nil let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.Update.updateBotInlineSend(flags: _1!, userId: _2!, query: _3!, geo: _4, id: _5!, msgId: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.Update.updateBotInlineSend(flags: _1!, userId: _2!, query: _3!, geo: _4, id: _5!, msgId: _6) } public static func parse_updateBotMenuButton(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -3214,12 +3197,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateBotMenuButton(botId: _1!, button: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updateBotMenuButton(botId: _1!, button: _2!) } public static func parse_updateBotMessageReaction(_ reader: BufferReader) -> Update? { var _1: Api.Peer? @@ -3251,12 +3231,14 @@ public extension Api { let _c5 = _5 != nil let _c6 = _6 != nil let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.Update.updateBotMessageReaction(peer: _1!, msgId: _2!, date: _3!, actor: _4!, oldReactions: _5!, newReactions: _6!, qts: _7!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.Update.updateBotMessageReaction(peer: _1!, msgId: _2!, date: _3!, actor: _4!, oldReactions: _5!, newReactions: _6!, qts: _7!) } public static func parse_updateBotMessageReactions(_ reader: BufferReader) -> Update? { var _1: Api.Peer? @@ -3278,12 +3260,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.Update.updateBotMessageReactions(peer: _1!, msgId: _2!, date: _3!, reactions: _4!, qts: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.Update.updateBotMessageReactions(peer: _1!, msgId: _2!, date: _3!, reactions: _4!, qts: _5!) } public static func parse_updateBotNewBusinessMessage(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -3305,12 +3287,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.Update.updateBotNewBusinessMessage(flags: _1!, connectionId: _2!, message: _3!, replyToMessage: _4, qts: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.Update.updateBotNewBusinessMessage(flags: _1!, connectionId: _2!, message: _3!, replyToMessage: _4, qts: _5!) } public static func parse_updateBotPrecheckoutQuery(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -3339,12 +3321,15 @@ public extension Api { let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil let _c7 = _7 != nil let _c8 = _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.Update.updateBotPrecheckoutQuery(flags: _1!, queryId: _2!, userId: _3!, payload: _4!, info: _5, shippingOptionId: _6, currency: _7!, totalAmount: _8!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.Update.updateBotPrecheckoutQuery(flags: _1!, queryId: _2!, userId: _3!, payload: _4!, info: _5, shippingOptionId: _6, currency: _7!, totalAmount: _8!) } public static func parse_updateBotPurchasedPaidMedia(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -3356,12 +3341,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateBotPurchasedPaidMedia(userId: _1!, payload: _2!, qts: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateBotPurchasedPaidMedia(userId: _1!, payload: _2!, qts: _3!) } public static func parse_updateBotShippingQuery(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -3378,12 +3361,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.Update.updateBotShippingQuery(queryId: _1!, userId: _2!, payload: _3!, shippingAddress: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.Update.updateBotShippingQuery(queryId: _1!, userId: _2!, payload: _3!, shippingAddress: _4!) } public static func parse_updateBotStopped(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -3400,12 +3382,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.Update.updateBotStopped(userId: _1!, date: _2!, stopped: _3!, qts: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.Update.updateBotStopped(userId: _1!, date: _2!, stopped: _3!, qts: _4!) } public static func parse_updateBotWebhookJSON(_ reader: BufferReader) -> Update? { var _1: Api.DataJSON? @@ -3413,12 +3394,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.DataJSON } let _c1 = _1 != nil - if _c1 { - return Api.Update.updateBotWebhookJSON(data: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Update.updateBotWebhookJSON(data: _1!) } public static func parse_updateBotWebhookJSONQuery(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -3432,12 +3409,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateBotWebhookJSONQuery(queryId: _1!, data: _2!, timeout: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateBotWebhookJSONQuery(queryId: _1!, data: _2!, timeout: _3!) } public static func parse_updateBusinessBotCallbackQuery(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -3468,23 +3443,22 @@ public extension Api { let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil let _c7 = _7 != nil let _c8 = (Int(_1!) & Int(1 << 0) == 0) || _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.Update.updateBusinessBotCallbackQuery(flags: _1!, queryId: _2!, userId: _3!, connectionId: _4!, message: _5!, replyToMessage: _6, chatInstance: _7!, data: _8) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.Update.updateBusinessBotCallbackQuery(flags: _1!, queryId: _2!, userId: _3!, connectionId: _4!, message: _5!, replyToMessage: _6, chatInstance: _7!, data: _8) } public static func parse_updateChannel(_ reader: BufferReader) -> Update? { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.Update.updateChannel(channelId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Update.updateChannel(channelId: _1!) } public static func parse_updateChannelAvailableMessages(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -3493,12 +3467,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateChannelAvailableMessages(channelId: _1!, availableMinId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updateChannelAvailableMessages(channelId: _1!, availableMinId: _2!) } public static func parse_updateChannelMessageForwards(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -3510,12 +3481,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateChannelMessageForwards(channelId: _1!, id: _2!, forwards: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateChannelMessageForwards(channelId: _1!, id: _2!, forwards: _3!) } public static func parse_updateChannelMessageViews(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -3527,12 +3496,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateChannelMessageViews(channelId: _1!, id: _2!, views: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateChannelMessageViews(channelId: _1!, id: _2!, views: _3!) } public static func parse_updateChannelParticipant(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -3568,12 +3535,16 @@ public extension Api { let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil let _c8 = (Int(_1!) & Int(1 << 2) == 0) || _8 != nil let _c9 = _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.Update.updateChannelParticipant(flags: _1!, channelId: _2!, date: _3!, actorId: _4!, userId: _5!, prevParticipant: _6, newParticipant: _7, invite: _8, qts: _9!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + return Api.Update.updateChannelParticipant(flags: _1!, channelId: _2!, date: _3!, actorId: _4!, userId: _5!, prevParticipant: _6, newParticipant: _7, invite: _8, qts: _9!) } public static func parse_updateChannelReadMessagesContents(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -3595,12 +3566,12 @@ public extension Api { let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.Update.updateChannelReadMessagesContents(flags: _1!, channelId: _2!, topMsgId: _3, savedPeerId: _4, messages: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.Update.updateChannelReadMessagesContents(flags: _1!, channelId: _2!, topMsgId: _3, savedPeerId: _4, messages: _5!) } public static func parse_updateChannelTooLong(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -3612,12 +3583,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateChannelTooLong(flags: _1!, channelId: _2!, pts: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateChannelTooLong(flags: _1!, channelId: _2!, pts: _3) } public static func parse_updateChannelUserTyping(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -3639,12 +3608,12 @@ public extension Api { let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.Update.updateChannelUserTyping(flags: _1!, channelId: _2!, topMsgId: _3, fromId: _4!, action: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.Update.updateChannelUserTyping(flags: _1!, channelId: _2!, topMsgId: _3, fromId: _4!, action: _5!) } public static func parse_updateChannelViewForumAsMessages(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -3655,12 +3624,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateChannelViewForumAsMessages(channelId: _1!, enabled: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updateChannelViewForumAsMessages(channelId: _1!, enabled: _2!) } public static func parse_updateChannelWebPage(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -3677,23 +3643,18 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.Update.updateChannelWebPage(channelId: _1!, webpage: _2!, pts: _3!, ptsCount: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.Update.updateChannelWebPage(channelId: _1!, webpage: _2!, pts: _3!, ptsCount: _4!) } public static func parse_updateChat(_ reader: BufferReader) -> Update? { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.Update.updateChat(chatId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Update.updateChat(chatId: _1!) } public static func parse_updateChatDefaultBannedRights(_ reader: BufferReader) -> Update? { var _1: Api.Peer? @@ -3709,12 +3670,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateChatDefaultBannedRights(peer: _1!, defaultBannedRights: _2!, version: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateChatDefaultBannedRights(peer: _1!, defaultBannedRights: _2!, version: _3!) } public static func parse_updateChatParticipant(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -3750,12 +3709,16 @@ public extension Api { let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil let _c8 = (Int(_1!) & Int(1 << 2) == 0) || _8 != nil let _c9 = _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.Update.updateChatParticipant(flags: _1!, chatId: _2!, date: _3!, actorId: _4!, userId: _5!, prevParticipant: _6, newParticipant: _7, invite: _8, qts: _9!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + return Api.Update.updateChatParticipant(flags: _1!, chatId: _2!, date: _3!, actorId: _4!, userId: _5!, prevParticipant: _6, newParticipant: _7, invite: _8, qts: _9!) } public static func parse_updateChatParticipantAdd(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -3773,12 +3736,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.Update.updateChatParticipantAdd(chatId: _1!, userId: _2!, inviterId: _3!, date: _4!, version: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.Update.updateChatParticipantAdd(chatId: _1!, userId: _2!, inviterId: _3!, date: _4!, version: _5!) } public static func parse_updateChatParticipantAdmin(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -3795,12 +3758,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.Update.updateChatParticipantAdmin(chatId: _1!, userId: _2!, isAdmin: _3!, version: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.Update.updateChatParticipantAdmin(chatId: _1!, userId: _2!, isAdmin: _3!, version: _4!) } public static func parse_updateChatParticipantDelete(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -3812,12 +3774,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateChatParticipantDelete(chatId: _1!, userId: _2!, version: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateChatParticipantDelete(chatId: _1!, userId: _2!, version: _3!) } public static func parse_updateChatParticipants(_ reader: BufferReader) -> Update? { var _1: Api.ChatParticipants? @@ -3825,12 +3785,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.ChatParticipants } let _c1 = _1 != nil - if _c1 { - return Api.Update.updateChatParticipants(participants: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Update.updateChatParticipants(participants: _1!) } public static func parse_updateChatUserTyping(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -3846,12 +3802,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateChatUserTyping(chatId: _1!, fromId: _2!, action: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateChatUserTyping(chatId: _1!, fromId: _2!, action: _3!) } public static func parse_updateConfig(_ reader: BufferReader) -> Update? { return Api.Update.updateConfig @@ -3865,12 +3819,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.DcOption.self) } let _c1 = _1 != nil - if _c1 { - return Api.Update.updateDcOptions(dcOptions: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Update.updateDcOptions(dcOptions: _1!) } public static func parse_updateDeleteChannelMessages(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -3887,12 +3837,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.Update.updateDeleteChannelMessages(channelId: _1!, messages: _2!, pts: _3!, ptsCount: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.Update.updateDeleteChannelMessages(channelId: _1!, messages: _2!, pts: _3!, ptsCount: _4!) } public static func parse_updateDeleteGroupCallMessages(_ reader: BufferReader) -> Update? { var _1: Api.InputGroupCall? @@ -3905,12 +3854,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateDeleteGroupCallMessages(call: _1!, messages: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updateDeleteGroupCallMessages(call: _1!, messages: _2!) } public static func parse_updateDeleteMessages(_ reader: BufferReader) -> Update? { var _1: [Int32]? @@ -3924,23 +3870,17 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateDeleteMessages(messages: _1!, pts: _2!, ptsCount: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateDeleteMessages(messages: _1!, pts: _2!, ptsCount: _3!) } public static func parse_updateDeleteQuickReply(_ reader: BufferReader) -> Update? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.Update.updateDeleteQuickReply(shortcutId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Update.updateDeleteQuickReply(shortcutId: _1!) } public static func parse_updateDeleteQuickReplyMessages(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -3951,12 +3891,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateDeleteQuickReplyMessages(shortcutId: _1!, messages: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updateDeleteQuickReplyMessages(shortcutId: _1!, messages: _2!) } public static func parse_updateDeleteScheduledMessages(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -3977,12 +3914,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.Update.updateDeleteScheduledMessages(flags: _1!, peer: _2!, messages: _3!, sentMessages: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.Update.updateDeleteScheduledMessages(flags: _1!, peer: _2!, messages: _3!, sentMessages: _4) } public static func parse_updateDialogFilter(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -3996,12 +3932,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateDialogFilter(flags: _1!, id: _2!, filter: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateDialogFilter(flags: _1!, id: _2!, filter: _3) } public static func parse_updateDialogFilterOrder(_ reader: BufferReader) -> Update? { var _1: [Int32]? @@ -4009,12 +3943,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) } let _c1 = _1 != nil - if _c1 { - return Api.Update.updateDialogFilterOrder(order: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Update.updateDialogFilterOrder(order: _1!) } public static func parse_updateDialogFilters(_ reader: BufferReader) -> Update? { return Api.Update.updateDialogFilters @@ -4031,12 +3961,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateDialogPinned(flags: _1!, folderId: _2, peer: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateDialogPinned(flags: _1!, folderId: _2, peer: _3!) } public static func parse_updateDialogUnreadMark(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -4052,12 +3980,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateDialogUnreadMark(flags: _1!, peer: _2!, savedPeerId: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateDialogUnreadMark(flags: _1!, peer: _2!, savedPeerId: _3) } public static func parse_updateDraftMessage(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -4081,12 +4007,12 @@ public extension Api { let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.Update.updateDraftMessage(flags: _1!, peer: _2!, topMsgId: _3, savedPeerId: _4, draft: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.Update.updateDraftMessage(flags: _1!, peer: _2!, topMsgId: _3, savedPeerId: _4, draft: _5!) } public static func parse_updateEditChannelMessage(_ reader: BufferReader) -> Update? { var _1: Api.Message? @@ -4100,12 +4026,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateEditChannelMessage(message: _1!, pts: _2!, ptsCount: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateEditChannelMessage(message: _1!, pts: _2!, ptsCount: _3!) } public static func parse_updateEditMessage(_ reader: BufferReader) -> Update? { var _1: Api.Message? @@ -4119,23 +4043,26 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateEditMessage(message: _1!, pts: _2!, ptsCount: _3!) - } - else { - return nil + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateEditMessage(message: _1!, pts: _2!, ptsCount: _3!) + } + public static func parse_updateEmojiGameInfo(_ reader: BufferReader) -> Update? { + var _1: Api.messages.EmojiGameInfo? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.messages.EmojiGameInfo } + let _c1 = _1 != nil + if !_c1 { return nil } + return Api.Update.updateEmojiGameInfo(info: _1!) } public static func parse_updateEncryptedChatTyping(_ reader: BufferReader) -> Update? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.Update.updateEncryptedChatTyping(chatId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Update.updateEncryptedChatTyping(chatId: _1!) } public static func parse_updateEncryptedMessagesRead(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -4147,12 +4074,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateEncryptedMessagesRead(chatId: _1!, maxDate: _2!, date: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateEncryptedMessagesRead(chatId: _1!, maxDate: _2!, date: _3!) } public static func parse_updateEncryption(_ reader: BufferReader) -> Update? { var _1: Api.EncryptedChat? @@ -4163,12 +4088,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateEncryption(chat: _1!, date: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updateEncryption(chat: _1!, date: _2!) } public static func parse_updateFavedStickers(_ reader: BufferReader) -> Update? { return Api.Update.updateFavedStickers @@ -4185,12 +4107,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateFolderPeers(folderPeers: _1!, pts: _2!, ptsCount: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateFolderPeers(folderPeers: _1!, pts: _2!, ptsCount: _3!) } public static func parse_updateGeoLiveViewed(_ reader: BufferReader) -> Update? { var _1: Api.Peer? @@ -4201,12 +4121,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateGeoLiveViewed(peer: _1!, msgId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updateGeoLiveViewed(peer: _1!, msgId: _2!) } public static func parse_updateGroupCall(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -4222,12 +4139,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateGroupCall(flags: _1!, peer: _2, call: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateGroupCall(flags: _1!, peer: _2, call: _3!) } public static func parse_updateGroupCallChainBlocks(_ reader: BufferReader) -> Update? { var _1: Api.InputGroupCall? @@ -4246,12 +4161,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.Update.updateGroupCallChainBlocks(call: _1!, subChainId: _2!, blocks: _3!, nextOffset: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.Update.updateGroupCallChainBlocks(call: _1!, subChainId: _2!, blocks: _3!, nextOffset: _4!) } public static func parse_updateGroupCallConnection(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -4262,12 +4176,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateGroupCallConnection(flags: _1!, params: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updateGroupCallConnection(flags: _1!, params: _2!) } public static func parse_updateGroupCallEncryptedMessage(_ reader: BufferReader) -> Update? { var _1: Api.InputGroupCall? @@ -4283,12 +4194,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateGroupCallEncryptedMessage(call: _1!, fromId: _2!, encryptedMessage: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateGroupCallEncryptedMessage(call: _1!, fromId: _2!, encryptedMessage: _3!) } public static func parse_updateGroupCallMessage(_ reader: BufferReader) -> Update? { var _1: Api.InputGroupCall? @@ -4301,12 +4210,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateGroupCallMessage(call: _1!, message: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updateGroupCallMessage(call: _1!, message: _2!) } public static func parse_updateGroupCallParticipants(_ reader: BufferReader) -> Update? { var _1: Api.InputGroupCall? @@ -4322,12 +4228,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateGroupCallParticipants(call: _1!, participants: _2!, version: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateGroupCallParticipants(call: _1!, participants: _2!, version: _3!) } public static func parse_updateInlineBotCallbackQuery(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -4353,12 +4257,14 @@ public extension Api { let _c5 = _5 != nil let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.Update.updateInlineBotCallbackQuery(flags: _1!, queryId: _2!, userId: _3!, msgId: _4!, chatInstance: _5!, data: _6, gameShortName: _7) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.Update.updateInlineBotCallbackQuery(flags: _1!, queryId: _2!, userId: _3!, msgId: _4!, chatInstance: _5!, data: _6, gameShortName: _7) } public static func parse_updateLangPack(_ reader: BufferReader) -> Update? { var _1: Api.LangPackDifference? @@ -4366,23 +4272,15 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.LangPackDifference } let _c1 = _1 != nil - if _c1 { - return Api.Update.updateLangPack(difference: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Update.updateLangPack(difference: _1!) } public static func parse_updateLangPackTooLong(_ reader: BufferReader) -> Update? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.Update.updateLangPackTooLong(langCode: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Update.updateLangPackTooLong(langCode: _1!) } public static func parse_updateLoginToken(_ reader: BufferReader) -> Update? { return Api.Update.updateLoginToken @@ -4401,12 +4299,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateMessageExtendedMedia(peer: _1!, msgId: _2!, extendedMedia: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateMessageExtendedMedia(peer: _1!, msgId: _2!, extendedMedia: _3!) } public static func parse_updateMessageID(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -4415,12 +4311,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateMessageID(id: _1!, randomId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updateMessageID(id: _1!, randomId: _2!) } public static func parse_updateMessagePoll(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -4439,12 +4332,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.Update.updateMessagePoll(flags: _1!, pollId: _2!, poll: _3, results: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.Update.updateMessagePoll(flags: _1!, pollId: _2!, poll: _3, results: _4!) } public static func parse_updateMessagePollVote(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -4463,12 +4355,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.Update.updateMessagePollVote(pollId: _1!, peer: _2!, options: _3!, qts: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.Update.updateMessagePollVote(pollId: _1!, peer: _2!, options: _3!, qts: _4!) } public static func parse_updateMessageReactions(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -4495,12 +4386,13 @@ public extension Api { let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.Update.updateMessageReactions(flags: _1!, peer: _2!, msgId: _3!, topMsgId: _4, savedPeerId: _5, reactions: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.Update.updateMessageReactions(flags: _1!, peer: _2!, msgId: _3!, topMsgId: _4, savedPeerId: _5, reactions: _6!) } public static func parse_updateMonoForumNoPaidException(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -4514,12 +4406,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateMonoForumNoPaidException(flags: _1!, channelId: _2!, savedPeerId: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateMonoForumNoPaidException(flags: _1!, channelId: _2!, savedPeerId: _3!) } public static func parse_updateMoveStickerSetToTop(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -4528,12 +4418,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateMoveStickerSetToTop(flags: _1!, stickerset: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updateMoveStickerSetToTop(flags: _1!, stickerset: _2!) } public static func parse_updateNewAuthorization(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -4551,12 +4438,12 @@ public extension Api { let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.Update.updateNewAuthorization(flags: _1!, hash: _2!, date: _3, device: _4, location: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.Update.updateNewAuthorization(flags: _1!, hash: _2!, date: _3, device: _4, location: _5) } public static func parse_updateNewChannelMessage(_ reader: BufferReader) -> Update? { var _1: Api.Message? @@ -4570,12 +4457,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateNewChannelMessage(message: _1!, pts: _2!, ptsCount: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateNewChannelMessage(message: _1!, pts: _2!, ptsCount: _3!) } public static func parse_updateNewEncryptedMessage(_ reader: BufferReader) -> Update? { var _1: Api.EncryptedMessage? @@ -4586,12 +4471,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateNewEncryptedMessage(message: _1!, qts: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updateNewEncryptedMessage(message: _1!, qts: _2!) } public static func parse_updateNewMessage(_ reader: BufferReader) -> Update? { var _1: Api.Message? @@ -4605,12 +4487,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateNewMessage(message: _1!, pts: _2!, ptsCount: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateNewMessage(message: _1!, pts: _2!, ptsCount: _3!) } public static func parse_updateNewQuickReply(_ reader: BufferReader) -> Update? { var _1: Api.QuickReply? @@ -4618,12 +4498,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.QuickReply } let _c1 = _1 != nil - if _c1 { - return Api.Update.updateNewQuickReply(quickReply: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Update.updateNewQuickReply(quickReply: _1!) } public static func parse_updateNewScheduledMessage(_ reader: BufferReader) -> Update? { var _1: Api.Message? @@ -4631,12 +4507,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Message } let _c1 = _1 != nil - if _c1 { - return Api.Update.updateNewScheduledMessage(message: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Update.updateNewScheduledMessage(message: _1!) } public static func parse_updateNewStickerSet(_ reader: BufferReader) -> Update? { var _1: Api.messages.StickerSet? @@ -4644,12 +4516,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.messages.StickerSet } let _c1 = _1 != nil - if _c1 { - return Api.Update.updateNewStickerSet(stickerset: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Update.updateNewStickerSet(stickerset: _1!) } public static func parse_updateNewStoryReaction(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -4665,12 +4533,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateNewStoryReaction(storyId: _1!, peer: _2!, reaction: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateNewStoryReaction(storyId: _1!, peer: _2!, reaction: _3!) } public static func parse_updateNotifySettings(_ reader: BufferReader) -> Update? { var _1: Api.NotifyPeer? @@ -4683,12 +4549,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateNotifySettings(peer: _1!, notifySettings: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updateNotifySettings(peer: _1!, notifySettings: _2!) } public static func parse_updatePaidReactionPrivacy(_ reader: BufferReader) -> Update? { var _1: Api.PaidReactionPrivacy? @@ -4696,12 +4559,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.PaidReactionPrivacy } let _c1 = _1 != nil - if _c1 { - return Api.Update.updatePaidReactionPrivacy(private: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Update.updatePaidReactionPrivacy(private: _1!) } public static func parse_updatePeerBlocked(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -4712,12 +4571,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updatePeerBlocked(flags: _1!, peerId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updatePeerBlocked(flags: _1!, peerId: _2!) } public static func parse_updatePeerHistoryTTL(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -4731,12 +4587,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updatePeerHistoryTTL(flags: _1!, peer: _2!, ttlPeriod: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updatePeerHistoryTTL(flags: _1!, peer: _2!, ttlPeriod: _3) } public static func parse_updatePeerLocated(_ reader: BufferReader) -> Update? { var _1: [Api.PeerLocated]? @@ -4744,12 +4598,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PeerLocated.self) } let _c1 = _1 != nil - if _c1 { - return Api.Update.updatePeerLocated(peers: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Update.updatePeerLocated(peers: _1!) } public static func parse_updatePeerSettings(_ reader: BufferReader) -> Update? { var _1: Api.Peer? @@ -4762,12 +4612,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updatePeerSettings(peer: _1!, settings: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updatePeerSettings(peer: _1!, settings: _2!) } public static func parse_updatePeerWallpaper(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -4783,12 +4630,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updatePeerWallpaper(flags: _1!, peer: _2!, wallpaper: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updatePeerWallpaper(flags: _1!, peer: _2!, wallpaper: _3) } public static func parse_updatePendingJoinRequests(_ reader: BufferReader) -> Update? { var _1: Api.Peer? @@ -4804,12 +4649,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updatePendingJoinRequests(peer: _1!, requestsPending: _2!, recentRequesters: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updatePendingJoinRequests(peer: _1!, requestsPending: _2!, recentRequesters: _3!) } public static func parse_updatePhoneCall(_ reader: BufferReader) -> Update? { var _1: Api.PhoneCall? @@ -4817,12 +4660,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.PhoneCall } let _c1 = _1 != nil - if _c1 { - return Api.Update.updatePhoneCall(phoneCall: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Update.updatePhoneCall(phoneCall: _1!) } public static func parse_updatePhoneCallSignalingData(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -4831,12 +4670,9 @@ public extension Api { _2 = parseBytes(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updatePhoneCallSignalingData(phoneCallId: _1!, data: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updatePhoneCallSignalingData(phoneCallId: _1!, data: _2!) } public static func parse_updatePinnedChannelMessages(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -4856,12 +4692,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.Update.updatePinnedChannelMessages(flags: _1!, channelId: _2!, messages: _3!, pts: _4!, ptsCount: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.Update.updatePinnedChannelMessages(flags: _1!, channelId: _2!, messages: _3!, pts: _4!, ptsCount: _5!) } public static func parse_updatePinnedDialogs(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -4875,12 +4711,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updatePinnedDialogs(flags: _1!, folderId: _2, order: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updatePinnedDialogs(flags: _1!, folderId: _2, order: _3) } public static func parse_updatePinnedForumTopic(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -4894,12 +4728,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updatePinnedForumTopic(flags: _1!, peer: _2!, topicId: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updatePinnedForumTopic(flags: _1!, peer: _2!, topicId: _3!) } public static func parse_updatePinnedForumTopics(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -4915,12 +4747,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updatePinnedForumTopics(flags: _1!, peer: _2!, order: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updatePinnedForumTopics(flags: _1!, peer: _2!, order: _3) } public static func parse_updatePinnedMessages(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -4942,12 +4772,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.Update.updatePinnedMessages(flags: _1!, peer: _2!, messages: _3!, pts: _4!, ptsCount: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.Update.updatePinnedMessages(flags: _1!, peer: _2!, messages: _3!, pts: _4!, ptsCount: _5!) } public static func parse_updatePinnedSavedDialogs(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -4958,12 +4788,9 @@ public extension Api { } } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - if _c1 && _c2 { - return Api.Update.updatePinnedSavedDialogs(flags: _1!, order: _2) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updatePinnedSavedDialogs(flags: _1!, order: _2) } public static func parse_updatePrivacy(_ reader: BufferReader) -> Update? { var _1: Api.PrivacyKey? @@ -4976,12 +4803,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updatePrivacy(key: _1!, rules: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updatePrivacy(key: _1!, rules: _2!) } public static func parse_updatePtsChanged(_ reader: BufferReader) -> Update? { return Api.Update.updatePtsChanged @@ -4992,12 +4816,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.QuickReply.self) } let _c1 = _1 != nil - if _c1 { - return Api.Update.updateQuickReplies(quickReplies: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Update.updateQuickReplies(quickReplies: _1!) } public static func parse_updateQuickReplyMessage(_ reader: BufferReader) -> Update? { var _1: Api.Message? @@ -5005,12 +4825,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Message } let _c1 = _1 != nil - if _c1 { - return Api.Update.updateQuickReplyMessage(message: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Update.updateQuickReplyMessage(message: _1!) } public static func parse_updateReadChannelDiscussionInbox(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -5031,12 +4847,13 @@ public extension Api { let _c4 = _4 != nil let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.Update.updateReadChannelDiscussionInbox(flags: _1!, channelId: _2!, topMsgId: _3!, readMaxId: _4!, broadcastId: _5, broadcastPost: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.Update.updateReadChannelDiscussionInbox(flags: _1!, channelId: _2!, topMsgId: _3!, readMaxId: _4!, broadcastId: _5, broadcastPost: _6) } public static func parse_updateReadChannelDiscussionOutbox(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -5048,12 +4865,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateReadChannelDiscussionOutbox(channelId: _1!, topMsgId: _2!, readMaxId: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateReadChannelDiscussionOutbox(channelId: _1!, topMsgId: _2!, readMaxId: _3!) } public static func parse_updateReadChannelInbox(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -5074,12 +4889,13 @@ public extension Api { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.Update.updateReadChannelInbox(flags: _1!, folderId: _2, channelId: _3!, maxId: _4!, stillUnreadCount: _5!, pts: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.Update.updateReadChannelInbox(flags: _1!, folderId: _2, channelId: _3!, maxId: _4!, stillUnreadCount: _5!, pts: _6!) } public static func parse_updateReadChannelOutbox(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -5088,12 +4904,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateReadChannelOutbox(channelId: _1!, maxId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updateReadChannelOutbox(channelId: _1!, maxId: _2!) } public static func parse_updateReadFeaturedEmojiStickers(_ reader: BufferReader) -> Update? { return Api.Update.updateReadFeaturedEmojiStickers @@ -5128,12 +4941,15 @@ public extension Api { let _c6 = _6 != nil let _c7 = _7 != nil let _c8 = _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.Update.updateReadHistoryInbox(flags: _1!, folderId: _2, peer: _3!, topMsgId: _4, maxId: _5!, stillUnreadCount: _6!, pts: _7!, ptsCount: _8!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.Update.updateReadHistoryInbox(flags: _1!, folderId: _2, peer: _3!, topMsgId: _4, maxId: _5!, stillUnreadCount: _6!, pts: _7!, ptsCount: _8!) } public static func parse_updateReadHistoryOutbox(_ reader: BufferReader) -> Update? { var _1: Api.Peer? @@ -5150,12 +4966,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.Update.updateReadHistoryOutbox(peer: _1!, maxId: _2!, pts: _3!, ptsCount: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.Update.updateReadHistoryOutbox(peer: _1!, maxId: _2!, pts: _3!, ptsCount: _4!) } public static func parse_updateReadMessagesContents(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -5175,12 +4990,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.Update.updateReadMessagesContents(flags: _1!, messages: _2!, pts: _3!, ptsCount: _4!, date: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.Update.updateReadMessagesContents(flags: _1!, messages: _2!, pts: _3!, ptsCount: _4!, date: _5) } public static func parse_updateReadMonoForumInbox(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -5194,12 +5009,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateReadMonoForumInbox(channelId: _1!, savedPeerId: _2!, readMaxId: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateReadMonoForumInbox(channelId: _1!, savedPeerId: _2!, readMaxId: _3!) } public static func parse_updateReadMonoForumOutbox(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -5213,12 +5026,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateReadMonoForumOutbox(channelId: _1!, savedPeerId: _2!, readMaxId: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateReadMonoForumOutbox(channelId: _1!, savedPeerId: _2!, readMaxId: _3!) } public static func parse_updateReadStories(_ reader: BufferReader) -> Update? { var _1: Api.Peer? @@ -5229,12 +5040,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateReadStories(peer: _1!, maxId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updateReadStories(peer: _1!, maxId: _2!) } public static func parse_updateRecentEmojiStatuses(_ reader: BufferReader) -> Update? { return Api.Update.updateRecentEmojiStatuses @@ -5254,12 +5062,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateSavedDialogPinned(flags: _1!, peer: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updateSavedDialogPinned(flags: _1!, peer: _2!) } public static func parse_updateSavedGifs(_ reader: BufferReader) -> Update? { return Api.Update.updateSavedGifs @@ -5276,12 +5081,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.auth.SentCode } let _c1 = _1 != nil - if _c1 { - return Api.Update.updateSentPhoneCode(sentCode: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Update.updateSentPhoneCode(sentCode: _1!) } public static func parse_updateSentStoryReaction(_ reader: BufferReader) -> Update? { var _1: Api.Peer? @@ -5297,12 +5098,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateSentStoryReaction(peer: _1!, storyId: _2!, reaction: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateSentStoryReaction(peer: _1!, storyId: _2!, reaction: _3!) } public static func parse_updateServiceNotification(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -5327,23 +5126,20 @@ public extension Api { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.Update.updateServiceNotification(flags: _1!, inboxDate: _2, type: _3!, message: _4!, media: _5!, entities: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.Update.updateServiceNotification(flags: _1!, inboxDate: _2, type: _3!, message: _4!, media: _5!, entities: _6!) } public static func parse_updateSmsJob(_ reader: BufferReader) -> Update? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.Update.updateSmsJob(jobId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Update.updateSmsJob(jobId: _1!) } public static func parse_updateStarGiftAuctionState(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -5354,12 +5150,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateStarGiftAuctionState(giftId: _1!, state: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updateStarGiftAuctionState(giftId: _1!, state: _2!) } public static func parse_updateStarGiftAuctionUserState(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -5370,12 +5163,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateStarGiftAuctionUserState(giftId: _1!, userState: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updateStarGiftAuctionUserState(giftId: _1!, userState: _2!) } public static func parse_updateStarsBalance(_ reader: BufferReader) -> Update? { var _1: Api.StarsAmount? @@ -5383,12 +5173,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.StarsAmount } let _c1 = _1 != nil - if _c1 { - return Api.Update.updateStarsBalance(balance: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Update.updateStarsBalance(balance: _1!) } public static func parse_updateStarsRevenueStatus(_ reader: BufferReader) -> Update? { var _1: Api.Peer? @@ -5401,23 +5187,16 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateStarsRevenueStatus(peer: _1!, status: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updateStarsRevenueStatus(peer: _1!, status: _2!) } public static func parse_updateStickerSets(_ reader: BufferReader) -> Update? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.Update.updateStickerSets(flags: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Update.updateStickerSets(flags: _1!) } public static func parse_updateStickerSetsOrder(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -5428,12 +5207,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateStickerSetsOrder(flags: _1!, order: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updateStickerSetsOrder(flags: _1!, order: _2!) } public static func parse_updateStoriesStealthMode(_ reader: BufferReader) -> Update? { var _1: Api.StoriesStealthMode? @@ -5441,12 +5217,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.StoriesStealthMode } let _c1 = _1 != nil - if _c1 { - return Api.Update.updateStoriesStealthMode(stealthMode: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Update.updateStoriesStealthMode(stealthMode: _1!) } public static func parse_updateStory(_ reader: BufferReader) -> Update? { var _1: Api.Peer? @@ -5459,12 +5231,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateStory(peer: _1!, story: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updateStory(peer: _1!, story: _2!) } public static func parse_updateStoryID(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -5473,12 +5242,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateStoryID(id: _1!, randomId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updateStoryID(id: _1!, randomId: _2!) } public static func parse_updateTheme(_ reader: BufferReader) -> Update? { var _1: Api.Theme? @@ -5486,12 +5252,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Theme } let _c1 = _1 != nil - if _c1 { - return Api.Update.updateTheme(theme: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Update.updateTheme(theme: _1!) } public static func parse_updateTranscribedAudio(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -5511,23 +5273,19 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.Update.updateTranscribedAudio(flags: _1!, peer: _2!, msgId: _3!, transcriptionId: _4!, text: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.Update.updateTranscribedAudio(flags: _1!, peer: _2!, msgId: _3!, transcriptionId: _4!, text: _5!) } public static func parse_updateUser(_ reader: BufferReader) -> Update? { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.Update.updateUser(userId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Update.updateUser(userId: _1!) } public static func parse_updateUserEmojiStatus(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -5538,12 +5296,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateUserEmojiStatus(userId: _1!, emojiStatus: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updateUserEmojiStatus(userId: _1!, emojiStatus: _2!) } public static func parse_updateUserName(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -5560,12 +5315,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.Update.updateUserName(userId: _1!, firstName: _2!, lastName: _3!, usernames: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.Update.updateUserName(userId: _1!, firstName: _2!, lastName: _3!, usernames: _4!) } public static func parse_updateUserPhone(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -5574,12 +5328,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateUserPhone(userId: _1!, phone: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updateUserPhone(userId: _1!, phone: _2!) } public static func parse_updateUserStatus(_ reader: BufferReader) -> Update? { var _1: Int64? @@ -5590,12 +5341,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Update.updateUserStatus(userId: _1!, status: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Update.updateUserStatus(userId: _1!, status: _2!) } public static func parse_updateUserTyping(_ reader: BufferReader) -> Update? { var _1: Int32? @@ -5612,12 +5360,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.Update.updateUserTyping(flags: _1!, userId: _2!, topMsgId: _3, action: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.Update.updateUserTyping(flags: _1!, userId: _2!, topMsgId: _3, action: _4!) } public static func parse_updateWebPage(_ reader: BufferReader) -> Update? { var _1: Api.WebPage? @@ -5631,23 +5378,17 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Update.updateWebPage(webpage: _1!, pts: _2!, ptsCount: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.Update.updateWebPage(webpage: _1!, pts: _2!, ptsCount: _3!) } public static func parse_updateWebViewResultSent(_ reader: BufferReader) -> Update? { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.Update.updateWebViewResultSent(queryId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Update.updateWebViewResultSent(queryId: _1!) } } diff --git a/submodules/TelegramApi/Sources/Api28.swift b/submodules/TelegramApi/Sources/Api28.swift index 69ae42de..247694f3 100644 --- a/submodules/TelegramApi/Sources/Api28.swift +++ b/submodules/TelegramApi/Sources/Api28.swift @@ -159,12 +159,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Updates.updateShort(update: _1!, date: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Updates.updateShort(update: _1!, date: _2!) } public static func parse_updateShortChatMessage(_ reader: BufferReader) -> Updates? { var _1: Int32? @@ -212,12 +209,20 @@ public extension Api { let _c11 = (Int(_1!) & Int(1 << 3) == 0) || _11 != nil let _c12 = (Int(_1!) & Int(1 << 7) == 0) || _12 != nil let _c13 = (Int(_1!) & Int(1 << 25) == 0) || _13 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 { - return Api.Updates.updateShortChatMessage(flags: _1!, id: _2!, fromId: _3!, chatId: _4!, message: _5!, pts: _6!, ptsCount: _7!, date: _8!, fwdFrom: _9, viaBotId: _10, replyTo: _11, entities: _12, ttlPeriod: _13) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + if !_c13 { return nil } + return Api.Updates.updateShortChatMessage(flags: _1!, id: _2!, fromId: _3!, chatId: _4!, message: _5!, pts: _6!, ptsCount: _7!, date: _8!, fwdFrom: _9, viaBotId: _10, replyTo: _11, entities: _12, ttlPeriod: _13) } public static func parse_updateShortMessage(_ reader: BufferReader) -> Updates? { var _1: Int32? @@ -262,12 +267,19 @@ public extension Api { let _c10 = (Int(_1!) & Int(1 << 3) == 0) || _10 != nil let _c11 = (Int(_1!) & Int(1 << 7) == 0) || _11 != nil let _c12 = (Int(_1!) & Int(1 << 25) == 0) || _12 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 { - return Api.Updates.updateShortMessage(flags: _1!, id: _2!, userId: _3!, message: _4!, pts: _5!, ptsCount: _6!, date: _7!, fwdFrom: _8, viaBotId: _9, replyTo: _10, entities: _11, ttlPeriod: _12) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + return Api.Updates.updateShortMessage(flags: _1!, id: _2!, userId: _3!, message: _4!, pts: _5!, ptsCount: _6!, date: _7!, fwdFrom: _8, viaBotId: _9, replyTo: _10, entities: _11, ttlPeriod: _12) } public static func parse_updateShortSentMessage(_ reader: BufferReader) -> Updates? { var _1: Int32? @@ -298,12 +310,15 @@ public extension Api { let _c6 = (Int(_1!) & Int(1 << 9) == 0) || _6 != nil let _c7 = (Int(_1!) & Int(1 << 7) == 0) || _7 != nil let _c8 = (Int(_1!) & Int(1 << 25) == 0) || _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.Updates.updateShortSentMessage(flags: _1!, id: _2!, pts: _3!, ptsCount: _4!, date: _5!, media: _6, entities: _7, ttlPeriod: _8) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.Updates.updateShortSentMessage(flags: _1!, id: _2!, pts: _3!, ptsCount: _4!, date: _5!, media: _6, entities: _7, ttlPeriod: _8) } public static func parse_updates(_ reader: BufferReader) -> Updates? { var _1: [Api.Update]? @@ -327,12 +342,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.Updates.updates(updates: _1!, users: _2!, chats: _3!, date: _4!, seq: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.Updates.updates(updates: _1!, users: _2!, chats: _3!, date: _4!, seq: _5!) } public static func parse_updatesCombined(_ reader: BufferReader) -> Updates? { var _1: [Api.Update]? @@ -359,12 +374,13 @@ public extension Api { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.Updates.updatesCombined(updates: _1!, users: _2!, chats: _3!, date: _4!, seqStart: _5!, seq: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.Updates.updatesCombined(updates: _1!, users: _2!, chats: _3!, date: _4!, seqStart: _5!, seq: _6!) } public static func parse_updatesTooLong(_ reader: BufferReader) -> Updates? { return Api.Updates.updatesTooLong @@ -418,12 +434,8 @@ public extension Api { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.UrlAuthResult.urlAuthResultAccepted(url: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.UrlAuthResult.urlAuthResultAccepted(url: _1!) } public static func parse_urlAuthResultDefault(_ reader: BufferReader) -> UrlAuthResult? { return Api.UrlAuthResult.urlAuthResultDefault @@ -440,12 +452,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.UrlAuthResult.urlAuthResultRequest(flags: _1!, bot: _2!, domain: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.UrlAuthResult.urlAuthResultRequest(flags: _1!, bot: _2!, domain: _3!) } } @@ -593,23 +603,36 @@ public extension Api { let _c20 = (Int(_2!) & Int(1 << 12) == 0) || _20 != nil let _c21 = (Int(_2!) & Int(1 << 14) == 0) || _21 != nil let _c22 = (Int(_2!) & Int(1 << 15) == 0) || _22 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 { - return Api.User.user(flags: _1!, flags2: _2!, id: _3!, accessHash: _4, firstName: _5, lastName: _6, username: _7, phone: _8, photo: _9, status: _10, botInfoVersion: _11, restrictionReason: _12, botInlinePlaceholder: _13, langCode: _14, emojiStatus: _15, usernames: _16, storiesMaxId: _17, color: _18, profileColor: _19, botActiveUsers: _20, botVerificationIcon: _21, sendPaidMessagesStars: _22) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + if !_c13 { return nil } + if !_c14 { return nil } + if !_c15 { return nil } + if !_c16 { return nil } + if !_c17 { return nil } + if !_c18 { return nil } + if !_c19 { return nil } + if !_c20 { return nil } + if !_c21 { return nil } + if !_c22 { return nil } + return Api.User.user(flags: _1!, flags2: _2!, id: _3!, accessHash: _4, firstName: _5, lastName: _6, username: _7, phone: _8, photo: _9, status: _10, botInfoVersion: _11, restrictionReason: _12, botInlinePlaceholder: _13, langCode: _14, emojiStatus: _15, usernames: _16, storiesMaxId: _17, color: _18, profileColor: _19, botActiveUsers: _20, botVerificationIcon: _21, sendPaidMessagesStars: _22) } public static func parse_userEmpty(_ reader: BufferReader) -> User? { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.User.userEmpty(id: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.User.userEmpty(id: _1!) } } @@ -842,12 +865,46 @@ public extension Api { let _c37 = (Int(_2!) & Int(1 << 20) == 0) || _37 != nil let _c38 = (Int(_2!) & Int(1 << 21) == 0) || _38 != nil let _c39 = (Int(_2!) & Int(1 << 22) == 0) || _39 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 && _c28 && _c29 && _c30 && _c31 && _c32 && _c33 && _c34 && _c35 && _c36 && _c37 && _c38 && _c39 { - return Api.UserFull.userFull(flags: _1!, flags2: _2!, id: _3!, about: _4, settings: _5!, personalPhoto: _6, profilePhoto: _7, fallbackPhoto: _8, notifySettings: _9!, botInfo: _10, pinnedMsgId: _11, commonChatsCount: _12!, folderId: _13, ttlPeriod: _14, theme: _15, privateForwardName: _16, botGroupAdminRights: _17, botBroadcastAdminRights: _18, wallpaper: _19, stories: _20, businessWorkHours: _21, businessLocation: _22, businessGreetingMessage: _23, businessAwayMessage: _24, businessIntro: _25, birthday: _26, personalChannelId: _27, personalChannelMessage: _28, stargiftsCount: _29, starrefProgram: _30, botVerification: _31, sendPaidMessagesStars: _32, disallowedGifts: _33, starsRating: _34, starsMyPendingRating: _35, starsMyPendingRatingDate: _36, mainTab: _37, savedMusic: _38, note: _39) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + if !_c13 { return nil } + if !_c14 { return nil } + if !_c15 { return nil } + if !_c16 { return nil } + if !_c17 { return nil } + if !_c18 { return nil } + if !_c19 { return nil } + if !_c20 { return nil } + if !_c21 { return nil } + if !_c22 { return nil } + if !_c23 { return nil } + if !_c24 { return nil } + if !_c25 { return nil } + if !_c26 { return nil } + if !_c27 { return nil } + if !_c28 { return nil } + if !_c29 { return nil } + if !_c30 { return nil } + if !_c31 { return nil } + if !_c32 { return nil } + if !_c33 { return nil } + if !_c34 { return nil } + if !_c35 { return nil } + if !_c36 { return nil } + if !_c37 { return nil } + if !_c38 { return nil } + if !_c39 { return nil } + return Api.UserFull.userFull(flags: _1!, flags2: _2!, id: _3!, about: _4, settings: _5!, personalPhoto: _6, profilePhoto: _7, fallbackPhoto: _8, notifySettings: _9!, botInfo: _10, pinnedMsgId: _11, commonChatsCount: _12!, folderId: _13, ttlPeriod: _14, theme: _15, privateForwardName: _16, botGroupAdminRights: _17, botBroadcastAdminRights: _18, wallpaper: _19, stories: _20, businessWorkHours: _21, businessLocation: _22, businessGreetingMessage: _23, businessAwayMessage: _24, businessIntro: _25, birthday: _26, personalChannelId: _27, personalChannelMessage: _28, stargiftsCount: _29, starrefProgram: _30, botVerification: _31, sendPaidMessagesStars: _32, disallowedGifts: _33, starsRating: _34, starsMyPendingRating: _35, starsMyPendingRatingDate: _36, mainTab: _37, savedMusic: _38, note: _39) } } @@ -899,12 +956,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.UserProfilePhoto.userProfilePhoto(flags: _1!, photoId: _2!, strippedThumb: _3, dcId: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.UserProfilePhoto.userProfilePhoto(flags: _1!, photoId: _2!, strippedThumb: _3, dcId: _4!) } public static func parse_userProfilePhotoEmpty(_ reader: BufferReader) -> UserProfilePhoto? { return Api.UserProfilePhoto.userProfilePhotoEmpty @@ -986,56 +1042,36 @@ public extension Api { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.UserStatus.userStatusLastMonth(flags: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.UserStatus.userStatusLastMonth(flags: _1!) } public static func parse_userStatusLastWeek(_ reader: BufferReader) -> UserStatus? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.UserStatus.userStatusLastWeek(flags: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.UserStatus.userStatusLastWeek(flags: _1!) } public static func parse_userStatusOffline(_ reader: BufferReader) -> UserStatus? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.UserStatus.userStatusOffline(wasOnline: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.UserStatus.userStatusOffline(wasOnline: _1!) } public static func parse_userStatusOnline(_ reader: BufferReader) -> UserStatus? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.UserStatus.userStatusOnline(expires: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.UserStatus.userStatusOnline(expires: _1!) } public static func parse_userStatusRecently(_ reader: BufferReader) -> UserStatus? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.UserStatus.userStatusRecently(flags: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.UserStatus.userStatusRecently(flags: _1!) } } @@ -1070,12 +1106,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Username.username(flags: _1!, username: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Username.username(flags: _1!, username: _2!) } } @@ -1155,12 +1188,13 @@ public extension Api { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.VideoSize.videoSize(flags: _1!, type: _2!, w: _3!, h: _4!, size: _5!, videoStartTs: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.VideoSize.videoSize(flags: _1!, type: _2!, w: _3!, h: _4!, size: _5!, videoStartTs: _6) } public static func parse_videoSizeEmojiMarkup(_ reader: BufferReader) -> VideoSize? { var _1: Int64? @@ -1171,12 +1205,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.VideoSize.videoSizeEmojiMarkup(emojiId: _1!, backgroundColors: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.VideoSize.videoSizeEmojiMarkup(emojiId: _1!, backgroundColors: _2!) } public static func parse_videoSizeStickerMarkup(_ reader: BufferReader) -> VideoSize? { var _1: Api.InputStickerSet? @@ -1192,12 +1223,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.VideoSize.videoSizeStickerMarkup(stickerset: _1!, stickerId: _2!, backgroundColors: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.VideoSize.videoSizeStickerMarkup(stickerset: _1!, stickerId: _2!, backgroundColors: _3!) } } @@ -1263,12 +1292,13 @@ public extension Api { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = (Int(_2!) & Int(1 << 2) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.WallPaper.wallPaper(id: _1!, flags: _2!, accessHash: _3!, slug: _4!, document: _5!, settings: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.WallPaper.wallPaper(id: _1!, flags: _2!, accessHash: _3!, slug: _4!, document: _5!, settings: _6) } public static func parse_wallPaperNoFile(_ reader: BufferReader) -> WallPaper? { var _1: Int64? @@ -1282,12 +1312,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_2!) & Int(1 << 2) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.WallPaper.wallPaperNoFile(id: _1!, flags: _2!, settings: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.WallPaper.wallPaperNoFile(id: _1!, flags: _2!, settings: _3) } } @@ -1346,12 +1374,15 @@ public extension Api { let _c6 = (Int(_1!) & Int(1 << 3) == 0) || _6 != nil let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil let _c8 = (Int(_1!) & Int(1 << 7) == 0) || _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.WallPaperSettings.wallPaperSettings(flags: _1!, backgroundColor: _2, secondBackgroundColor: _3, thirdBackgroundColor: _4, fourthBackgroundColor: _5, intensity: _6, rotation: _7, emoticon: _8) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.WallPaperSettings.wallPaperSettings(flags: _1!, backgroundColor: _2, secondBackgroundColor: _3, thirdBackgroundColor: _4, fourthBackgroundColor: _5, intensity: _6, rotation: _7, emoticon: _8) } } @@ -1414,12 +1445,16 @@ public extension Api { let _c7 = _7 != nil let _c8 = _8 != nil let _c9 = _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.WebAuthorization.webAuthorization(hash: _1!, botId: _2!, domain: _3!, browser: _4!, platform: _5!, dateCreated: _6!, dateActive: _7!, ip: _8!, region: _9!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + return Api.WebAuthorization.webAuthorization(hash: _1!, botId: _2!, domain: _3!, browser: _4!, platform: _5!, dateCreated: _6!, dateActive: _7!, ip: _8!, region: _9!) } } @@ -1488,12 +1523,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.WebDocument.webDocument(url: _1!, accessHash: _2!, size: _3!, mimeType: _4!, attributes: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.WebDocument.webDocument(url: _1!, accessHash: _2!, size: _3!, mimeType: _4!, attributes: _5!) } public static func parse_webDocumentNoProxy(_ reader: BufferReader) -> WebDocument? { var _1: String? @@ -1510,12 +1545,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.WebDocument.webDocumentNoProxy(url: _1!, size: _2!, mimeType: _3!, attributes: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.WebDocument.webDocumentNoProxy(url: _1!, size: _2!, mimeType: _3!, attributes: _4!) } } @@ -1663,12 +1697,26 @@ public extension Api { let _c17 = (Int(_1!) & Int(1 << 9) == 0) || _17 != nil let _c18 = (Int(_1!) & Int(1 << 10) == 0) || _18 != nil let _c19 = (Int(_1!) & Int(1 << 12) == 0) || _19 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 { - return Api.WebPage.webPage(flags: _1!, id: _2!, url: _3!, displayUrl: _4!, hash: _5!, type: _6, siteName: _7, title: _8, description: _9, photo: _10, embedUrl: _11, embedType: _12, embedWidth: _13, embedHeight: _14, duration: _15, author: _16, document: _17, cachedPage: _18, attributes: _19) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + if !_c13 { return nil } + if !_c14 { return nil } + if !_c15 { return nil } + if !_c16 { return nil } + if !_c17 { return nil } + if !_c18 { return nil } + if !_c19 { return nil } + return Api.WebPage.webPage(flags: _1!, id: _2!, url: _3!, displayUrl: _4!, hash: _5!, type: _6, siteName: _7, title: _8, description: _9, photo: _10, embedUrl: _11, embedType: _12, embedWidth: _13, embedHeight: _14, duration: _15, author: _16, document: _17, cachedPage: _18, attributes: _19) } public static func parse_webPageEmpty(_ reader: BufferReader) -> WebPage? { var _1: Int32? @@ -1680,12 +1728,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.WebPage.webPageEmpty(flags: _1!, id: _2!, url: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.WebPage.webPageEmpty(flags: _1!, id: _2!, url: _3) } public static func parse_webPageNotModified(_ reader: BufferReader) -> WebPage? { var _1: Int32? @@ -1694,12 +1740,9 @@ public extension Api { if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - if _c1 && _c2 { - return Api.WebPage.webPageNotModified(flags: _1!, cachedPageViews: _2) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.WebPage.webPageNotModified(flags: _1!, cachedPageViews: _2) } public static func parse_webPagePending(_ reader: BufferReader) -> WebPage? { var _1: Int32? @@ -1714,12 +1757,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.WebPage.webPagePending(flags: _1!, id: _2!, url: _3, date: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.WebPage.webPagePending(flags: _1!, id: _2!, url: _3, date: _4!) } } diff --git a/submodules/TelegramApi/Sources/Api29.swift b/submodules/TelegramApi/Sources/Api29.swift index 17bc8c5a..7814d89e 100644 --- a/submodules/TelegramApi/Sources/Api29.swift +++ b/submodules/TelegramApi/Sources/Api29.swift @@ -93,12 +93,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.WebPageAttribute.webPageAttributeStarGiftAuction(gift: _1!, endDate: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.WebPageAttribute.webPageAttributeStarGiftAuction(gift: _1!, endDate: _2!) } public static func parse_webPageAttributeStarGiftCollection(_ reader: BufferReader) -> WebPageAttribute? { var _1: [Api.Document]? @@ -106,12 +103,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) } let _c1 = _1 != nil - if _c1 { - return Api.WebPageAttribute.webPageAttributeStarGiftCollection(icons: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.WebPageAttribute.webPageAttributeStarGiftCollection(icons: _1!) } public static func parse_webPageAttributeStickerSet(_ reader: BufferReader) -> WebPageAttribute? { var _1: Int32? @@ -122,12 +115,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.WebPageAttribute.webPageAttributeStickerSet(flags: _1!, stickers: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.WebPageAttribute.webPageAttributeStickerSet(flags: _1!, stickers: _2!) } public static func parse_webPageAttributeStory(_ reader: BufferReader) -> WebPageAttribute? { var _1: Int32? @@ -146,12 +136,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.WebPageAttribute.webPageAttributeStory(flags: _1!, peer: _2!, id: _3!, story: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.WebPageAttribute.webPageAttributeStory(flags: _1!, peer: _2!, id: _3!, story: _4) } public static func parse_webPageAttributeTheme(_ reader: BufferReader) -> WebPageAttribute? { var _1: Int32? @@ -167,12 +156,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.WebPageAttribute.webPageAttributeTheme(flags: _1!, documents: _2, settings: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.WebPageAttribute.webPageAttributeTheme(flags: _1!, documents: _2, settings: _3) } public static func parse_webPageAttributeUniqueStarGift(_ reader: BufferReader) -> WebPageAttribute? { var _1: Api.StarGift? @@ -180,12 +167,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.StarGift } let _c1 = _1 != nil - if _c1 { - return Api.WebPageAttribute.webPageAttributeUniqueStarGift(gift: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.WebPageAttribute.webPageAttributeUniqueStarGift(gift: _1!) } } @@ -222,12 +205,9 @@ public extension Api { } } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - if _c1 && _c2 { - return Api.WebViewMessageSent.webViewMessageSent(flags: _1!, msgId: _2) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.WebViewMessageSent.webViewMessageSent(flags: _1!, msgId: _2) } } @@ -266,12 +246,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.WebViewResult.webViewResultUrl(flags: _1!, queryId: _2, url: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.WebViewResult.webViewResultUrl(flags: _1!, queryId: _2, url: _3!) } } @@ -346,12 +324,13 @@ public extension Api.account { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.account.AuthorizationForm.authorizationForm(flags: _1!, requiredTypes: _2!, values: _3!, errors: _4!, users: _5!, privacyPolicyUrl: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.account.AuthorizationForm.authorizationForm(flags: _1!, requiredTypes: _2!, values: _3!, errors: _4!, users: _5!, privacyPolicyUrl: _6) } } @@ -392,12 +371,9 @@ public extension Api.account { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.account.Authorizations.authorizations(authorizationTtlDays: _1!, authorizations: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.account.Authorizations.authorizations(authorizationTtlDays: _1!, authorizations: _2!) } } @@ -442,12 +418,10 @@ public extension Api.account { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.account.AutoDownloadSettings.autoDownloadSettings(low: _1!, medium: _2!, high: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.account.AutoDownloadSettings.autoDownloadSettings(low: _1!, medium: _2!, high: _3!) } } @@ -522,12 +496,13 @@ public extension Api.account { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.account.AutoSaveSettings.autoSaveSettings(usersSettings: _1!, chatsSettings: _2!, broadcastsSettings: _3!, exceptions: _4!, chats: _5!, users: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.account.AutoSaveSettings.autoSaveSettings(usersSettings: _1!, chatsSettings: _2!, broadcastsSettings: _3!, exceptions: _4!, chats: _5!, users: _6!) } } @@ -584,12 +559,10 @@ public extension Api.account { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.account.BusinessChatLinks.businessChatLinks(links: _1!, chats: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.account.BusinessChatLinks.businessChatLinks(links: _1!, chats: _2!, users: _3!) } } @@ -667,12 +640,13 @@ public extension Api.account { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.account.ChatThemes.chatThemes(flags: _1!, hash: _2!, themes: _3!, chats: _4!, users: _5!, nextOffset: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.account.ChatThemes.chatThemes(flags: _1!, hash: _2!, themes: _3!, chats: _4!, users: _5!, nextOffset: _6) } public static func parse_chatThemesNotModified(_ reader: BufferReader) -> ChatThemes? { return Api.account.ChatThemes.chatThemesNotModified @@ -722,12 +696,9 @@ public extension Api.account { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.account.ConnectedBots.connectedBots(connectedBots: _1!, users: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.account.ConnectedBots.connectedBots(connectedBots: _1!, users: _2!) } } @@ -758,12 +729,8 @@ public extension Api.account { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.account.ContentSettings.contentSettings(flags: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.account.ContentSettings.contentSettings(flags: _1!) } } @@ -804,12 +771,8 @@ public extension Api.account { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.account.EmailVerified.emailVerified(email: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.account.EmailVerified.emailVerified(email: _1!) } public static func parse_emailVerifiedLogin(_ reader: BufferReader) -> EmailVerified? { var _1: String? @@ -820,12 +783,9 @@ public extension Api.account { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.account.EmailVerified.emailVerifiedLogin(email: _1!, sentCode: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.account.EmailVerified.emailVerifiedLogin(email: _1!, sentCode: _2!) } } @@ -875,12 +835,9 @@ public extension Api.account { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.account.EmojiStatuses.emojiStatuses(hash: _1!, statuses: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.account.EmojiStatuses.emojiStatuses(hash: _1!, statuses: _2!) } public static func parse_emojiStatusesNotModified(_ reader: BufferReader) -> EmojiStatuses? { return Api.account.EmojiStatuses.emojiStatusesNotModified @@ -914,12 +871,8 @@ public extension Api.account { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.account.PaidMessagesRevenue.paidMessagesRevenue(starsAmount: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.account.PaidMessagesRevenue.paidMessagesRevenue(starsAmount: _1!) } } @@ -952,12 +905,8 @@ public extension Api.account { _1 = Api.parse(reader, signature: signature) as? Api.DataJSON } let _c1 = _1 != nil - if _c1 { - return Api.account.PasskeyRegistrationOptions.passkeyRegistrationOptions(options: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.account.PasskeyRegistrationOptions.passkeyRegistrationOptions(options: _1!) } } @@ -994,12 +943,8 @@ public extension Api.account { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Passkey.self) } let _c1 = _1 != nil - if _c1 { - return Api.account.Passkeys.passkeys(passkeys: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.account.Passkeys.passkeys(passkeys: _1!) } } @@ -1076,12 +1021,18 @@ public extension Api.account { let _c9 = _9 != nil let _c10 = (Int(_1!) & Int(1 << 5) == 0) || _10 != nil let _c11 = (Int(_1!) & Int(1 << 6) == 0) || _11 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 { - return Api.account.Password.password(flags: _1!, currentAlgo: _2, srpB: _3, srpId: _4, hint: _5, emailUnconfirmedPattern: _6, newAlgo: _7!, newSecureAlgo: _8!, secureRandom: _9!, pendingResetDate: _10, loginEmailPattern: _11) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + return Api.account.Password.password(flags: _1!, currentAlgo: _2, srpB: _3, srpId: _4, hint: _5, emailUnconfirmedPattern: _6, newAlgo: _7!, newSecureAlgo: _8!, secureRandom: _9!, pendingResetDate: _10, loginEmailPattern: _11) } } @@ -1136,12 +1087,13 @@ public extension Api.account { let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.account.PasswordInputSettings.passwordInputSettings(flags: _1!, newAlgo: _2, newPasswordHash: _3, hint: _4, email: _5, newSecureSettings: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.account.PasswordInputSettings.passwordInputSettings(flags: _1!, newAlgo: _2, newPasswordHash: _3, hint: _4, email: _5, newSecureSettings: _6) } } @@ -1182,12 +1134,10 @@ public extension Api.account { let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.account.PasswordSettings.passwordSettings(flags: _1!, email: _2, secureSettings: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.account.PasswordSettings.passwordSettings(flags: _1!, email: _2, secureSettings: _3) } } @@ -1244,12 +1194,10 @@ public extension Api.account { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.account.PrivacyRules.privacyRules(rules: _1!, chats: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.account.PrivacyRules.privacyRules(rules: _1!, chats: _2!, users: _3!) } } @@ -1298,12 +1246,8 @@ public extension Api.account { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.account.ResetPasswordResult.resetPasswordFailedWait(retryDate: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.account.ResetPasswordResult.resetPasswordFailedWait(retryDate: _1!) } public static func parse_resetPasswordOk(_ reader: BufferReader) -> ResetPasswordResult? { return Api.account.ResetPasswordResult.resetPasswordOk @@ -1312,12 +1256,8 @@ public extension Api.account { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.account.ResetPasswordResult.resetPasswordRequestedWait(untilDate: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.account.ResetPasswordResult.resetPasswordRequestedWait(untilDate: _1!) } } @@ -1388,12 +1328,13 @@ public extension Api.account { let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil let _c5 = _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.account.ResolvedBusinessChatLinks.resolvedBusinessChatLinks(flags: _1!, peer: _2!, message: _3!, entities: _4, chats: _5!, users: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.account.ResolvedBusinessChatLinks.resolvedBusinessChatLinks(flags: _1!, peer: _2!, message: _3!, entities: _4, chats: _5!, users: _6!) } } diff --git a/submodules/TelegramApi/Sources/Api3.swift b/submodules/TelegramApi/Sources/Api3.swift index 49988fc9..b71ce826 100644 --- a/submodules/TelegramApi/Sources/Api3.swift +++ b/submodules/TelegramApi/Sources/Api3.swift @@ -34,12 +34,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.BusinessGreetingMessage.businessGreetingMessage(shortcutId: _1!, recipients: _2!, noActivityDays: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.BusinessGreetingMessage.businessGreetingMessage(shortcutId: _1!, recipients: _2!, noActivityDays: _3!) } } @@ -84,12 +82,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.BusinessIntro.businessIntro(flags: _1!, title: _2!, description: _3!, sticker: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.BusinessIntro.businessIntro(flags: _1!, title: _2!, description: _3!, sticker: _4) } } @@ -130,12 +127,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.BusinessLocation.businessLocation(flags: _1!, geoPoint: _2, address: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.BusinessLocation.businessLocation(flags: _1!, geoPoint: _2, address: _3!) } } @@ -176,12 +171,9 @@ public extension Api { } } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 4) == 0) || _2 != nil - if _c1 && _c2 { - return Api.BusinessRecipients.businessRecipients(flags: _1!, users: _2) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.BusinessRecipients.businessRecipients(flags: _1!, users: _2) } } @@ -216,12 +208,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.BusinessWeeklyOpen.businessWeeklyOpen(startMinute: _1!, endMinute: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.BusinessWeeklyOpen.businessWeeklyOpen(startMinute: _1!, endMinute: _2!) } } @@ -266,12 +255,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.BusinessWorkHours.businessWorkHours(flags: _1!, timezoneId: _2!, weeklyOpen: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.BusinessWorkHours.businessWorkHours(flags: _1!, timezoneId: _2!, weeklyOpen: _3!) } } @@ -308,12 +295,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.CdnPublicKey.self) } let _c1 = _1 != nil - if _c1 { - return Api.CdnConfig.cdnConfig(publicKeys: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.CdnConfig.cdnConfig(publicKeys: _1!) } } @@ -348,12 +331,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.CdnPublicKey.cdnPublicKey(dcId: _1!, publicKey: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.CdnPublicKey.cdnPublicKey(dcId: _1!, publicKey: _2!) } } @@ -398,12 +378,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.ChannelAdminLogEvent.channelAdminLogEvent(id: _1!, date: _2!, userId: _3!, action: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.ChannelAdminLogEvent.channelAdminLogEvent(id: _1!, date: _2!, userId: _3!, action: _4!) } } @@ -922,12 +901,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeAbout(prevValue: _1!, newValue: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeAbout(prevValue: _1!, newValue: _2!) } public static func parse_channelAdminLogEventActionChangeAvailableReactions(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.ChatReactions? @@ -940,12 +916,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeAvailableReactions(prevValue: _1!, newValue: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeAvailableReactions(prevValue: _1!, newValue: _2!) } public static func parse_channelAdminLogEventActionChangeEmojiStatus(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.EmojiStatus? @@ -958,12 +931,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeEmojiStatus(prevValue: _1!, newValue: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeEmojiStatus(prevValue: _1!, newValue: _2!) } public static func parse_channelAdminLogEventActionChangeEmojiStickerSet(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.InputStickerSet? @@ -976,12 +946,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeEmojiStickerSet(prevStickerset: _1!, newStickerset: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeEmojiStickerSet(prevStickerset: _1!, newStickerset: _2!) } public static func parse_channelAdminLogEventActionChangeHistoryTTL(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Int32? @@ -990,12 +957,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeHistoryTTL(prevValue: _1!, newValue: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeHistoryTTL(prevValue: _1!, newValue: _2!) } public static func parse_channelAdminLogEventActionChangeLinkedChat(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Int64? @@ -1004,12 +968,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeLinkedChat(prevValue: _1!, newValue: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeLinkedChat(prevValue: _1!, newValue: _2!) } public static func parse_channelAdminLogEventActionChangeLocation(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.ChannelLocation? @@ -1022,12 +983,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeLocation(prevValue: _1!, newValue: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeLocation(prevValue: _1!, newValue: _2!) } public static func parse_channelAdminLogEventActionChangePeerColor(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.PeerColor? @@ -1040,12 +998,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangePeerColor(prevValue: _1!, newValue: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangePeerColor(prevValue: _1!, newValue: _2!) } public static func parse_channelAdminLogEventActionChangePhoto(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.Photo? @@ -1058,12 +1013,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangePhoto(prevPhoto: _1!, newPhoto: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangePhoto(prevPhoto: _1!, newPhoto: _2!) } public static func parse_channelAdminLogEventActionChangeProfilePeerColor(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.PeerColor? @@ -1076,12 +1028,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeProfilePeerColor(prevValue: _1!, newValue: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeProfilePeerColor(prevValue: _1!, newValue: _2!) } public static func parse_channelAdminLogEventActionChangeStickerSet(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.InputStickerSet? @@ -1094,12 +1043,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeStickerSet(prevStickerset: _1!, newStickerset: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeStickerSet(prevStickerset: _1!, newStickerset: _2!) } public static func parse_channelAdminLogEventActionChangeTitle(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: String? @@ -1108,12 +1054,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeTitle(prevValue: _1!, newValue: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeTitle(prevValue: _1!, newValue: _2!) } public static func parse_channelAdminLogEventActionChangeUsername(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: String? @@ -1122,12 +1065,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeUsername(prevValue: _1!, newValue: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeUsername(prevValue: _1!, newValue: _2!) } public static func parse_channelAdminLogEventActionChangeUsernames(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: [String]? @@ -1140,12 +1080,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeUsernames(prevValue: _1!, newValue: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeUsernames(prevValue: _1!, newValue: _2!) } public static func parse_channelAdminLogEventActionChangeWallpaper(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.WallPaper? @@ -1158,12 +1095,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeWallpaper(prevValue: _1!, newValue: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeWallpaper(prevValue: _1!, newValue: _2!) } public static func parse_channelAdminLogEventActionCreateTopic(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.ForumTopic? @@ -1171,12 +1105,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.ForumTopic } let _c1 = _1 != nil - if _c1 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionCreateTopic(topic: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionCreateTopic(topic: _1!) } public static func parse_channelAdminLogEventActionDefaultBannedRights(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.ChatBannedRights? @@ -1189,12 +1119,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionDefaultBannedRights(prevBannedRights: _1!, newBannedRights: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionDefaultBannedRights(prevBannedRights: _1!, newBannedRights: _2!) } public static func parse_channelAdminLogEventActionDeleteMessage(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.Message? @@ -1202,12 +1129,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Message } let _c1 = _1 != nil - if _c1 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionDeleteMessage(message: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionDeleteMessage(message: _1!) } public static func parse_channelAdminLogEventActionDeleteTopic(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.ForumTopic? @@ -1215,12 +1138,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.ForumTopic } let _c1 = _1 != nil - if _c1 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionDeleteTopic(topic: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionDeleteTopic(topic: _1!) } public static func parse_channelAdminLogEventActionDiscardGroupCall(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.InputGroupCall? @@ -1228,12 +1147,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.InputGroupCall } let _c1 = _1 != nil - if _c1 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionDiscardGroupCall(call: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionDiscardGroupCall(call: _1!) } public static func parse_channelAdminLogEventActionEditMessage(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.Message? @@ -1246,12 +1161,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionEditMessage(prevMessage: _1!, newMessage: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionEditMessage(prevMessage: _1!, newMessage: _2!) } public static func parse_channelAdminLogEventActionEditTopic(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.ForumTopic? @@ -1264,12 +1176,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionEditTopic(prevTopic: _1!, newTopic: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionEditTopic(prevTopic: _1!, newTopic: _2!) } public static func parse_channelAdminLogEventActionExportedInviteDelete(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.ExportedChatInvite? @@ -1277,12 +1186,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite } let _c1 = _1 != nil - if _c1 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionExportedInviteDelete(invite: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionExportedInviteDelete(invite: _1!) } public static func parse_channelAdminLogEventActionExportedInviteEdit(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.ExportedChatInvite? @@ -1295,12 +1200,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionExportedInviteEdit(prevInvite: _1!, newInvite: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionExportedInviteEdit(prevInvite: _1!, newInvite: _2!) } public static func parse_channelAdminLogEventActionExportedInviteRevoke(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.ExportedChatInvite? @@ -1308,12 +1210,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite } let _c1 = _1 != nil - if _c1 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionExportedInviteRevoke(invite: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionExportedInviteRevoke(invite: _1!) } public static func parse_channelAdminLogEventActionParticipantInvite(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.ChannelParticipant? @@ -1321,12 +1219,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.ChannelParticipant } let _c1 = _1 != nil - if _c1 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionParticipantInvite(participant: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionParticipantInvite(participant: _1!) } public static func parse_channelAdminLogEventActionParticipantJoin(_ reader: BufferReader) -> ChannelAdminLogEventAction? { return Api.ChannelAdminLogEventAction.channelAdminLogEventActionParticipantJoin @@ -1340,12 +1234,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionParticipantJoinByInvite(flags: _1!, invite: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionParticipantJoinByInvite(flags: _1!, invite: _2!) } public static func parse_channelAdminLogEventActionParticipantJoinByRequest(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.ExportedChatInvite? @@ -1356,12 +1247,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionParticipantJoinByRequest(invite: _1!, approvedBy: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionParticipantJoinByRequest(invite: _1!, approvedBy: _2!) } public static func parse_channelAdminLogEventActionParticipantLeave(_ reader: BufferReader) -> ChannelAdminLogEventAction? { return Api.ChannelAdminLogEventAction.channelAdminLogEventActionParticipantLeave @@ -1372,12 +1260,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.GroupCallParticipant } let _c1 = _1 != nil - if _c1 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionParticipantMute(participant: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionParticipantMute(participant: _1!) } public static func parse_channelAdminLogEventActionParticipantSubExtend(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.ChannelParticipant? @@ -1390,12 +1274,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionParticipantSubExtend(prevParticipant: _1!, newParticipant: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionParticipantSubExtend(prevParticipant: _1!, newParticipant: _2!) } public static func parse_channelAdminLogEventActionParticipantToggleAdmin(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.ChannelParticipant? @@ -1408,12 +1289,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionParticipantToggleAdmin(prevParticipant: _1!, newParticipant: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionParticipantToggleAdmin(prevParticipant: _1!, newParticipant: _2!) } public static func parse_channelAdminLogEventActionParticipantToggleBan(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.ChannelParticipant? @@ -1426,12 +1304,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionParticipantToggleBan(prevParticipant: _1!, newParticipant: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionParticipantToggleBan(prevParticipant: _1!, newParticipant: _2!) } public static func parse_channelAdminLogEventActionParticipantUnmute(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.GroupCallParticipant? @@ -1439,12 +1314,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.GroupCallParticipant } let _c1 = _1 != nil - if _c1 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionParticipantUnmute(participant: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionParticipantUnmute(participant: _1!) } public static func parse_channelAdminLogEventActionParticipantVolume(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.GroupCallParticipant? @@ -1452,12 +1323,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.GroupCallParticipant } let _c1 = _1 != nil - if _c1 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionParticipantVolume(participant: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionParticipantVolume(participant: _1!) } public static func parse_channelAdminLogEventActionPinTopic(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Int32? @@ -1473,12 +1340,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionPinTopic(flags: _1!, prevTopic: _2, newTopic: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionPinTopic(flags: _1!, prevTopic: _2, newTopic: _3) } public static func parse_channelAdminLogEventActionSendMessage(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.Message? @@ -1486,12 +1351,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Message } let _c1 = _1 != nil - if _c1 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionSendMessage(message: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionSendMessage(message: _1!) } public static func parse_channelAdminLogEventActionStartGroupCall(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.InputGroupCall? @@ -1499,12 +1360,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.InputGroupCall } let _c1 = _1 != nil - if _c1 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionStartGroupCall(call: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionStartGroupCall(call: _1!) } public static func parse_channelAdminLogEventActionStopPoll(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.Message? @@ -1512,12 +1369,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Message } let _c1 = _1 != nil - if _c1 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionStopPoll(message: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionStopPoll(message: _1!) } public static func parse_channelAdminLogEventActionToggleAntiSpam(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.Bool? @@ -1525,12 +1378,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Bool } let _c1 = _1 != nil - if _c1 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionToggleAntiSpam(newValue: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionToggleAntiSpam(newValue: _1!) } public static func parse_channelAdminLogEventActionToggleAutotranslation(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.Bool? @@ -1538,12 +1387,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Bool } let _c1 = _1 != nil - if _c1 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionToggleAutotranslation(newValue: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionToggleAutotranslation(newValue: _1!) } public static func parse_channelAdminLogEventActionToggleForum(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.Bool? @@ -1551,12 +1396,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Bool } let _c1 = _1 != nil - if _c1 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionToggleForum(newValue: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionToggleForum(newValue: _1!) } public static func parse_channelAdminLogEventActionToggleGroupCallSetting(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.Bool? @@ -1564,12 +1405,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Bool } let _c1 = _1 != nil - if _c1 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionToggleGroupCallSetting(joinMuted: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionToggleGroupCallSetting(joinMuted: _1!) } public static func parse_channelAdminLogEventActionToggleInvites(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.Bool? @@ -1577,12 +1414,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Bool } let _c1 = _1 != nil - if _c1 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionToggleInvites(newValue: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionToggleInvites(newValue: _1!) } public static func parse_channelAdminLogEventActionToggleNoForwards(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.Bool? @@ -1590,12 +1423,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Bool } let _c1 = _1 != nil - if _c1 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionToggleNoForwards(newValue: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionToggleNoForwards(newValue: _1!) } public static func parse_channelAdminLogEventActionTogglePreHistoryHidden(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.Bool? @@ -1603,12 +1432,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Bool } let _c1 = _1 != nil - if _c1 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionTogglePreHistoryHidden(newValue: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionTogglePreHistoryHidden(newValue: _1!) } public static func parse_channelAdminLogEventActionToggleSignatureProfiles(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.Bool? @@ -1616,12 +1441,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Bool } let _c1 = _1 != nil - if _c1 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionToggleSignatureProfiles(newValue: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionToggleSignatureProfiles(newValue: _1!) } public static func parse_channelAdminLogEventActionToggleSignatures(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.Bool? @@ -1629,12 +1450,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Bool } let _c1 = _1 != nil - if _c1 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionToggleSignatures(newValue: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionToggleSignatures(newValue: _1!) } public static func parse_channelAdminLogEventActionToggleSlowMode(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Int32? @@ -1643,12 +1460,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionToggleSlowMode(prevValue: _1!, newValue: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionToggleSlowMode(prevValue: _1!, newValue: _2!) } public static func parse_channelAdminLogEventActionUpdatePinned(_ reader: BufferReader) -> ChannelAdminLogEventAction? { var _1: Api.Message? @@ -1656,12 +1470,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Message } let _c1 = _1 != nil - if _c1 { - return Api.ChannelAdminLogEventAction.channelAdminLogEventActionUpdatePinned(message: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelAdminLogEventAction.channelAdminLogEventActionUpdatePinned(message: _1!) } } diff --git a/submodules/TelegramApi/Sources/Api30.swift b/submodules/TelegramApi/Sources/Api30.swift index 0d25527d..b10d58f7 100644 --- a/submodules/TelegramApi/Sources/Api30.swift +++ b/submodules/TelegramApi/Sources/Api30.swift @@ -39,12 +39,8 @@ public extension Api.account { _1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) } let _c1 = _1 != nil - if _c1 { - return Api.account.SavedMusicIds.savedMusicIds(ids: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.account.SavedMusicIds.savedMusicIds(ids: _1!) } public static func parse_savedMusicIdsNotModified(_ reader: BufferReader) -> SavedMusicIds? { return Api.account.SavedMusicIds.savedMusicIdsNotModified @@ -92,12 +88,8 @@ public extension Api.account { _1 = Api.parse(reader, signature: signature) as? Api.Document } let _c1 = _1 != nil - if _c1 { - return Api.account.SavedRingtone.savedRingtoneConverted(document: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.account.SavedRingtone.savedRingtoneConverted(document: _1!) } } @@ -147,12 +139,9 @@ public extension Api.account { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.account.SavedRingtones.savedRingtones(hash: _1!, ringtones: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.account.SavedRingtones.savedRingtones(hash: _1!, ringtones: _2!) } public static func parse_savedRingtonesNotModified(_ reader: BufferReader) -> SavedRingtones? { return Api.account.SavedRingtones.savedRingtonesNotModified @@ -190,12 +179,9 @@ public extension Api.account { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.account.SentEmailCode.sentEmailCode(emailPattern: _1!, length: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.account.SentEmailCode.sentEmailCode(emailPattern: _1!, length: _2!) } } @@ -226,12 +212,8 @@ public extension Api.account { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.account.Takeout.takeout(id: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.account.Takeout.takeout(id: _1!) } } @@ -281,12 +263,9 @@ public extension Api.account { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.account.Themes.themes(hash: _1!, themes: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.account.Themes.themes(hash: _1!, themes: _2!) } public static func parse_themesNotModified(_ reader: BufferReader) -> Themes? { return Api.account.Themes.themesNotModified @@ -324,12 +303,9 @@ public extension Api.account { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.account.TmpPassword.tmpPassword(tmpPassword: _1!, validUntil: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.account.TmpPassword.tmpPassword(tmpPassword: _1!, validUntil: _2!) } } @@ -379,12 +355,9 @@ public extension Api.account { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.account.WallPapers.wallPapers(hash: _1!, wallpapers: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.account.WallPapers.wallPapers(hash: _1!, wallpapers: _2!) } public static func parse_wallPapersNotModified(_ reader: BufferReader) -> WallPapers? { return Api.account.WallPapers.wallPapersNotModified @@ -434,12 +407,9 @@ public extension Api.account { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.account.WebAuthorizations.webAuthorizations(authorizations: _1!, users: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.account.WebAuthorizations.webAuthorizations(authorizations: _1!, users: _2!) } } @@ -498,12 +468,12 @@ public extension Api.auth { let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.auth.Authorization.authorization(flags: _1!, otherwiseReloginDays: _2, tmpSessions: _3, futureAuthToken: _4, user: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.auth.Authorization.authorization(flags: _1!, otherwiseReloginDays: _2, tmpSessions: _3, futureAuthToken: _4, user: _5!) } public static func parse_authorizationSignUpRequired(_ reader: BufferReader) -> Authorization? { var _1: Int32? @@ -514,12 +484,9 @@ public extension Api.auth { } } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - if _c1 && _c2 { - return Api.auth.Authorization.authorizationSignUpRequired(flags: _1!, termsOfService: _2) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.auth.Authorization.authorizationSignUpRequired(flags: _1!, termsOfService: _2) } } @@ -630,12 +597,9 @@ public extension Api.auth { _2 = parseBytes(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.auth.ExportedAuthorization.exportedAuthorization(id: _1!, bytes: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.auth.ExportedAuthorization.exportedAuthorization(id: _1!, bytes: _2!) } } @@ -670,12 +634,9 @@ public extension Api.auth { if Int(_1!) & Int(1 << 0) != 0 {_2 = parseBytes(reader) } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - if _c1 && _c2 { - return Api.auth.LoggedOut.loggedOut(flags: _1!, futureAuthToken: _2) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.auth.LoggedOut.loggedOut(flags: _1!, futureAuthToken: _2) } } @@ -729,12 +690,9 @@ public extension Api.auth { _2 = parseBytes(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.auth.LoginToken.loginToken(expires: _1!, token: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.auth.LoginToken.loginToken(expires: _1!, token: _2!) } public static func parse_loginTokenMigrateTo(_ reader: BufferReader) -> LoginToken? { var _1: Int32? @@ -743,12 +701,9 @@ public extension Api.auth { _2 = parseBytes(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.auth.LoginToken.loginTokenMigrateTo(dcId: _1!, token: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.auth.LoginToken.loginTokenMigrateTo(dcId: _1!, token: _2!) } public static func parse_loginTokenSuccess(_ reader: BufferReader) -> LoginToken? { var _1: Api.auth.Authorization? @@ -756,12 +711,8 @@ public extension Api.auth { _1 = Api.parse(reader, signature: signature) as? Api.auth.Authorization } let _c1 = _1 != nil - if _c1 { - return Api.auth.LoginToken.loginTokenSuccess(authorization: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.auth.LoginToken.loginTokenSuccess(authorization: _1!) } } @@ -794,12 +745,8 @@ public extension Api.auth { _1 = Api.parse(reader, signature: signature) as? Api.DataJSON } let _c1 = _1 != nil - if _c1 { - return Api.auth.PasskeyLoginOptions.passkeyLoginOptions(options: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.auth.PasskeyLoginOptions.passkeyLoginOptions(options: _1!) } } @@ -830,12 +777,8 @@ public extension Api.auth { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.auth.PasswordRecovery.passwordRecovery(emailPattern: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.auth.PasswordRecovery.passwordRecovery(emailPattern: _1!) } } @@ -909,12 +852,12 @@ public extension Api.auth { let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.auth.SentCode.sentCode(flags: _1!, type: _2!, phoneCodeHash: _3!, nextType: _4, timeout: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.auth.SentCode.sentCode(flags: _1!, type: _2!, phoneCodeHash: _3!, nextType: _4, timeout: _5) } public static func parse_sentCodePaymentRequired(_ reader: BufferReader) -> SentCode? { var _1: String? @@ -935,12 +878,13 @@ public extension Api.auth { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.auth.SentCode.sentCodePaymentRequired(storeProduct: _1!, phoneCodeHash: _2!, supportEmailAddress: _3!, supportEmailSubject: _4!, currency: _5!, amount: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.auth.SentCode.sentCodePaymentRequired(storeProduct: _1!, phoneCodeHash: _2!, supportEmailAddress: _3!, supportEmailSubject: _4!, currency: _5!, amount: _6!) } public static func parse_sentCodeSuccess(_ reader: BufferReader) -> SentCode? { var _1: Api.auth.Authorization? @@ -948,12 +892,8 @@ public extension Api.auth { _1 = Api.parse(reader, signature: signature) as? Api.auth.Authorization } let _c1 = _1 != nil - if _c1 { - return Api.auth.SentCode.sentCodeSuccess(authorization: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.auth.SentCode.sentCodeSuccess(authorization: _1!) } } @@ -1088,23 +1028,15 @@ public extension Api.auth { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.auth.SentCodeType.sentCodeTypeApp(length: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.auth.SentCodeType.sentCodeTypeApp(length: _1!) } public static func parse_sentCodeTypeCall(_ reader: BufferReader) -> SentCodeType? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.auth.SentCodeType.sentCodeTypeCall(length: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.auth.SentCodeType.sentCodeTypeCall(length: _1!) } public static func parse_sentCodeTypeEmailCode(_ reader: BufferReader) -> SentCodeType? { var _1: Int32? @@ -1122,12 +1054,12 @@ public extension Api.auth { let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 3) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 4) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.auth.SentCodeType.sentCodeTypeEmailCode(flags: _1!, emailPattern: _2!, length: _3!, resetAvailablePeriod: _4, resetPendingDate: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.auth.SentCodeType.sentCodeTypeEmailCode(flags: _1!, emailPattern: _2!, length: _3!, resetAvailablePeriod: _4, resetPendingDate: _5) } public static func parse_sentCodeTypeFirebaseSms(_ reader: BufferReader) -> SentCodeType? { var _1: Int32? @@ -1151,23 +1083,21 @@ public extension Api.auth { let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.auth.SentCodeType.sentCodeTypeFirebaseSms(flags: _1!, nonce: _2, playIntegrityProjectId: _3, playIntegrityNonce: _4, receipt: _5, pushTimeout: _6, length: _7!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.auth.SentCodeType.sentCodeTypeFirebaseSms(flags: _1!, nonce: _2, playIntegrityProjectId: _3, playIntegrityNonce: _4, receipt: _5, pushTimeout: _6, length: _7!) } public static func parse_sentCodeTypeFlashCall(_ reader: BufferReader) -> SentCodeType? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.auth.SentCodeType.sentCodeTypeFlashCall(pattern: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.auth.SentCodeType.sentCodeTypeFlashCall(pattern: _1!) } public static func parse_sentCodeTypeFragmentSms(_ reader: BufferReader) -> SentCodeType? { var _1: String? @@ -1176,12 +1106,9 @@ public extension Api.auth { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.auth.SentCodeType.sentCodeTypeFragmentSms(url: _1!, length: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.auth.SentCodeType.sentCodeTypeFragmentSms(url: _1!, length: _2!) } public static func parse_sentCodeTypeMissedCall(_ reader: BufferReader) -> SentCodeType? { var _1: String? @@ -1190,34 +1117,23 @@ public extension Api.auth { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.auth.SentCodeType.sentCodeTypeMissedCall(prefix: _1!, length: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.auth.SentCodeType.sentCodeTypeMissedCall(prefix: _1!, length: _2!) } public static func parse_sentCodeTypeSetUpEmailRequired(_ reader: BufferReader) -> SentCodeType? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.auth.SentCodeType.sentCodeTypeSetUpEmailRequired(flags: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.auth.SentCodeType.sentCodeTypeSetUpEmailRequired(flags: _1!) } public static func parse_sentCodeTypeSms(_ reader: BufferReader) -> SentCodeType? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.auth.SentCodeType.sentCodeTypeSms(length: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.auth.SentCodeType.sentCodeTypeSms(length: _1!) } public static func parse_sentCodeTypeSmsPhrase(_ reader: BufferReader) -> SentCodeType? { var _1: Int32? @@ -1226,12 +1142,9 @@ public extension Api.auth { if Int(_1!) & Int(1 << 0) != 0 {_2 = parseString(reader) } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - if _c1 && _c2 { - return Api.auth.SentCodeType.sentCodeTypeSmsPhrase(flags: _1!, beginning: _2) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.auth.SentCodeType.sentCodeTypeSmsPhrase(flags: _1!, beginning: _2) } public static func parse_sentCodeTypeSmsWord(_ reader: BufferReader) -> SentCodeType? { var _1: Int32? @@ -1240,12 +1153,9 @@ public extension Api.auth { if Int(_1!) & Int(1 << 0) != 0 {_2 = parseString(reader) } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - if _c1 && _c2 { - return Api.auth.SentCodeType.sentCodeTypeSmsWord(flags: _1!, beginning: _2) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.auth.SentCodeType.sentCodeTypeSmsWord(flags: _1!, beginning: _2) } } diff --git a/submodules/TelegramApi/Sources/Api31.swift b/submodules/TelegramApi/Sources/Api31.swift index b35e3904..d713950b 100644 --- a/submodules/TelegramApi/Sources/Api31.swift +++ b/submodules/TelegramApi/Sources/Api31.swift @@ -32,12 +32,10 @@ public extension Api.bots { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.bots.BotInfo.botInfo(name: _1!, about: _2!, description: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.bots.BotInfo.botInfo(name: _1!, about: _2!, description: _3!) } } @@ -82,12 +80,10 @@ public extension Api.bots { let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.bots.PopularAppBots.popularAppBots(flags: _1!, nextOffset: _2, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.bots.PopularAppBots.popularAppBots(flags: _1!, nextOffset: _2, users: _3!) } } @@ -134,12 +130,9 @@ public extension Api.bots { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.bots.PreviewInfo.previewInfo(media: _1!, langCodes: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.bots.PreviewInfo.previewInfo(media: _1!, langCodes: _2!) } } @@ -196,12 +189,10 @@ public extension Api.channels { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.channels.AdminLogResults.adminLogResults(events: _1!, chats: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.channels.AdminLogResults.adminLogResults(events: _1!, chats: _2!, users: _3!) } } @@ -254,12 +245,10 @@ public extension Api.channels { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.channels.ChannelParticipant.channelParticipant(participant: _1!, chats: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.channels.ChannelParticipant.channelParticipant(participant: _1!, chats: _2!, users: _3!) } } @@ -329,12 +318,11 @@ public extension Api.channels { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.channels.ChannelParticipants.channelParticipants(count: _1!, participants: _2!, chats: _3!, users: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.channels.ChannelParticipants.channelParticipants(count: _1!, participants: _2!, chats: _3!, users: _4!) } public static func parse_channelParticipantsNotModified(_ reader: BufferReader) -> ChannelParticipants? { return Api.channels.ChannelParticipants.channelParticipantsNotModified @@ -394,12 +382,10 @@ public extension Api.channels { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.channels.SendAsPeers.sendAsPeers(peers: _1!, chats: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.channels.SendAsPeers.sendAsPeers(peers: _1!, chats: _2!, users: _3!) } } @@ -461,12 +447,9 @@ public extension Api.channels { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.channels.SponsoredMessageReportResult.sponsoredMessageReportResultChooseOption(title: _1!, options: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.channels.SponsoredMessageReportResult.sponsoredMessageReportResultChooseOption(title: _1!, options: _2!) } public static func parse_sponsoredMessageReportResultReported(_ reader: BufferReader) -> SponsoredMessageReportResult? { return Api.channels.SponsoredMessageReportResult.sponsoredMessageReportResultReported @@ -569,12 +552,13 @@ public extension Api.chatlists { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.chatlists.ChatlistInvite.chatlistInvite(flags: _1!, title: _2!, emoticon: _3, peers: _4!, chats: _5!, users: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.chatlists.ChatlistInvite.chatlistInvite(flags: _1!, title: _2!, emoticon: _3, peers: _4!, chats: _5!, users: _6!) } public static func parse_chatlistInviteAlready(_ reader: BufferReader) -> ChatlistInvite? { var _1: Int32? @@ -600,12 +584,12 @@ public extension Api.chatlists { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.chatlists.ChatlistInvite.chatlistInviteAlready(filterId: _1!, missingPeers: _2!, alreadyPeers: _3!, chats: _4!, users: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.chatlists.ChatlistInvite.chatlistInviteAlready(filterId: _1!, missingPeers: _2!, alreadyPeers: _3!, chats: _4!, users: _5!) } } @@ -662,12 +646,10 @@ public extension Api.chatlists { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.chatlists.ChatlistUpdates.chatlistUpdates(missingPeers: _1!, chats: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.chatlists.ChatlistUpdates.chatlistUpdates(missingPeers: _1!, chats: _2!, users: _3!) } } @@ -706,12 +688,9 @@ public extension Api.chatlists { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.chatlists.ExportedChatlistInvite.exportedChatlistInvite(filter: _1!, invite: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.chatlists.ExportedChatlistInvite.exportedChatlistInvite(filter: _1!, invite: _2!) } } @@ -768,12 +747,10 @@ public extension Api.chatlists { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.chatlists.ExportedInvites.exportedInvites(invites: _1!, chats: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.chatlists.ExportedInvites.exportedInvites(invites: _1!, chats: _2!, users: _3!) } } @@ -854,12 +831,10 @@ public extension Api.contacts { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.contacts.Blocked.blocked(blocked: _1!, chats: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.contacts.Blocked.blocked(blocked: _1!, chats: _2!, users: _3!) } public static func parse_blockedSlice(_ reader: BufferReader) -> Blocked? { var _1: Int32? @@ -880,12 +855,11 @@ public extension Api.contacts { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.contacts.Blocked.blockedSlice(count: _1!, blocked: _2!, chats: _3!, users: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.contacts.Blocked.blockedSlice(count: _1!, blocked: _2!, chats: _3!, users: _4!) } } @@ -932,12 +906,9 @@ public extension Api.contacts { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.contacts.ContactBirthdays.contactBirthdays(contacts: _1!, users: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.contacts.ContactBirthdays.contactBirthdays(contacts: _1!, users: _2!) } } @@ -997,12 +968,10 @@ public extension Api.contacts { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.contacts.Contacts.contacts(contacts: _1!, savedCount: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.contacts.Contacts.contacts(contacts: _1!, savedCount: _2!, users: _3!) } public static func parse_contactsNotModified(_ reader: BufferReader) -> Contacts? { return Api.contacts.Contacts.contactsNotModified @@ -1072,12 +1041,11 @@ public extension Api.contacts { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.contacts.Found.found(myResults: _1!, results: _2!, chats: _3!, users: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.contacts.Found.found(myResults: _1!, results: _2!, chats: _3!, users: _4!) } } @@ -1144,12 +1112,11 @@ public extension Api.contacts { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.contacts.ImportedContacts.importedContacts(imported: _1!, popularInvites: _2!, retryContacts: _3!, users: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.contacts.ImportedContacts.importedContacts(imported: _1!, popularInvites: _2!, retryContacts: _3!, users: _4!) } } @@ -1202,12 +1169,10 @@ public extension Api.contacts { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.contacts.ResolvedPeer.resolvedPeer(peer: _1!, chats: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.contacts.ResolvedPeer.resolvedPeer(peer: _1!, chats: _2!, users: _3!) } } @@ -1273,12 +1238,10 @@ public extension Api.contacts { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.contacts.SponsoredPeers.sponsoredPeers(peers: _1!, chats: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.contacts.SponsoredPeers.sponsoredPeers(peers: _1!, chats: _2!, users: _3!) } public static func parse_sponsoredPeersEmpty(_ reader: BufferReader) -> SponsoredPeers? { return Api.contacts.SponsoredPeers.sponsoredPeersEmpty @@ -1356,12 +1319,10 @@ public extension Api.contacts { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.contacts.TopPeers.topPeers(categories: _1!, chats: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.contacts.TopPeers.topPeers(categories: _1!, chats: _2!, users: _3!) } public static func parse_topPeersDisabled(_ reader: BufferReader) -> TopPeers? { return Api.contacts.TopPeers.topPeersDisabled @@ -1418,12 +1379,13 @@ public extension Api.fragment { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.fragment.CollectibleInfo.collectibleInfo(purchaseDate: _1!, currency: _2!, amount: _3!, cryptoCurrency: _4!, cryptoAmount: _5!, url: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.fragment.CollectibleInfo.collectibleInfo(purchaseDate: _1!, currency: _2!, amount: _3!, cryptoCurrency: _4!, cryptoAmount: _5!, url: _6!) } } @@ -1469,12 +1431,9 @@ public extension Api.help { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.help.AppConfig.appConfig(hash: _1!, config: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.help.AppConfig.appConfig(hash: _1!, config: _2!) } public static func parse_appConfigNotModified(_ reader: BufferReader) -> AppConfig? { return Api.help.AppConfig.appConfigNotModified diff --git a/submodules/TelegramApi/Sources/Api32.swift b/submodules/TelegramApi/Sources/Api32.swift index a9067b18..5649eb9f 100644 --- a/submodules/TelegramApi/Sources/Api32.swift +++ b/submodules/TelegramApi/Sources/Api32.swift @@ -71,12 +71,15 @@ public extension Api.help { let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil let _c8 = (Int(_1!) & Int(1 << 3) == 0) || _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.help.AppUpdate.appUpdate(flags: _1!, id: _2!, version: _3!, text: _4!, entities: _5!, document: _6, url: _7, sticker: _8) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.help.AppUpdate.appUpdate(flags: _1!, id: _2!, version: _3!, text: _4!, entities: _5!, document: _6, url: _7, sticker: _8) } public static func parse_noAppUpdate(_ reader: BufferReader) -> AppUpdate? { return Api.help.AppUpdate.noAppUpdate @@ -129,12 +132,9 @@ public extension Api.help { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.help.CountriesList.countriesList(countries: _1!, hash: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.help.CountriesList.countriesList(countries: _1!, hash: _2!) } public static func parse_countriesListNotModified(_ reader: BufferReader) -> CountriesList? { return Api.help.CountriesList.countriesListNotModified @@ -190,12 +190,12 @@ public extension Api.help { let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.help.Country.country(flags: _1!, iso2: _2!, defaultName: _3!, name: _4, countryCodes: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.help.Country.country(flags: _1!, iso2: _2!, defaultName: _3!, name: _4, countryCodes: _5!) } } @@ -250,12 +250,11 @@ public extension Api.help { let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.help.CountryCode.countryCode(flags: _1!, countryCode: _2!, prefixes: _3, patterns: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.help.CountryCode.countryCode(flags: _1!, countryCode: _2!, prefixes: _3, patterns: _4) } } @@ -309,12 +308,10 @@ public extension Api.help { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.help.DeepLinkInfo.deepLinkInfo(flags: _1!, message: _2!, entities: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.help.DeepLinkInfo.deepLinkInfo(flags: _1!, message: _2!, entities: _3) } public static func parse_deepLinkInfoEmpty(_ reader: BufferReader) -> DeepLinkInfo? { return Api.help.DeepLinkInfo.deepLinkInfoEmpty @@ -348,12 +345,8 @@ public extension Api.help { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.help.InviteText.inviteText(message: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.help.InviteText.inviteText(message: _1!) } } @@ -399,12 +392,9 @@ public extension Api.help { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.help.PassportConfig.passportConfig(hash: _1!, countriesLangs: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.help.PassportConfig.passportConfig(hash: _1!, countriesLangs: _2!) } public static func parse_passportConfigNotModified(_ reader: BufferReader) -> PassportConfig? { return Api.help.PassportConfig.passportConfigNotModified @@ -462,12 +452,13 @@ public extension Api.help { let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 4) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.help.PeerColorOption.peerColorOption(flags: _1!, colorId: _2!, colors: _3, darkColors: _4, channelMinLevel: _5, groupMinLevel: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.help.PeerColorOption.peerColorOption(flags: _1!, colorId: _2!, colors: _3, darkColors: _4, channelMinLevel: _5, groupMinLevel: _6) } } @@ -537,12 +528,10 @@ public extension Api.help { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.help.PeerColorSet.peerColorProfileSet(paletteColors: _1!, bgColors: _2!, storyColors: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.help.PeerColorSet.peerColorProfileSet(paletteColors: _1!, bgColors: _2!, storyColors: _3!) } public static func parse_peerColorSet(_ reader: BufferReader) -> PeerColorSet? { var _1: [Int32]? @@ -550,12 +539,8 @@ public extension Api.help { _1 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) } let _c1 = _1 != nil - if _c1 { - return Api.help.PeerColorSet.peerColorSet(colors: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.help.PeerColorSet.peerColorSet(colors: _1!) } } @@ -605,12 +590,9 @@ public extension Api.help { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.help.PeerColors.peerColors(hash: _1!, colors: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.help.PeerColors.peerColors(hash: _1!, colors: _2!) } public static func parse_peerColorsNotModified(_ reader: BufferReader) -> PeerColors? { return Api.help.PeerColors.peerColorsNotModified @@ -694,12 +676,13 @@ public extension Api.help { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.help.PremiumPromo.premiumPromo(statusText: _1!, statusEntities: _2!, videoSections: _3!, videos: _4!, periodOptions: _5!, users: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.help.PremiumPromo.premiumPromo(statusText: _1!, statusEntities: _2!, videoSections: _3!, videos: _4!, periodOptions: _5!, users: _6!) } } @@ -803,23 +786,24 @@ public extension Api.help { let _c8 = (Int(_1!) & Int(1 << 4) == 0) || _8 != nil let _c9 = _9 != nil let _c10 = _10 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 { - return Api.help.PromoData.promoData(flags: _1!, expires: _2!, peer: _3, psaType: _4, psaMessage: _5, pendingSuggestions: _6!, dismissedSuggestions: _7!, customPendingSuggestion: _8, chats: _9!, users: _10!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + return Api.help.PromoData.promoData(flags: _1!, expires: _2!, peer: _3, psaType: _4, psaMessage: _5, pendingSuggestions: _6!, dismissedSuggestions: _7!, customPendingSuggestion: _8, chats: _9!, users: _10!) } public static func parse_promoDataEmpty(_ reader: BufferReader) -> PromoData? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.help.PromoData.promoDataEmpty(expires: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.help.PromoData.promoDataEmpty(expires: _1!) } } @@ -876,12 +860,10 @@ public extension Api.help { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.help.RecentMeUrls.recentMeUrls(urls: _1!, chats: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.help.RecentMeUrls.recentMeUrls(urls: _1!, chats: _2!, users: _3!) } } @@ -918,12 +900,9 @@ public extension Api.help { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.help.Support.support(phoneNumber: _1!, user: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.help.Support.support(phoneNumber: _1!, user: _2!) } } @@ -954,12 +933,8 @@ public extension Api.help { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.help.SupportName.supportName(name: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.help.SupportName.supportName(name: _1!) } } @@ -1014,12 +989,12 @@ public extension Api.help { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.help.TermsOfService.termsOfService(flags: _1!, id: _2!, text: _3!, entities: _4!, minAgeConfirm: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.help.TermsOfService.termsOfService(flags: _1!, id: _2!, text: _3!, entities: _4!, minAgeConfirm: _5) } } @@ -1065,23 +1040,16 @@ public extension Api.help { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.help.TermsOfServiceUpdate.termsOfServiceUpdate(expires: _1!, termsOfService: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.help.TermsOfServiceUpdate.termsOfServiceUpdate(expires: _1!, termsOfService: _2!) } public static func parse_termsOfServiceUpdateEmpty(_ reader: BufferReader) -> TermsOfServiceUpdate? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.help.TermsOfServiceUpdate.termsOfServiceUpdateEmpty(expires: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.help.TermsOfServiceUpdate.termsOfServiceUpdateEmpty(expires: _1!) } } @@ -1131,12 +1099,9 @@ public extension Api.help { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.help.TimezonesList.timezonesList(timezones: _1!, hash: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.help.TimezonesList.timezonesList(timezones: _1!, hash: _2!) } public static func parse_timezonesListNotModified(_ reader: BufferReader) -> TimezonesList? { return Api.help.TimezonesList.timezonesListNotModified @@ -1197,12 +1162,11 @@ public extension Api.help { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.help.UserInfo.userInfo(message: _1!, entities: _2!, author: _3!, date: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.help.UserInfo.userInfo(message: _1!, entities: _2!, author: _3!, date: _4!) } public static func parse_userInfoEmpty(_ reader: BufferReader) -> UserInfo? { return Api.help.UserInfo.userInfoEmpty @@ -1254,12 +1218,11 @@ public extension Api.messages { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.messages.AffectedFoundMessages.affectedFoundMessages(pts: _1!, ptsCount: _2!, offset: _3!, messages: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.messages.AffectedFoundMessages.affectedFoundMessages(pts: _1!, ptsCount: _2!, offset: _3!, messages: _4!) } } @@ -1298,12 +1261,10 @@ public extension Api.messages { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.AffectedHistory.affectedHistory(pts: _1!, ptsCount: _2!, offset: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.messages.AffectedHistory.affectedHistory(pts: _1!, ptsCount: _2!, offset: _3!) } } @@ -1338,12 +1299,9 @@ public extension Api.messages { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.AffectedMessages.affectedMessages(pts: _1!, ptsCount: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.messages.AffectedMessages.affectedMessages(pts: _1!, ptsCount: _2!) } } diff --git a/submodules/TelegramApi/Sources/Api33.swift b/submodules/TelegramApi/Sources/Api33.swift index 1341629f..98c550df 100644 --- a/submodules/TelegramApi/Sources/Api33.swift +++ b/submodules/TelegramApi/Sources/Api33.swift @@ -43,12 +43,9 @@ public extension Api.messages { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.AllStickers.allStickers(hash: _1!, sets: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.messages.AllStickers.allStickers(hash: _1!, sets: _2!) } public static func parse_allStickersNotModified(_ reader: BufferReader) -> AllStickers? { return Api.messages.AllStickers.allStickersNotModified @@ -92,12 +89,9 @@ public extension Api.messages { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.ArchivedStickers.archivedStickers(count: _1!, sets: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.messages.ArchivedStickers.archivedStickers(count: _1!, sets: _2!) } } @@ -157,12 +151,10 @@ public extension Api.messages { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.AvailableEffects.availableEffects(hash: _1!, effects: _2!, documents: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.messages.AvailableEffects.availableEffects(hash: _1!, effects: _2!, documents: _3!) } public static func parse_availableEffectsNotModified(_ reader: BufferReader) -> AvailableEffects? { return Api.messages.AvailableEffects.availableEffectsNotModified @@ -215,12 +207,9 @@ public extension Api.messages { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.AvailableReactions.availableReactions(hash: _1!, reactions: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.messages.AvailableReactions.availableReactions(hash: _1!, reactions: _2!) } public static func parse_availableReactionsNotModified(_ reader: BufferReader) -> AvailableReactions? { return Api.messages.AvailableReactions.availableReactionsNotModified @@ -260,12 +249,9 @@ public extension Api.messages { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.BotApp.botApp(flags: _1!, app: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.messages.BotApp.botApp(flags: _1!, app: _2!) } } @@ -308,12 +294,11 @@ public extension Api.messages { let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.messages.BotCallbackAnswer.botCallbackAnswer(flags: _1!, message: _2, url: _3, cacheTime: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.messages.BotCallbackAnswer.botCallbackAnswer(flags: _1!, message: _2, url: _3, cacheTime: _4!) } } @@ -348,12 +333,9 @@ public extension Api.messages { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.BotPreparedInlineMessage.botPreparedInlineMessage(id: _1!, expireDate: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.messages.BotPreparedInlineMessage.botPreparedInlineMessage(id: _1!, expireDate: _2!) } } @@ -428,12 +410,15 @@ public extension Api.messages { let _c6 = _6 != nil let _c7 = _7 != nil let _c8 = _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.messages.BotResults.botResults(flags: _1!, queryId: _2!, nextOffset: _3, switchPm: _4, switchWebview: _5, results: _6!, cacheTime: _7!, users: _8!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.messages.BotResults.botResults(flags: _1!, queryId: _2!, nextOffset: _3, switchPm: _4, switchWebview: _5, results: _6!, cacheTime: _7!, users: _8!) } } @@ -480,12 +465,9 @@ public extension Api.messages { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.ChatAdminsWithInvites.chatAdminsWithInvites(admins: _1!, users: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.messages.ChatAdminsWithInvites.chatAdminsWithInvites(admins: _1!, users: _2!) } } @@ -538,12 +520,10 @@ public extension Api.messages { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.ChatFull.chatFull(fullChat: _1!, chats: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.messages.ChatFull.chatFull(fullChat: _1!, chats: _2!, users: _3!) } } @@ -594,12 +574,10 @@ public extension Api.messages { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.ChatInviteImporters.chatInviteImporters(count: _1!, importers: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.messages.ChatInviteImporters.chatInviteImporters(count: _1!, importers: _2!, users: _3!) } } @@ -650,12 +628,8 @@ public extension Api.messages { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) } let _c1 = _1 != nil - if _c1 { - return Api.messages.Chats.chats(chats: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.messages.Chats.chats(chats: _1!) } public static func parse_chatsSlice(_ reader: BufferReader) -> Chats? { var _1: Int32? @@ -666,12 +640,9 @@ public extension Api.messages { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.Chats.chatsSlice(count: _1!, chats: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.messages.Chats.chatsSlice(count: _1!, chats: _2!) } } @@ -702,12 +673,8 @@ public extension Api.messages { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.messages.CheckedHistoryImportPeer.checkedHistoryImportPeer(confirmText: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.messages.CheckedHistoryImportPeer.checkedHistoryImportPeer(confirmText: _1!) } } @@ -759,23 +726,18 @@ public extension Api.messages { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.messages.DhConfig.dhConfig(g: _1!, p: _2!, version: _3!, random: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.messages.DhConfig.dhConfig(g: _1!, p: _2!, version: _3!, random: _4!) } public static func parse_dhConfigNotModified(_ reader: BufferReader) -> DhConfig? { var _1: Buffer? _1 = parseBytes(reader) let _c1 = _1 != nil - if _c1 { - return Api.messages.DhConfig.dhConfigNotModified(random: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.messages.DhConfig.dhConfigNotModified(random: _1!) } } @@ -816,12 +778,9 @@ public extension Api.messages { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.DialogFilters.dialogFilters(flags: _1!, filters: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.messages.DialogFilters.dialogFilters(flags: _1!, filters: _2!) } } @@ -926,23 +885,18 @@ public extension Api.messages { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.messages.Dialogs.dialogs(dialogs: _1!, messages: _2!, chats: _3!, users: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.messages.Dialogs.dialogs(dialogs: _1!, messages: _2!, chats: _3!, users: _4!) } public static func parse_dialogsNotModified(_ reader: BufferReader) -> Dialogs? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.messages.Dialogs.dialogsNotModified(count: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.messages.Dialogs.dialogsNotModified(count: _1!) } public static func parse_dialogsSlice(_ reader: BufferReader) -> Dialogs? { var _1: Int32? @@ -968,12 +922,12 @@ public extension Api.messages { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.messages.Dialogs.dialogsSlice(count: _1!, dialogs: _2!, messages: _3!, chats: _4!, users: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.messages.Dialogs.dialogsSlice(count: _1!, dialogs: _2!, messages: _3!, chats: _4!, users: _5!) } } @@ -1050,12 +1004,132 @@ public extension Api.messages { let _c6 = _6 != nil let _c7 = _7 != nil let _c8 = _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.messages.DiscussionMessage.discussionMessage(flags: _1!, messages: _2!, maxId: _3, readInboxMaxId: _4, readOutboxMaxId: _5, unreadCount: _6!, chats: _7!, users: _8!) - } - else { - return nil + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.messages.DiscussionMessage.discussionMessage(flags: _1!, messages: _2!, maxId: _3, readInboxMaxId: _4, readOutboxMaxId: _5, unreadCount: _6!, chats: _7!, users: _8!) + } + + } +} +public extension Api.messages { + enum EmojiGameInfo: TypeConstructorDescription { + case emojiGameDiceInfo(flags: Int32, gameHash: String, prevStake: Int64, currentStreak: Int32, params: [Int32], playsLeft: Int32?) + case emojiGameUnavailable + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .emojiGameDiceInfo(let flags, let gameHash, let prevStake, let currentStreak, let params, let playsLeft): + if boxed { + buffer.appendInt32(1155883043) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(gameHash, buffer: buffer, boxed: false) + serializeInt64(prevStake, buffer: buffer, boxed: false) + serializeInt32(currentStreak, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(params.count)) + for item in params { + serializeInt32(item, buffer: buffer, boxed: false) + } + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(playsLeft!, buffer: buffer, boxed: false)} + break + case .emojiGameUnavailable: + if boxed { + buffer.appendInt32(1508266805) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .emojiGameDiceInfo(let flags, let gameHash, let prevStake, let currentStreak, let params, let playsLeft): + return ("emojiGameDiceInfo", [("flags", flags as Any), ("gameHash", gameHash as Any), ("prevStake", prevStake as Any), ("currentStreak", currentStreak as Any), ("params", params as Any), ("playsLeft", playsLeft as Any)]) + case .emojiGameUnavailable: + return ("emojiGameUnavailable", []) + } + } + + public static func parse_emojiGameDiceInfo(_ reader: BufferReader) -> EmojiGameInfo? { + var _1: Int32? + _1 = reader.readInt32() + var _2: String? + _2 = parseString(reader) + var _3: Int64? + _3 = reader.readInt64() + var _4: Int32? + _4 = reader.readInt32() + var _5: [Int32]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) } + var _6: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_6 = reader.readInt32() } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.messages.EmojiGameInfo.emojiGameDiceInfo(flags: _1!, gameHash: _2!, prevStake: _3!, currentStreak: _4!, params: _5!, playsLeft: _6) + } + public static func parse_emojiGameUnavailable(_ reader: BufferReader) -> EmojiGameInfo? { + return Api.messages.EmojiGameInfo.emojiGameUnavailable + } + + } +} +public extension Api.messages { + enum EmojiGameOutcome: TypeConstructorDescription { + case emojiGameOutcome(seed: Buffer, stakeTonAmount: Int64, tonAmount: Int64) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .emojiGameOutcome(let seed, let stakeTonAmount, let tonAmount): + if boxed { + buffer.appendInt32(-634726841) + } + serializeBytes(seed, buffer: buffer, boxed: false) + serializeInt64(stakeTonAmount, buffer: buffer, boxed: false) + serializeInt64(tonAmount, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .emojiGameOutcome(let seed, let stakeTonAmount, let tonAmount): + return ("emojiGameOutcome", [("seed", seed as Any), ("stakeTonAmount", stakeTonAmount as Any), ("tonAmount", tonAmount as Any)]) + } + } + + public static func parse_emojiGameOutcome(_ reader: BufferReader) -> EmojiGameOutcome? { + var _1: Buffer? + _1 = parseBytes(reader) + var _2: Int64? + _2 = reader.readInt64() + var _3: Int64? + _3 = reader.readInt64() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.messages.EmojiGameOutcome.emojiGameOutcome(seed: _1!, stakeTonAmount: _2!, tonAmount: _3!) } } @@ -1105,12 +1179,9 @@ public extension Api.messages { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.EmojiGroups.emojiGroups(hash: _1!, groups: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.messages.EmojiGroups.emojiGroups(hash: _1!, groups: _2!) } public static func parse_emojiGroupsNotModified(_ reader: BufferReader) -> EmojiGroups? { return Api.messages.EmojiGroups.emojiGroupsNotModified @@ -1171,12 +1242,9 @@ public extension Api.messages { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.ExportedChatInvite.exportedChatInvite(invite: _1!, users: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.messages.ExportedChatInvite.exportedChatInvite(invite: _1!, users: _2!) } public static func parse_exportedChatInviteReplaced(_ reader: BufferReader) -> ExportedChatInvite? { var _1: Api.ExportedChatInvite? @@ -1194,12 +1262,10 @@ public extension Api.messages { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.ExportedChatInvite.exportedChatInviteReplaced(invite: _1!, newInvite: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.messages.ExportedChatInvite.exportedChatInviteReplaced(invite: _1!, newInvite: _2!, users: _3!) } } @@ -1250,164 +1316,10 @@ public extension Api.messages { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.ExportedChatInvites.exportedChatInvites(count: _1!, invites: _2!, users: _3!) - } - else { - return nil - } - } - - } -} -public extension Api.messages { - enum FavedStickers: TypeConstructorDescription { - case favedStickers(hash: Int64, packs: [Api.StickerPack], stickers: [Api.Document]) - case favedStickersNotModified - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .favedStickers(let hash, let packs, let stickers): - if boxed { - buffer.appendInt32(750063767) - } - serializeInt64(hash, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(packs.count)) - for item in packs { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(stickers.count)) - for item in stickers { - item.serialize(buffer, true) - } - break - case .favedStickersNotModified: - if boxed { - buffer.appendInt32(-1634752813) - } - - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .favedStickers(let hash, let packs, let stickers): - return ("favedStickers", [("hash", hash as Any), ("packs", packs as Any), ("stickers", stickers as Any)]) - case .favedStickersNotModified: - return ("favedStickersNotModified", []) - } - } - - public static func parse_favedStickers(_ reader: BufferReader) -> FavedStickers? { - var _1: Int64? - _1 = reader.readInt64() - var _2: [Api.StickerPack]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerPack.self) - } - var _3: [Api.Document]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.FavedStickers.favedStickers(hash: _1!, packs: _2!, stickers: _3!) - } - else { - return nil - } - } - public static func parse_favedStickersNotModified(_ reader: BufferReader) -> FavedStickers? { - return Api.messages.FavedStickers.favedStickersNotModified - } - - } -} -public extension Api.messages { - enum FeaturedStickers: TypeConstructorDescription { - case featuredStickers(flags: Int32, hash: Int64, count: Int32, sets: [Api.StickerSetCovered], unread: [Int64]) - case featuredStickersNotModified(count: Int32) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .featuredStickers(let flags, let hash, let count, let sets, let unread): - if boxed { - buffer.appendInt32(-1103615738) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(hash, buffer: buffer, boxed: false) - serializeInt32(count, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(sets.count)) - for item in sets { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(unread.count)) - for item in unread { - serializeInt64(item, buffer: buffer, boxed: false) - } - break - case .featuredStickersNotModified(let count): - if boxed { - buffer.appendInt32(-958657434) - } - serializeInt32(count, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .featuredStickers(let flags, let hash, let count, let sets, let unread): - return ("featuredStickers", [("flags", flags as Any), ("hash", hash as Any), ("count", count as Any), ("sets", sets as Any), ("unread", unread as Any)]) - case .featuredStickersNotModified(let count): - return ("featuredStickersNotModified", [("count", count as Any)]) - } - } - - public static func parse_featuredStickers(_ reader: BufferReader) -> FeaturedStickers? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int64? - _2 = reader.readInt64() - var _3: Int32? - _3 = reader.readInt32() - var _4: [Api.StickerSetCovered]? - if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerSetCovered.self) - } - var _5: [Int64]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.messages.FeaturedStickers.featuredStickers(flags: _1!, hash: _2!, count: _3!, sets: _4!, unread: _5!) - } - else { - return nil - } - } - public static func parse_featuredStickersNotModified(_ reader: BufferReader) -> FeaturedStickers? { - var _1: Int32? - _1 = reader.readInt32() - let _c1 = _1 != nil - if _c1 { - return Api.messages.FeaturedStickers.featuredStickersNotModified(count: _1!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.messages.ExportedChatInvites.exportedChatInvites(count: _1!, invites: _2!, users: _3!) } } diff --git a/submodules/TelegramApi/Sources/Api34.swift b/submodules/TelegramApi/Sources/Api34.swift index a0c446bb..23900f23 100644 --- a/submodules/TelegramApi/Sources/Api34.swift +++ b/submodules/TelegramApi/Sources/Api34.swift @@ -1,3 +1,149 @@ +public extension Api.messages { + enum FavedStickers: TypeConstructorDescription { + case favedStickers(hash: Int64, packs: [Api.StickerPack], stickers: [Api.Document]) + case favedStickersNotModified + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .favedStickers(let hash, let packs, let stickers): + if boxed { + buffer.appendInt32(750063767) + } + serializeInt64(hash, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(packs.count)) + for item in packs { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(stickers.count)) + for item in stickers { + item.serialize(buffer, true) + } + break + case .favedStickersNotModified: + if boxed { + buffer.appendInt32(-1634752813) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .favedStickers(let hash, let packs, let stickers): + return ("favedStickers", [("hash", hash as Any), ("packs", packs as Any), ("stickers", stickers as Any)]) + case .favedStickersNotModified: + return ("favedStickersNotModified", []) + } + } + + public static func parse_favedStickers(_ reader: BufferReader) -> FavedStickers? { + var _1: Int64? + _1 = reader.readInt64() + var _2: [Api.StickerPack]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerPack.self) + } + var _3: [Api.Document]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.messages.FavedStickers.favedStickers(hash: _1!, packs: _2!, stickers: _3!) + } + public static func parse_favedStickersNotModified(_ reader: BufferReader) -> FavedStickers? { + return Api.messages.FavedStickers.favedStickersNotModified + } + + } +} +public extension Api.messages { + enum FeaturedStickers: TypeConstructorDescription { + case featuredStickers(flags: Int32, hash: Int64, count: Int32, sets: [Api.StickerSetCovered], unread: [Int64]) + case featuredStickersNotModified(count: Int32) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .featuredStickers(let flags, let hash, let count, let sets, let unread): + if boxed { + buffer.appendInt32(-1103615738) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + serializeInt32(count, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(sets.count)) + for item in sets { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(unread.count)) + for item in unread { + serializeInt64(item, buffer: buffer, boxed: false) + } + break + case .featuredStickersNotModified(let count): + if boxed { + buffer.appendInt32(-958657434) + } + serializeInt32(count, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .featuredStickers(let flags, let hash, let count, let sets, let unread): + return ("featuredStickers", [("flags", flags as Any), ("hash", hash as Any), ("count", count as Any), ("sets", sets as Any), ("unread", unread as Any)]) + case .featuredStickersNotModified(let count): + return ("featuredStickersNotModified", [("count", count as Any)]) + } + } + + public static func parse_featuredStickers(_ reader: BufferReader) -> FeaturedStickers? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Int32? + _3 = reader.readInt32() + var _4: [Api.StickerSetCovered]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerSetCovered.self) + } + var _5: [Int64]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.messages.FeaturedStickers.featuredStickers(flags: _1!, hash: _2!, count: _3!, sets: _4!, unread: _5!) + } + public static func parse_featuredStickersNotModified(_ reader: BufferReader) -> FeaturedStickers? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if !_c1 { return nil } + return Api.messages.FeaturedStickers.featuredStickersNotModified(count: _1!) + } + + } +} public extension Api.messages { enum ForumTopics: TypeConstructorDescription { case forumTopics(flags: Int32, count: Int32, topics: [Api.ForumTopic], messages: [Api.Message], chats: [Api.Chat], users: [Api.User], pts: Int32) @@ -72,12 +218,14 @@ public extension Api.messages { let _c5 = _5 != nil let _c6 = _6 != nil let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.messages.ForumTopics.forumTopics(flags: _1!, count: _2!, topics: _3!, messages: _4!, chats: _5!, users: _6!, pts: _7!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.messages.ForumTopics.forumTopics(flags: _1!, count: _2!, topics: _3!, messages: _4!, chats: _5!, users: _6!, pts: _7!) } } @@ -127,12 +275,9 @@ public extension Api.messages { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.FoundStickerSets.foundStickerSets(hash: _1!, sets: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.messages.FoundStickerSets.foundStickerSets(hash: _1!, sets: _2!) } public static func parse_foundStickerSetsNotModified(_ reader: BufferReader) -> FoundStickerSets? { return Api.messages.FoundStickerSets.foundStickerSetsNotModified @@ -194,12 +339,11 @@ public extension Api.messages { let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.messages.FoundStickers.foundStickers(flags: _1!, nextOffset: _2, hash: _3!, stickers: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.messages.FoundStickers.foundStickers(flags: _1!, nextOffset: _2, hash: _3!, stickers: _4!) } public static func parse_foundStickersNotModified(_ reader: BufferReader) -> FoundStickers? { var _1: Int32? @@ -208,12 +352,9 @@ public extension Api.messages { if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - if _c1 && _c2 { - return Api.messages.FoundStickers.foundStickersNotModified(flags: _1!, nextOffset: _2) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.messages.FoundStickers.foundStickersNotModified(flags: _1!, nextOffset: _2) } } @@ -260,12 +401,9 @@ public extension Api.messages { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.HighScores.highScores(scores: _1!, users: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.messages.HighScores.highScores(scores: _1!, users: _2!) } } @@ -296,12 +434,8 @@ public extension Api.messages { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.messages.HistoryImport.historyImport(id: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.messages.HistoryImport.historyImport(id: _1!) } } @@ -336,12 +470,9 @@ public extension Api.messages { if Int(_1!) & Int(1 << 2) != 0 {_2 = parseString(reader) } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 2) == 0) || _2 != nil - if _c1 && _c2 { - return Api.messages.HistoryImportParsed.historyImportParsed(flags: _1!, title: _2) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.messages.HistoryImportParsed.historyImportParsed(flags: _1!, title: _2) } } @@ -398,12 +529,10 @@ public extension Api.messages { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.InactiveChats.inactiveChats(dates: _1!, chats: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.messages.InactiveChats.inactiveChats(dates: _1!, chats: _2!, users: _3!) } } @@ -446,12 +575,9 @@ public extension Api.messages { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.InvitedUsers.invitedUsers(updates: _1!, missingInvitees: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.messages.InvitedUsers.invitedUsers(updates: _1!, missingInvitees: _2!) } } @@ -482,12 +608,8 @@ public extension Api.messages { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.messages.MessageEditData.messageEditData(flags: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.messages.MessageEditData.messageEditData(flags: _1!) } } @@ -556,12 +678,13 @@ public extension Api.messages { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.messages.MessageReactionsList.messageReactionsList(flags: _1!, count: _2!, reactions: _3!, chats: _4!, users: _5!, nextOffset: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.messages.MessageReactionsList.messageReactionsList(flags: _1!, count: _2!, reactions: _3!, chats: _4!, users: _5!, nextOffset: _6) } } @@ -618,12 +741,10 @@ public extension Api.messages { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.MessageViews.messageViews(views: _1!, chats: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.messages.MessageViews.messageViews(views: _1!, chats: _2!, users: _3!) } } @@ -776,12 +897,15 @@ public extension Api.messages { let _c6 = _6 != nil let _c7 = _7 != nil let _c8 = _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.messages.Messages.channelMessages(flags: _1!, pts: _2!, count: _3!, offsetIdOffset: _4, messages: _5!, topics: _6!, chats: _7!, users: _8!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.messages.Messages.channelMessages(flags: _1!, pts: _2!, count: _3!, offsetIdOffset: _4, messages: _5!, topics: _6!, chats: _7!, users: _8!) } public static func parse_messages(_ reader: BufferReader) -> Messages? { var _1: [Api.Message]? @@ -804,23 +928,18 @@ public extension Api.messages { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.messages.Messages.messages(messages: _1!, topics: _2!, chats: _3!, users: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.messages.Messages.messages(messages: _1!, topics: _2!, chats: _3!, users: _4!) } public static func parse_messagesNotModified(_ reader: BufferReader) -> Messages? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.messages.Messages.messagesNotModified(count: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.messages.Messages.messagesNotModified(count: _1!) } public static func parse_messagesSlice(_ reader: BufferReader) -> Messages? { var _1: Int32? @@ -860,12 +979,16 @@ public extension Api.messages { let _c7 = _7 != nil let _c8 = _8 != nil let _c9 = _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.messages.Messages.messagesSlice(flags: _1!, count: _2!, nextRate: _3, offsetIdOffset: _4, searchFlood: _5, messages: _6!, topics: _7!, chats: _8!, users: _9!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + return Api.messages.Messages.messagesSlice(flags: _1!, count: _2!, nextRate: _3, offsetIdOffset: _4, searchFlood: _5, messages: _6!, topics: _7!, chats: _8!, users: _9!) } } @@ -906,12 +1029,9 @@ public extension Api.messages { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.MyStickers.myStickers(count: _1!, sets: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.messages.MyStickers.myStickers(count: _1!, sets: _2!) } } @@ -984,12 +1104,12 @@ public extension Api.messages { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.messages.PeerDialogs.peerDialogs(dialogs: _1!, messages: _2!, chats: _3!, users: _4!, state: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.messages.PeerDialogs.peerDialogs(dialogs: _1!, messages: _2!, chats: _3!, users: _4!, state: _5!) } } @@ -1042,12 +1162,10 @@ public extension Api.messages { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.PeerSettings.peerSettings(settings: _1!, chats: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.messages.PeerSettings.peerSettings(settings: _1!, chats: _2!, users: _3!) } } @@ -1108,12 +1226,12 @@ public extension Api.messages { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.messages.PreparedInlineMessage.preparedInlineMessage(queryId: _1!, result: _2!, peerTypes: _3!, cacheTime: _4!, users: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.messages.PreparedInlineMessage.preparedInlineMessage(queryId: _1!, result: _2!, peerTypes: _3!, cacheTime: _4!, users: _5!) } } @@ -1189,12 +1307,11 @@ public extension Api.messages { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.messages.QuickReplies.quickReplies(quickReplies: _1!, messages: _2!, chats: _3!, users: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.messages.QuickReplies.quickReplies(quickReplies: _1!, messages: _2!, chats: _3!, users: _4!) } public static func parse_quickRepliesNotModified(_ reader: BufferReader) -> QuickReplies? { return Api.messages.QuickReplies.quickRepliesNotModified @@ -1247,12 +1364,9 @@ public extension Api.messages { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.Reactions.reactions(hash: _1!, reactions: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.messages.Reactions.reactions(hash: _1!, reactions: _2!) } public static func parse_reactionsNotModified(_ reader: BufferReader) -> Reactions? { return Api.messages.Reactions.reactionsNotModified @@ -1325,12 +1439,11 @@ public extension Api.messages { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.messages.RecentStickers.recentStickers(hash: _1!, packs: _2!, stickers: _3!, dates: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.messages.RecentStickers.recentStickers(hash: _1!, packs: _2!, stickers: _3!, dates: _4!) } public static func parse_recentStickersNotModified(_ reader: BufferReader) -> RecentStickers? { return Api.messages.RecentStickers.recentStickersNotModified @@ -1438,23 +1551,18 @@ public extension Api.messages { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.messages.SavedDialogs.savedDialogs(dialogs: _1!, messages: _2!, chats: _3!, users: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.messages.SavedDialogs.savedDialogs(dialogs: _1!, messages: _2!, chats: _3!, users: _4!) } public static func parse_savedDialogsNotModified(_ reader: BufferReader) -> SavedDialogs? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.messages.SavedDialogs.savedDialogsNotModified(count: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.messages.SavedDialogs.savedDialogsNotModified(count: _1!) } public static func parse_savedDialogsSlice(_ reader: BufferReader) -> SavedDialogs? { var _1: Int32? @@ -1480,70 +1588,12 @@ public extension Api.messages { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.messages.SavedDialogs.savedDialogsSlice(count: _1!, dialogs: _2!, messages: _3!, chats: _4!, users: _5!) - } - else { - return nil - } - } - - } -} -public extension Api.messages { - enum SavedGifs: TypeConstructorDescription { - case savedGifs(hash: Int64, gifs: [Api.Document]) - case savedGifsNotModified - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .savedGifs(let hash, let gifs): - if boxed { - buffer.appendInt32(-2069878259) - } - serializeInt64(hash, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(gifs.count)) - for item in gifs { - item.serialize(buffer, true) - } - break - case .savedGifsNotModified: - if boxed { - buffer.appendInt32(-402498398) - } - - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .savedGifs(let hash, let gifs): - return ("savedGifs", [("hash", hash as Any), ("gifs", gifs as Any)]) - case .savedGifsNotModified: - return ("savedGifsNotModified", []) - } - } - - public static func parse_savedGifs(_ reader: BufferReader) -> SavedGifs? { - var _1: Int64? - _1 = reader.readInt64() - var _2: [Api.Document]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.SavedGifs.savedGifs(hash: _1!, gifs: _2!) - } - else { - return nil - } - } - public static func parse_savedGifsNotModified(_ reader: BufferReader) -> SavedGifs? { - return Api.messages.SavedGifs.savedGifsNotModified + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.messages.SavedDialogs.savedDialogsSlice(count: _1!, dialogs: _2!, messages: _3!, chats: _4!, users: _5!) } } diff --git a/submodules/TelegramApi/Sources/Api35.swift b/submodules/TelegramApi/Sources/Api35.swift index 31b027ae..0898d641 100644 --- a/submodules/TelegramApi/Sources/Api35.swift +++ b/submodules/TelegramApi/Sources/Api35.swift @@ -1,3 +1,58 @@ +public extension Api.messages { + enum SavedGifs: TypeConstructorDescription { + case savedGifs(hash: Int64, gifs: [Api.Document]) + case savedGifsNotModified + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .savedGifs(let hash, let gifs): + if boxed { + buffer.appendInt32(-2069878259) + } + serializeInt64(hash, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(gifs.count)) + for item in gifs { + item.serialize(buffer, true) + } + break + case .savedGifsNotModified: + if boxed { + buffer.appendInt32(-402498398) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .savedGifs(let hash, let gifs): + return ("savedGifs", [("hash", hash as Any), ("gifs", gifs as Any)]) + case .savedGifsNotModified: + return ("savedGifsNotModified", []) + } + } + + public static func parse_savedGifs(_ reader: BufferReader) -> SavedGifs? { + var _1: Int64? + _1 = reader.readInt64() + var _2: [Api.Document]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if !_c1 { return nil } + if !_c2 { return nil } + return Api.messages.SavedGifs.savedGifs(hash: _1!, gifs: _2!) + } + public static func parse_savedGifsNotModified(_ reader: BufferReader) -> SavedGifs? { + return Api.messages.SavedGifs.savedGifsNotModified + } + + } +} public extension Api.messages { enum SavedReactionTags: TypeConstructorDescription { case savedReactionTags(tags: [Api.SavedReactionTag], hash: Int64) @@ -43,12 +98,9 @@ public extension Api.messages { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.SavedReactionTags.savedReactionTags(tags: _1!, hash: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.messages.SavedReactionTags.savedReactionTags(tags: _1!, hash: _2!) } public static func parse_savedReactionTagsNotModified(_ reader: BufferReader) -> SavedReactionTags? { return Api.messages.SavedReactionTags.savedReactionTagsNotModified @@ -92,12 +144,10 @@ public extension Api.messages { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.SearchCounter.searchCounter(flags: _1!, filter: _2!, count: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.messages.SearchCounter.searchCounter(flags: _1!, filter: _2!, count: _3!) } } @@ -184,12 +234,16 @@ public extension Api.messages { let _c7 = _7 != nil let _c8 = _8 != nil let _c9 = _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.messages.SearchResultsCalendar.searchResultsCalendar(flags: _1!, count: _2!, minDate: _3!, minMsgId: _4!, offsetIdOffset: _5, periods: _6!, messages: _7!, chats: _8!, users: _9!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + return Api.messages.SearchResultsCalendar.searchResultsCalendar(flags: _1!, count: _2!, minDate: _3!, minMsgId: _4!, offsetIdOffset: _5, periods: _6!, messages: _7!, chats: _8!, users: _9!) } } @@ -230,12 +284,9 @@ public extension Api.messages { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.SearchResultsPositions.searchResultsPositions(count: _1!, positions: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.messages.SearchResultsPositions.searchResultsPositions(count: _1!, positions: _2!) } } @@ -281,23 +332,16 @@ public extension Api.messages { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.SentEncryptedMessage.sentEncryptedFile(date: _1!, file: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.messages.SentEncryptedMessage.sentEncryptedFile(date: _1!, file: _2!) } public static func parse_sentEncryptedMessage(_ reader: BufferReader) -> SentEncryptedMessage? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.messages.SentEncryptedMessage.sentEncryptedMessage(date: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.messages.SentEncryptedMessage.sentEncryptedMessage(date: _1!) } } @@ -379,12 +423,14 @@ public extension Api.messages { let _c5 = _5 != nil let _c6 = _6 != nil let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.messages.SponsoredMessages.sponsoredMessages(flags: _1!, postsBetween: _2, startDelay: _3, betweenDelay: _4, messages: _5!, chats: _6!, users: _7!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.messages.SponsoredMessages.sponsoredMessages(flags: _1!, postsBetween: _2, startDelay: _3, betweenDelay: _4, messages: _5!, chats: _6!, users: _7!) } public static func parse_sponsoredMessagesEmpty(_ reader: BufferReader) -> SponsoredMessages? { return Api.messages.SponsoredMessages.sponsoredMessagesEmpty @@ -459,12 +505,11 @@ public extension Api.messages { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.messages.StickerSet.stickerSet(set: _1!, packs: _2!, keywords: _3!, documents: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.messages.StickerSet.stickerSet(set: _1!, packs: _2!, keywords: _3!, documents: _4!) } public static func parse_stickerSetNotModified(_ reader: BufferReader) -> StickerSet? { return Api.messages.StickerSet.stickerSetNotModified @@ -513,12 +558,8 @@ public extension Api.messages { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerSetCovered.self) } let _c1 = _1 != nil - if _c1 { - return Api.messages.StickerSetInstallResult.stickerSetInstallResultArchive(sets: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.messages.StickerSetInstallResult.stickerSetInstallResultArchive(sets: _1!) } public static func parse_stickerSetInstallResultSuccess(_ reader: BufferReader) -> StickerSetInstallResult? { return Api.messages.StickerSetInstallResult.stickerSetInstallResultSuccess @@ -571,12 +612,9 @@ public extension Api.messages { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.messages.Stickers.stickers(hash: _1!, stickers: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.messages.Stickers.stickers(hash: _1!, stickers: _2!) } public static func parse_stickersNotModified(_ reader: BufferReader) -> Stickers? { return Api.messages.Stickers.stickersNotModified @@ -626,12 +664,12 @@ public extension Api.messages { let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.messages.TranscribedAudio.transcribedAudio(flags: _1!, transcriptionId: _2!, text: _3!, trialRemainsNum: _4, trialRemainsUntilDate: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.messages.TranscribedAudio.transcribedAudio(flags: _1!, transcriptionId: _2!, text: _3!, trialRemainsNum: _4, trialRemainsUntilDate: _5) } } @@ -668,12 +706,8 @@ public extension Api.messages { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.TextWithEntities.self) } let _c1 = _1 != nil - if _c1 { - return Api.messages.TranslatedText.translateResult(result: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.messages.TranslatedText.translateResult(result: _1!) } } @@ -742,12 +776,13 @@ public extension Api.messages { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.messages.VotesList.votesList(flags: _1!, count: _2!, votes: _3!, chats: _4!, users: _5!, nextOffset: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.messages.VotesList.votesList(flags: _1!, count: _2!, votes: _3!, chats: _4!, users: _5!, nextOffset: _6) } } @@ -800,12 +835,10 @@ public extension Api.messages { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.WebPage.webPage(webpage: _1!, chats: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.messages.WebPage.webPage(webpage: _1!, chats: _2!, users: _3!) } } @@ -858,12 +891,10 @@ public extension Api.messages { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.messages.WebPagePreview.webPagePreview(media: _1!, chats: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.messages.WebPagePreview.webPagePreview(media: _1!, chats: _2!, users: _3!) } } @@ -904,12 +935,9 @@ public extension Api.payments { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.payments.BankCardData.bankCardData(title: _1!, openUrls: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.payments.BankCardData.bankCardData(title: _1!, openUrls: _2!) } } @@ -951,12 +979,8 @@ public extension Api.payments { _1 = Api.parse(reader, signature: signature) as? Api.TextWithEntities } let _c1 = _1 != nil - if _c1 { - return Api.payments.CheckCanSendGiftResult.checkCanSendGiftResultFail(reason: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.payments.CheckCanSendGiftResult.checkCanSendGiftResultFail(reason: _1!) } public static func parse_checkCanSendGiftResultOk(_ reader: BufferReader) -> CheckCanSendGiftResult? { return Api.payments.CheckCanSendGiftResult.checkCanSendGiftResultOk @@ -1036,12 +1060,16 @@ public extension Api.payments { let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil let _c8 = _8 != nil let _c9 = _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.payments.CheckedGiftCode.checkedGiftCode(flags: _1!, fromId: _2, giveawayMsgId: _3, toId: _4, date: _5!, days: _6!, usedDate: _7, chats: _8!, users: _9!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + return Api.payments.CheckedGiftCode.checkedGiftCode(flags: _1!, fromId: _2, giveawayMsgId: _3, toId: _4, date: _5!, days: _6!, usedDate: _7, chats: _8!, users: _9!) } } @@ -1092,12 +1120,10 @@ public extension Api.payments { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.payments.ConnectedStarRefBots.connectedStarRefBots(count: _1!, connectedBots: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.payments.ConnectedStarRefBots.connectedStarRefBots(count: _1!, connectedBots: _2!, users: _3!) } } @@ -1128,12 +1154,8 @@ public extension Api.payments { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.payments.ExportedInvoice.exportedInvoice(url: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.payments.ExportedInvoice.exportedInvoice(url: _1!) } } @@ -1195,12 +1217,12 @@ public extension Api.payments { let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 4) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.payments.GiveawayInfo.giveawayInfo(flags: _1!, startDate: _2!, joinedTooEarlyDate: _3, adminDisallowedChatId: _4, disallowedCountry: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.payments.GiveawayInfo.giveawayInfo(flags: _1!, startDate: _2!, joinedTooEarlyDate: _3, adminDisallowedChatId: _4, disallowedCountry: _5) } public static func parse_giveawayInfoResults(_ reader: BufferReader) -> GiveawayInfo? { var _1: Int32? @@ -1224,12 +1246,14 @@ public extension Api.payments { let _c5 = _5 != nil let _c6 = _6 != nil let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.payments.GiveawayInfo.giveawayInfoResults(flags: _1!, startDate: _2!, giftCodeSlug: _3, starsPrize: _4, finishDate: _5!, winnersCount: _6!, activatedCount: _7) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.payments.GiveawayInfo.giveawayInfoResults(flags: _1!, startDate: _2!, giftCodeSlug: _3, starsPrize: _4, finishDate: _5!, winnersCount: _6!, activatedCount: _7) } } @@ -1372,12 +1396,22 @@ public extension Api.payments { let _c13 = (Int(_1!) & Int(1 << 0) == 0) || _13 != nil let _c14 = (Int(_1!) & Int(1 << 1) == 0) || _14 != nil let _c15 = _15 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 { - return Api.payments.PaymentForm.paymentForm(flags: _1!, formId: _2!, botId: _3!, title: _4!, description: _5!, photo: _6, invoice: _7!, providerId: _8!, url: _9!, nativeProvider: _10, nativeParams: _11, additionalMethods: _12, savedInfo: _13, savedCredentials: _14, users: _15!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + if !_c13 { return nil } + if !_c14 { return nil } + if !_c15 { return nil } + return Api.payments.PaymentForm.paymentForm(flags: _1!, formId: _2!, botId: _3!, title: _4!, description: _5!, photo: _6, invoice: _7!, providerId: _8!, url: _9!, nativeProvider: _10, nativeParams: _11, additionalMethods: _12, savedInfo: _13, savedCredentials: _14, users: _15!) } public static func parse_paymentFormStarGift(_ reader: BufferReader) -> PaymentForm? { var _1: Int64? @@ -1388,12 +1422,9 @@ public extension Api.payments { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.payments.PaymentForm.paymentFormStarGift(formId: _1!, invoice: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.payments.PaymentForm.paymentFormStarGift(formId: _1!, invoice: _2!) } public static func parse_paymentFormStars(_ reader: BufferReader) -> PaymentForm? { var _1: Int32? @@ -1426,188 +1457,15 @@ public extension Api.payments { let _c6 = (Int(_1!) & Int(1 << 5) == 0) || _6 != nil let _c7 = _7 != nil let _c8 = _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.payments.PaymentForm.paymentFormStars(flags: _1!, formId: _2!, botId: _3!, title: _4!, description: _5!, photo: _6, invoice: _7!, users: _8!) - } - else { - return nil - } - } - - } -} -public extension Api.payments { - enum PaymentReceipt: TypeConstructorDescription { - case paymentReceipt(flags: Int32, date: Int32, botId: Int64, providerId: Int64, title: String, description: String, photo: Api.WebDocument?, invoice: Api.Invoice, info: Api.PaymentRequestedInfo?, shipping: Api.ShippingOption?, tipAmount: Int64?, currency: String, totalAmount: Int64, credentialsTitle: String, users: [Api.User]) - case paymentReceiptStars(flags: Int32, date: Int32, botId: Int64, title: String, description: String, photo: Api.WebDocument?, invoice: Api.Invoice, currency: String, totalAmount: Int64, transactionId: String, users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .paymentReceipt(let flags, let date, let botId, let providerId, let title, let description, let photo, let invoice, let info, let shipping, let tipAmount, let currency, let totalAmount, let credentialsTitle, let users): - if boxed { - buffer.appendInt32(1891958275) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - serializeInt64(botId, buffer: buffer, boxed: false) - serializeInt64(providerId, buffer: buffer, boxed: false) - serializeString(title, buffer: buffer, boxed: false) - serializeString(description, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 2) != 0 {photo!.serialize(buffer, true)} - invoice.serialize(buffer, true) - if Int(flags) & Int(1 << 0) != 0 {info!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {shipping!.serialize(buffer, true)} - if Int(flags) & Int(1 << 3) != 0 {serializeInt64(tipAmount!, buffer: buffer, boxed: false)} - serializeString(currency, buffer: buffer, boxed: false) - serializeInt64(totalAmount, buffer: buffer, boxed: false) - serializeString(credentialsTitle, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - case .paymentReceiptStars(let flags, let date, let botId, let title, let description, let photo, let invoice, let currency, let totalAmount, let transactionId, let users): - if boxed { - buffer.appendInt32(-625215430) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(date, buffer: buffer, boxed: false) - serializeInt64(botId, buffer: buffer, boxed: false) - serializeString(title, buffer: buffer, boxed: false) - serializeString(description, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 2) != 0 {photo!.serialize(buffer, true)} - invoice.serialize(buffer, true) - serializeString(currency, buffer: buffer, boxed: false) - serializeInt64(totalAmount, buffer: buffer, boxed: false) - serializeString(transactionId, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .paymentReceipt(let flags, let date, let botId, let providerId, let title, let description, let photo, let invoice, let info, let shipping, let tipAmount, let currency, let totalAmount, let credentialsTitle, let users): - return ("paymentReceipt", [("flags", flags as Any), ("date", date as Any), ("botId", botId as Any), ("providerId", providerId as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("invoice", invoice as Any), ("info", info as Any), ("shipping", shipping as Any), ("tipAmount", tipAmount as Any), ("currency", currency as Any), ("totalAmount", totalAmount as Any), ("credentialsTitle", credentialsTitle as Any), ("users", users as Any)]) - case .paymentReceiptStars(let flags, let date, let botId, let title, let description, let photo, let invoice, let currency, let totalAmount, let transactionId, let users): - return ("paymentReceiptStars", [("flags", flags as Any), ("date", date as Any), ("botId", botId as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("invoice", invoice as Any), ("currency", currency as Any), ("totalAmount", totalAmount as Any), ("transactionId", transactionId as Any), ("users", users as Any)]) - } - } - - public static func parse_paymentReceipt(_ reader: BufferReader) -> PaymentReceipt? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int64? - _3 = reader.readInt64() - var _4: Int64? - _4 = reader.readInt64() - var _5: String? - _5 = parseString(reader) - var _6: String? - _6 = parseString(reader) - var _7: Api.WebDocument? - if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.WebDocument - } } - var _8: Api.Invoice? - if let signature = reader.readInt32() { - _8 = Api.parse(reader, signature: signature) as? Api.Invoice - } - var _9: Api.PaymentRequestedInfo? - if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { - _9 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo - } } - var _10: Api.ShippingOption? - if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { - _10 = Api.parse(reader, signature: signature) as? Api.ShippingOption - } } - var _11: Int64? - if Int(_1!) & Int(1 << 3) != 0 {_11 = reader.readInt64() } - var _12: String? - _12 = parseString(reader) - var _13: Int64? - _13 = reader.readInt64() - var _14: String? - _14 = parseString(reader) - var _15: [Api.User]? - if let _ = reader.readInt32() { - _15 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil - let _c8 = _8 != nil - let _c9 = (Int(_1!) & Int(1 << 0) == 0) || _9 != nil - let _c10 = (Int(_1!) & Int(1 << 1) == 0) || _10 != nil - let _c11 = (Int(_1!) & Int(1 << 3) == 0) || _11 != nil - let _c12 = _12 != nil - let _c13 = _13 != nil - let _c14 = _14 != nil - let _c15 = _15 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 { - return Api.payments.PaymentReceipt.paymentReceipt(flags: _1!, date: _2!, botId: _3!, providerId: _4!, title: _5!, description: _6!, photo: _7, invoice: _8!, info: _9, shipping: _10, tipAmount: _11, currency: _12!, totalAmount: _13!, credentialsTitle: _14!, users: _15!) - } - else { - return nil - } - } - public static func parse_paymentReceiptStars(_ reader: BufferReader) -> PaymentReceipt? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int64? - _3 = reader.readInt64() - var _4: String? - _4 = parseString(reader) - var _5: String? - _5 = parseString(reader) - var _6: Api.WebDocument? - if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.WebDocument - } } - var _7: Api.Invoice? - if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.Invoice - } - var _8: String? - _8 = parseString(reader) - var _9: Int64? - _9 = reader.readInt64() - var _10: String? - _10 = parseString(reader) - var _11: [Api.User]? - if let _ = reader.readInt32() { - _11 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil - let _c7 = _7 != nil - let _c8 = _8 != nil - let _c9 = _9 != nil - let _c10 = _10 != nil - let _c11 = _11 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 { - return Api.payments.PaymentReceipt.paymentReceiptStars(flags: _1!, date: _2!, botId: _3!, title: _4!, description: _5!, photo: _6, invoice: _7!, currency: _8!, totalAmount: _9!, transactionId: _10!, users: _11!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.payments.PaymentForm.paymentFormStars(flags: _1!, formId: _2!, botId: _3!, title: _4!, description: _5!, photo: _6, invoice: _7!, users: _8!) } } diff --git a/submodules/TelegramApi/Sources/Api36.swift b/submodules/TelegramApi/Sources/Api36.swift index 22436f1e..1bad226d 100644 --- a/submodules/TelegramApi/Sources/Api36.swift +++ b/submodules/TelegramApi/Sources/Api36.swift @@ -1,3 +1,195 @@ +public extension Api.payments { + enum PaymentReceipt: TypeConstructorDescription { + case paymentReceipt(flags: Int32, date: Int32, botId: Int64, providerId: Int64, title: String, description: String, photo: Api.WebDocument?, invoice: Api.Invoice, info: Api.PaymentRequestedInfo?, shipping: Api.ShippingOption?, tipAmount: Int64?, currency: String, totalAmount: Int64, credentialsTitle: String, users: [Api.User]) + case paymentReceiptStars(flags: Int32, date: Int32, botId: Int64, title: String, description: String, photo: Api.WebDocument?, invoice: Api.Invoice, currency: String, totalAmount: Int64, transactionId: String, users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .paymentReceipt(let flags, let date, let botId, let providerId, let title, let description, let photo, let invoice, let info, let shipping, let tipAmount, let currency, let totalAmount, let credentialsTitle, let users): + if boxed { + buffer.appendInt32(1891958275) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + serializeInt64(botId, buffer: buffer, boxed: false) + serializeInt64(providerId, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + serializeString(description, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 2) != 0 {photo!.serialize(buffer, true)} + invoice.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {info!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {shipping!.serialize(buffer, true)} + if Int(flags) & Int(1 << 3) != 0 {serializeInt64(tipAmount!, buffer: buffer, boxed: false)} + serializeString(currency, buffer: buffer, boxed: false) + serializeInt64(totalAmount, buffer: buffer, boxed: false) + serializeString(credentialsTitle, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + case .paymentReceiptStars(let flags, let date, let botId, let title, let description, let photo, let invoice, let currency, let totalAmount, let transactionId, let users): + if boxed { + buffer.appendInt32(-625215430) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(date, buffer: buffer, boxed: false) + serializeInt64(botId, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + serializeString(description, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 2) != 0 {photo!.serialize(buffer, true)} + invoice.serialize(buffer, true) + serializeString(currency, buffer: buffer, boxed: false) + serializeInt64(totalAmount, buffer: buffer, boxed: false) + serializeString(transactionId, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .paymentReceipt(let flags, let date, let botId, let providerId, let title, let description, let photo, let invoice, let info, let shipping, let tipAmount, let currency, let totalAmount, let credentialsTitle, let users): + return ("paymentReceipt", [("flags", flags as Any), ("date", date as Any), ("botId", botId as Any), ("providerId", providerId as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("invoice", invoice as Any), ("info", info as Any), ("shipping", shipping as Any), ("tipAmount", tipAmount as Any), ("currency", currency as Any), ("totalAmount", totalAmount as Any), ("credentialsTitle", credentialsTitle as Any), ("users", users as Any)]) + case .paymentReceiptStars(let flags, let date, let botId, let title, let description, let photo, let invoice, let currency, let totalAmount, let transactionId, let users): + return ("paymentReceiptStars", [("flags", flags as Any), ("date", date as Any), ("botId", botId as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("invoice", invoice as Any), ("currency", currency as Any), ("totalAmount", totalAmount as Any), ("transactionId", transactionId as Any), ("users", users as Any)]) + } + } + + public static func parse_paymentReceipt(_ reader: BufferReader) -> PaymentReceipt? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int64? + _3 = reader.readInt64() + var _4: Int64? + _4 = reader.readInt64() + var _5: String? + _5 = parseString(reader) + var _6: String? + _6 = parseString(reader) + var _7: Api.WebDocument? + if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { + _7 = Api.parse(reader, signature: signature) as? Api.WebDocument + } } + var _8: Api.Invoice? + if let signature = reader.readInt32() { + _8 = Api.parse(reader, signature: signature) as? Api.Invoice + } + var _9: Api.PaymentRequestedInfo? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _9 = Api.parse(reader, signature: signature) as? Api.PaymentRequestedInfo + } } + var _10: Api.ShippingOption? + if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { + _10 = Api.parse(reader, signature: signature) as? Api.ShippingOption + } } + var _11: Int64? + if Int(_1!) & Int(1 << 3) != 0 {_11 = reader.readInt64() } + var _12: String? + _12 = parseString(reader) + var _13: Int64? + _13 = reader.readInt64() + var _14: String? + _14 = parseString(reader) + var _15: [Api.User]? + if let _ = reader.readInt32() { + _15 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil + let _c8 = _8 != nil + let _c9 = (Int(_1!) & Int(1 << 0) == 0) || _9 != nil + let _c10 = (Int(_1!) & Int(1 << 1) == 0) || _10 != nil + let _c11 = (Int(_1!) & Int(1 << 3) == 0) || _11 != nil + let _c12 = _12 != nil + let _c13 = _13 != nil + let _c14 = _14 != nil + let _c15 = _15 != nil + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + if !_c13 { return nil } + if !_c14 { return nil } + if !_c15 { return nil } + return Api.payments.PaymentReceipt.paymentReceipt(flags: _1!, date: _2!, botId: _3!, providerId: _4!, title: _5!, description: _6!, photo: _7, invoice: _8!, info: _9, shipping: _10, tipAmount: _11, currency: _12!, totalAmount: _13!, credentialsTitle: _14!, users: _15!) + } + public static func parse_paymentReceiptStars(_ reader: BufferReader) -> PaymentReceipt? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int64? + _3 = reader.readInt64() + var _4: String? + _4 = parseString(reader) + var _5: String? + _5 = parseString(reader) + var _6: Api.WebDocument? + if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { + _6 = Api.parse(reader, signature: signature) as? Api.WebDocument + } } + var _7: Api.Invoice? + if let signature = reader.readInt32() { + _7 = Api.parse(reader, signature: signature) as? Api.Invoice + } + var _8: String? + _8 = parseString(reader) + var _9: Int64? + _9 = reader.readInt64() + var _10: String? + _10 = parseString(reader) + var _11: [Api.User]? + if let _ = reader.readInt32() { + _11 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil + let _c7 = _7 != nil + let _c8 = _8 != nil + let _c9 = _9 != nil + let _c10 = _10 != nil + let _c11 = _11 != nil + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + return Api.payments.PaymentReceipt.paymentReceiptStars(flags: _1!, date: _2!, botId: _3!, title: _4!, description: _5!, photo: _6, invoice: _7!, currency: _8!, totalAmount: _9!, transactionId: _10!, users: _11!) + } + + } +} public extension Api.payments { indirect enum PaymentResult: TypeConstructorDescription { case paymentResult(updates: Api.Updates) @@ -35,23 +227,15 @@ public extension Api.payments { _1 = Api.parse(reader, signature: signature) as? Api.Updates } let _c1 = _1 != nil - if _c1 { - return Api.payments.PaymentResult.paymentResult(updates: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.payments.PaymentResult.paymentResult(updates: _1!) } public static func parse_paymentVerificationNeeded(_ reader: BufferReader) -> PaymentResult? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.payments.PaymentResult.paymentVerificationNeeded(url: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.payments.PaymentResult.paymentVerificationNeeded(url: _1!) } } @@ -144,12 +328,16 @@ public extension Api.payments { let _c7 = _7 != nil let _c8 = (Int(_1!) & Int(1 << 2) == 0) || _8 != nil let _c9 = _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.payments.ResaleStarGifts.resaleStarGifts(flags: _1!, count: _2!, gifts: _3!, nextOffset: _4, attributes: _5, attributesHash: _6, chats: _7!, counters: _8, users: _9!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + return Api.payments.ResaleStarGifts.resaleStarGifts(flags: _1!, count: _2!, gifts: _3!, nextOffset: _4, attributes: _5, attributesHash: _6, chats: _7!, counters: _8, users: _9!) } } @@ -186,12 +374,9 @@ public extension Api.payments { } } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - if _c1 && _c2 { - return Api.payments.SavedInfo.savedInfo(flags: _1!, savedInfo: _2) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.payments.SavedInfo.savedInfo(flags: _1!, savedInfo: _2) } } @@ -266,12 +451,14 @@ public extension Api.payments { let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil let _c6 = _6 != nil let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.payments.SavedStarGifts.savedStarGifts(flags: _1!, count: _2!, chatNotificationsEnabled: _3, gifts: _4!, nextOffset: _5, chats: _6!, users: _7!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.payments.SavedStarGifts.savedStarGifts(flags: _1!, count: _2!, chatNotificationsEnabled: _3, gifts: _4!, nextOffset: _5, chats: _6!, users: _7!) } } @@ -337,12 +524,10 @@ public extension Api.payments { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.payments.StarGiftActiveAuctions.starGiftActiveAuctions(auctions: _1!, users: _2!, chats: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.payments.StarGiftActiveAuctions.starGiftActiveAuctions(auctions: _1!, users: _2!, chats: _3!) } public static func parse_starGiftActiveAuctionsNotModified(_ reader: BufferReader) -> StarGiftActiveAuctions? { return Api.payments.StarGiftActiveAuctions.starGiftActiveAuctionsNotModified @@ -402,12 +587,10 @@ public extension Api.payments { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.payments.StarGiftAuctionAcquiredGifts.starGiftAuctionAcquiredGifts(gifts: _1!, users: _2!, chats: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.payments.StarGiftAuctionAcquiredGifts.starGiftAuctionAcquiredGifts(gifts: _1!, users: _2!, chats: _3!) } } @@ -476,12 +659,13 @@ public extension Api.payments { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.payments.StarGiftAuctionState.starGiftAuctionState(gift: _1!, state: _2!, userState: _3!, timeout: _4!, users: _5!, chats: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.payments.StarGiftAuctionState.starGiftAuctionState(gift: _1!, state: _2!, userState: _3!, timeout: _4!, users: _5!, chats: _6!) } } @@ -527,12 +711,8 @@ public extension Api.payments { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarGiftCollection.self) } let _c1 = _1 != nil - if _c1 { - return Api.payments.StarGiftCollections.starGiftCollections(collections: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.payments.StarGiftCollections.starGiftCollections(collections: _1!) } public static func parse_starGiftCollectionsNotModified(_ reader: BufferReader) -> StarGiftCollections? { return Api.payments.StarGiftCollections.starGiftCollectionsNotModified @@ -572,12 +752,8 @@ public extension Api.payments { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarGiftAttribute.self) } let _c1 = _1 != nil - if _c1 { - return Api.payments.StarGiftUpgradeAttributes.starGiftUpgradeAttributes(attributes: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.payments.StarGiftUpgradeAttributes.starGiftUpgradeAttributes(attributes: _1!) } } @@ -634,12 +810,10 @@ public extension Api.payments { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.payments.StarGiftUpgradePreview.starGiftUpgradePreview(sampleAttributes: _1!, prices: _2!, nextPrices: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.payments.StarGiftUpgradePreview.starGiftUpgradePreview(sampleAttributes: _1!, prices: _2!, nextPrices: _3!) } } @@ -670,12 +844,8 @@ public extension Api.payments { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.payments.StarGiftWithdrawalUrl.starGiftWithdrawalUrl(url: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.payments.StarGiftWithdrawalUrl.starGiftWithdrawalUrl(url: _1!) } } @@ -745,12 +915,11 @@ public extension Api.payments { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.payments.StarGifts.starGifts(hash: _1!, gifts: _2!, chats: _3!, users: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.payments.StarGifts.starGifts(hash: _1!, gifts: _2!, chats: _3!, users: _4!) } public static func parse_starGiftsNotModified(_ reader: BufferReader) -> StarGifts? { return Api.payments.StarGifts.starGiftsNotModified @@ -784,12 +953,8 @@ public extension Api.payments { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.payments.StarsRevenueAdsAccountUrl.starsRevenueAdsAccountUrl(url: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.payments.StarsRevenueAdsAccountUrl.starsRevenueAdsAccountUrl(url: _1!) } } @@ -842,12 +1007,12 @@ public extension Api.payments { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.payments.StarsRevenueStats.starsRevenueStats(flags: _1!, topHoursGraph: _2, revenueGraph: _3!, status: _4!, usdRate: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.payments.StarsRevenueStats.starsRevenueStats(flags: _1!, topHoursGraph: _2, revenueGraph: _3!, status: _4!, usdRate: _5!) } } @@ -878,12 +1043,8 @@ public extension Api.payments { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.payments.StarsRevenueWithdrawalUrl.starsRevenueWithdrawalUrl(url: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.payments.StarsRevenueWithdrawalUrl.starsRevenueWithdrawalUrl(url: _1!) } } @@ -972,12 +1133,16 @@ public extension Api.payments { let _c7 = (Int(_1!) & Int(1 << 0) == 0) || _7 != nil let _c8 = _8 != nil let _c9 = _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.payments.StarsStatus.starsStatus(flags: _1!, balance: _2!, subscriptions: _3, subscriptionsNextOffset: _4, subscriptionsMissingBalance: _5, history: _6, nextOffset: _7, chats: _8!, users: _9!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + return Api.payments.StarsStatus.starsStatus(flags: _1!, balance: _2!, subscriptions: _3, subscriptionsNextOffset: _4, subscriptionsMissingBalance: _5, history: _6, nextOffset: _7, chats: _8!, users: _9!) } } @@ -1036,12 +1201,12 @@ public extension Api.payments { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.payments.SuggestedStarRefBots.suggestedStarRefBots(flags: _1!, count: _2!, suggestedBots: _3!, users: _4!, nextOffset: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.payments.SuggestedStarRefBots.suggestedStarRefBots(flags: _1!, count: _2!, suggestedBots: _3!, users: _4!, nextOffset: _5) } } @@ -1094,12 +1259,10 @@ public extension Api.payments { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.payments.UniqueStarGift.uniqueStarGift(gift: _1!, chats: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.payments.UniqueStarGift.uniqueStarGift(gift: _1!, chats: _2!, users: _3!) } } @@ -1178,12 +1341,20 @@ public extension Api.payments { let _c11 = (Int(_1!) & Int(1 << 4) == 0) || _11 != nil let _c12 = (Int(_1!) & Int(1 << 5) == 0) || _12 != nil let _c13 = (Int(_1!) & Int(1 << 5) == 0) || _13 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 { - return Api.payments.UniqueStarGiftValueInfo.uniqueStarGiftValueInfo(flags: _1!, currency: _2!, value: _3!, initialSaleDate: _4!, initialSaleStars: _5!, initialSalePrice: _6!, lastSaleDate: _7, lastSalePrice: _8, floorPrice: _9, averagePrice: _10, listedCount: _11, fragmentListedCount: _12, fragmentListedUrl: _13) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + if !_c13 { return nil } + return Api.payments.UniqueStarGiftValueInfo.uniqueStarGiftValueInfo(flags: _1!, currency: _2!, value: _3!, initialSaleDate: _4!, initialSaleStars: _5!, initialSalePrice: _6!, lastSaleDate: _7, lastSalePrice: _8, floorPrice: _9, averagePrice: _10, listedCount: _11, fragmentListedCount: _12, fragmentListedUrl: _13) } } @@ -1228,12 +1399,10 @@ public extension Api.payments { let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.payments.ValidatedRequestedInfo.validatedRequestedInfo(flags: _1!, id: _2, shippingOptions: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.payments.ValidatedRequestedInfo.validatedRequestedInfo(flags: _1!, id: _2, shippingOptions: _3) } } @@ -1264,12 +1433,8 @@ public extension Api.phone { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.phone.ExportedGroupCallInvite.exportedGroupCallInvite(link: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.phone.ExportedGroupCallInvite.exportedGroupCallInvite(link: _1!) } } @@ -1336,12 +1501,12 @@ public extension Api.phone { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.phone.GroupCall.groupCall(call: _1!, participants: _2!, participantsNextOffset: _3!, chats: _4!, users: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.phone.GroupCall.groupCall(call: _1!, participants: _2!, participantsNextOffset: _3!, chats: _4!, users: _5!) } } @@ -1402,12 +1567,11 @@ public extension Api.phone { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.phone.GroupCallStars.groupCallStars(totalStars: _1!, topDonors: _2!, chats: _3!, users: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.phone.GroupCallStars.groupCallStars(totalStars: _1!, topDonors: _2!, chats: _3!, users: _4!) } } @@ -1444,12 +1608,8 @@ public extension Api.phone { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.GroupCallStreamChannel.self) } let _c1 = _1 != nil - if _c1 { - return Api.phone.GroupCallStreamChannels.groupCallStreamChannels(channels: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.phone.GroupCallStreamChannels.groupCallStreamChannels(channels: _1!) } } @@ -1484,12 +1644,9 @@ public extension Api.phone { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.phone.GroupCallStreamRtmpUrl.groupCallStreamRtmpUrl(url: _1!, key: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.phone.GroupCallStreamRtmpUrl.groupCallStreamRtmpUrl(url: _1!, key: _2!) } } @@ -1558,122 +1715,13 @@ public extension Api.phone { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.phone.GroupParticipants.groupParticipants(count: _1!, participants: _2!, nextOffset: _3!, chats: _4!, users: _5!, version: _6!) - } - else { - return nil - } - } - - } -} -public extension Api.phone { - enum JoinAsPeers: TypeConstructorDescription { - case joinAsPeers(peers: [Api.Peer], chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .joinAsPeers(let peers, let chats, let users): - if boxed { - buffer.appendInt32(-1343921601) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(peers.count)) - for item in peers { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .joinAsPeers(let peers, let chats, let users): - return ("joinAsPeers", [("peers", peers as Any), ("chats", chats as Any), ("users", users as Any)]) - } - } - - public static func parse_joinAsPeers(_ reader: BufferReader) -> JoinAsPeers? { - var _1: [Api.Peer]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) - } - var _2: [Api.Chat]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _3: [Api.User]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.phone.JoinAsPeers.joinAsPeers(peers: _1!, chats: _2!, users: _3!) - } - else { - return nil - } - } - - } -} -public extension Api.phone { - enum PhoneCall: TypeConstructorDescription { - case phoneCall(phoneCall: Api.PhoneCall, users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .phoneCall(let phoneCall, let users): - if boxed { - buffer.appendInt32(-326966976) - } - phoneCall.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .phoneCall(let phoneCall, let users): - return ("phoneCall", [("phoneCall", phoneCall as Any), ("users", users as Any)]) - } - } - - public static func parse_phoneCall(_ reader: BufferReader) -> PhoneCall? { - var _1: Api.PhoneCall? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.PhoneCall - } - var _2: [Api.User]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.phone.PhoneCall.phoneCall(phoneCall: _1!, users: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.phone.GroupParticipants.groupParticipants(count: _1!, participants: _2!, nextOffset: _3!, chats: _4!, users: _5!, version: _6!) } } diff --git a/submodules/TelegramApi/Sources/Api37.swift b/submodules/TelegramApi/Sources/Api37.swift index 3b5d2415..c9b4cfd1 100644 --- a/submodules/TelegramApi/Sources/Api37.swift +++ b/submodules/TelegramApi/Sources/Api37.swift @@ -1,3 +1,108 @@ +public extension Api.phone { + enum JoinAsPeers: TypeConstructorDescription { + case joinAsPeers(peers: [Api.Peer], chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .joinAsPeers(let peers, let chats, let users): + if boxed { + buffer.appendInt32(-1343921601) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(peers.count)) + for item in peers { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .joinAsPeers(let peers, let chats, let users): + return ("joinAsPeers", [("peers", peers as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_joinAsPeers(_ reader: BufferReader) -> JoinAsPeers? { + var _1: [Api.Peer]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self) + } + var _2: [Api.Chat]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _3: [Api.User]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.phone.JoinAsPeers.joinAsPeers(peers: _1!, chats: _2!, users: _3!) + } + + } +} +public extension Api.phone { + enum PhoneCall: TypeConstructorDescription { + case phoneCall(phoneCall: Api.PhoneCall, users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .phoneCall(let phoneCall, let users): + if boxed { + buffer.appendInt32(-326966976) + } + phoneCall.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .phoneCall(let phoneCall, let users): + return ("phoneCall", [("phoneCall", phoneCall as Any), ("users", users as Any)]) + } + } + + public static func parse_phoneCall(_ reader: BufferReader) -> PhoneCall? { + var _1: Api.PhoneCall? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.PhoneCall + } + var _2: [Api.User]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if !_c1 { return nil } + if !_c2 { return nil } + return Api.phone.PhoneCall.phoneCall(phoneCall: _1!, users: _2!) + } + + } +} public extension Api.photos { enum Photo: TypeConstructorDescription { case photo(photo: Api.Photo, users: [Api.User]) @@ -36,12 +141,9 @@ public extension Api.photos { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.photos.Photo.photo(photo: _1!, users: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.photos.Photo.photo(photo: _1!, users: _2!) } } @@ -107,12 +209,9 @@ public extension Api.photos { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.photos.Photos.photos(photos: _1!, users: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.photos.Photos.photos(photos: _1!, users: _2!) } public static func parse_photosSlice(_ reader: BufferReader) -> Photos? { var _1: Int32? @@ -128,12 +227,10 @@ public extension Api.photos { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.photos.Photos.photosSlice(count: _1!, photos: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.photos.Photos.photosSlice(count: _1!, photos: _2!, users: _3!) } } @@ -192,12 +289,12 @@ public extension Api.premium { let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.premium.BoostsList.boostsList(flags: _1!, count: _2!, boosts: _3!, nextOffset: _4, users: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.premium.BoostsList.boostsList(flags: _1!, count: _2!, boosts: _3!, nextOffset: _4, users: _5!) } } @@ -278,12 +375,17 @@ public extension Api.premium { let _c8 = _8 != nil let _c9 = (Int(_1!) & Int(1 << 3) == 0) || _9 != nil let _c10 = (Int(_1!) & Int(1 << 2) == 0) || _10 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 { - return Api.premium.BoostsStatus.boostsStatus(flags: _1!, level: _2!, currentLevelBoosts: _3!, boosts: _4!, giftBoosts: _5, nextLevelBoosts: _6, premiumAudience: _7, boostUrl: _8!, prepaidGiveaways: _9, myBoostSlots: _10) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + return Api.premium.BoostsStatus.boostsStatus(flags: _1!, level: _2!, currentLevelBoosts: _3!, boosts: _4!, giftBoosts: _5, nextLevelBoosts: _6, premiumAudience: _7, boostUrl: _8!, prepaidGiveaways: _9, myBoostSlots: _10) } } @@ -340,12 +442,10 @@ public extension Api.premium { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.premium.MyBoosts.myBoosts(myBoosts: _1!, chats: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.premium.MyBoosts.myBoosts(myBoosts: _1!, chats: _2!, users: _3!) } } @@ -380,12 +480,9 @@ public extension Api.smsjobs { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.smsjobs.EligibilityToJoin.eligibleToJoin(termsUrl: _1!, monthlySentSms: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.smsjobs.EligibilityToJoin.eligibleToJoin(termsUrl: _1!, monthlySentSms: _2!) } } @@ -444,12 +541,15 @@ public extension Api.smsjobs { let _c6 = _6 != nil let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil let _c8 = _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.smsjobs.Status.status(flags: _1!, recentSent: _2!, recentSince: _3!, recentRemains: _4!, totalSent: _5!, totalSince: _6!, lastGiftSlug: _7, termsUrl: _8!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.smsjobs.Status.status(flags: _1!, recentSent: _2!, recentSince: _3!, recentRemains: _4!, totalSent: _5!, totalSince: _6!, lastGiftSlug: _7, termsUrl: _8!) } } @@ -612,12 +712,29 @@ public extension Api.stats { let _c20 = _20 != nil let _c21 = _21 != nil let _c22 = _22 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 { - return Api.stats.BroadcastStats.broadcastStats(period: _1!, followers: _2!, viewsPerPost: _3!, sharesPerPost: _4!, reactionsPerPost: _5!, viewsPerStory: _6!, sharesPerStory: _7!, reactionsPerStory: _8!, enabledNotifications: _9!, growthGraph: _10!, followersGraph: _11!, muteGraph: _12!, topHoursGraph: _13!, interactionsGraph: _14!, ivInteractionsGraph: _15!, viewsBySourceGraph: _16!, newFollowersBySourceGraph: _17!, languagesGraph: _18!, reactionsByEmotionGraph: _19!, storyInteractionsGraph: _20!, storyReactionsByEmotionGraph: _21!, recentPostsInteractions: _22!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + if !_c13 { return nil } + if !_c14 { return nil } + if !_c15 { return nil } + if !_c16 { return nil } + if !_c17 { return nil } + if !_c18 { return nil } + if !_c19 { return nil } + if !_c20 { return nil } + if !_c21 { return nil } + if !_c22 { return nil } + return Api.stats.BroadcastStats.broadcastStats(period: _1!, followers: _2!, viewsPerPost: _3!, sharesPerPost: _4!, reactionsPerPost: _5!, viewsPerStory: _6!, sharesPerStory: _7!, reactionsPerStory: _8!, enabledNotifications: _9!, growthGraph: _10!, followersGraph: _11!, muteGraph: _12!, topHoursGraph: _13!, interactionsGraph: _14!, ivInteractionsGraph: _15!, viewsBySourceGraph: _16!, newFollowersBySourceGraph: _17!, languagesGraph: _18!, reactionsByEmotionGraph: _19!, storyInteractionsGraph: _20!, storyReactionsByEmotionGraph: _21!, recentPostsInteractions: _22!) } } @@ -762,12 +879,24 @@ public extension Api.stats { let _c15 = _15 != nil let _c16 = _16 != nil let _c17 = _17 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 { - return Api.stats.MegagroupStats.megagroupStats(period: _1!, members: _2!, messages: _3!, viewers: _4!, posters: _5!, growthGraph: _6!, membersGraph: _7!, newMembersBySourceGraph: _8!, languagesGraph: _9!, messagesGraph: _10!, actionsGraph: _11!, topHoursGraph: _12!, weekdaysGraph: _13!, topPosters: _14!, topAdmins: _15!, topInviters: _16!, users: _17!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + if !_c13 { return nil } + if !_c14 { return nil } + if !_c15 { return nil } + if !_c16 { return nil } + if !_c17 { return nil } + return Api.stats.MegagroupStats.megagroupStats(period: _1!, members: _2!, messages: _3!, viewers: _4!, posters: _5!, growthGraph: _6!, membersGraph: _7!, newMembersBySourceGraph: _8!, languagesGraph: _9!, messagesGraph: _10!, actionsGraph: _11!, topHoursGraph: _12!, weekdaysGraph: _13!, topPosters: _14!, topAdmins: _15!, topInviters: _16!, users: _17!) } } @@ -806,12 +935,9 @@ public extension Api.stats { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.stats.MessageStats.messageStats(viewsGraph: _1!, reactionsByEmotionGraph: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.stats.MessageStats.messageStats(viewsGraph: _1!, reactionsByEmotionGraph: _2!) } } @@ -880,12 +1006,13 @@ public extension Api.stats { let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil let _c5 = _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.stats.PublicForwards.publicForwards(flags: _1!, count: _2!, forwards: _3!, nextOffset: _4, chats: _5!, users: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.stats.PublicForwards.publicForwards(flags: _1!, count: _2!, forwards: _3!, nextOffset: _4, chats: _5!, users: _6!) } } @@ -924,12 +1051,9 @@ public extension Api.stats { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.stats.StoryStats.storyStats(viewsGraph: _1!, reactionsByEmotionGraph: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.stats.StoryStats.storyStats(viewsGraph: _1!, reactionsByEmotionGraph: _2!) } } @@ -960,12 +1084,8 @@ public extension Api.stickers { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.stickers.SuggestedShortName.suggestedShortName(shortName: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.stickers.SuggestedShortName.suggestedShortName(shortName: _1!) } } @@ -1151,12 +1271,9 @@ public extension Api.stories { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.stories.Albums.albums(hash: _1!, albums: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.stories.Albums.albums(hash: _1!, albums: _2!) } public static func parse_albumsNotModified(_ reader: BufferReader) -> Albums? { return Api.stories.Albums.albumsNotModified @@ -1245,12 +1362,14 @@ public extension Api.stories { let _c5 = _5 != nil let _c6 = _6 != nil let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.stories.AllStories.allStories(flags: _1!, count: _2!, state: _3!, peerStories: _4!, chats: _5!, users: _6!, stealthMode: _7!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.stories.AllStories.allStories(flags: _1!, count: _2!, state: _3!, peerStories: _4!, chats: _5!, users: _6!, stealthMode: _7!) } public static func parse_allStoriesNotModified(_ reader: BufferReader) -> AllStories? { var _1: Int32? @@ -1264,12 +1383,10 @@ public extension Api.stories { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.stories.AllStories.allStoriesNotModified(flags: _1!, state: _2!, stealthMode: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.stories.AllStories.allStoriesNotModified(flags: _1!, state: _2!, stealthMode: _3!) } } @@ -1300,12 +1417,8 @@ public extension Api.stories { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.stories.CanSendStoryCount.canSendStoryCount(countRemains: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.stories.CanSendStoryCount.canSendStoryCount(countRemains: _1!) } } @@ -1374,150 +1487,13 @@ public extension Api.stories { let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil let _c5 = _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.stories.FoundStories.foundStories(flags: _1!, count: _2!, stories: _3!, nextOffset: _4, chats: _5!, users: _6!) - } - else { - return nil - } - } - - } -} -public extension Api.stories { - enum PeerStories: TypeConstructorDescription { - case peerStories(stories: Api.PeerStories, chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .peerStories(let stories, let chats, let users): - if boxed { - buffer.appendInt32(-890861720) - } - stories.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .peerStories(let stories, let chats, let users): - return ("peerStories", [("stories", stories as Any), ("chats", chats as Any), ("users", users as Any)]) - } - } - - public static func parse_peerStories(_ reader: BufferReader) -> PeerStories? { - var _1: Api.PeerStories? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.PeerStories - } - var _2: [Api.Chat]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _3: [Api.User]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.stories.PeerStories.peerStories(stories: _1!, chats: _2!, users: _3!) - } - else { - return nil - } - } - - } -} -public extension Api.stories { - enum Stories: TypeConstructorDescription { - case stories(flags: Int32, count: Int32, stories: [Api.StoryItem], pinnedToTop: [Int32]?, chats: [Api.Chat], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .stories(let flags, let count, let stories, let pinnedToTop, let chats, let users): - if boxed { - buffer.appendInt32(1673780490) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(count, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(stories.count)) - for item in stories { - item.serialize(buffer, true) - } - if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(pinnedToTop!.count)) - for item in pinnedToTop! { - serializeInt32(item, buffer: buffer, boxed: false) - }} - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(chats.count)) - for item in chats { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .stories(let flags, let count, let stories, let pinnedToTop, let chats, let users): - return ("stories", [("flags", flags as Any), ("count", count as Any), ("stories", stories as Any), ("pinnedToTop", pinnedToTop as Any), ("chats", chats as Any), ("users", users as Any)]) - } - } - - public static func parse_stories(_ reader: BufferReader) -> Stories? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: [Api.StoryItem]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryItem.self) - } - var _4: [Int32]? - if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() { - _4 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) - } } - var _5: [Api.Chat]? - if let _ = reader.readInt32() { - _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) - } - var _6: [Api.User]? - if let _ = reader.readInt32() { - _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.stories.Stories.stories(flags: _1!, count: _2!, stories: _3!, pinnedToTop: _4, chats: _5!, users: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.stories.FoundStories.foundStories(flags: _1!, count: _2!, stories: _3!, nextOffset: _4, chats: _5!, users: _6!) } } diff --git a/submodules/TelegramApi/Sources/Api38.swift b/submodules/TelegramApi/Sources/Api38.swift index 77f635e8..68d4e320 100644 --- a/submodules/TelegramApi/Sources/Api38.swift +++ b/submodules/TelegramApi/Sources/Api38.swift @@ -1,3 +1,140 @@ +public extension Api.stories { + enum PeerStories: TypeConstructorDescription { + case peerStories(stories: Api.PeerStories, chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .peerStories(let stories, let chats, let users): + if boxed { + buffer.appendInt32(-890861720) + } + stories.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .peerStories(let stories, let chats, let users): + return ("peerStories", [("stories", stories as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_peerStories(_ reader: BufferReader) -> PeerStories? { + var _1: Api.PeerStories? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.PeerStories + } + var _2: [Api.Chat]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _3: [Api.User]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.stories.PeerStories.peerStories(stories: _1!, chats: _2!, users: _3!) + } + + } +} +public extension Api.stories { + enum Stories: TypeConstructorDescription { + case stories(flags: Int32, count: Int32, stories: [Api.StoryItem], pinnedToTop: [Int32]?, chats: [Api.Chat], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .stories(let flags, let count, let stories, let pinnedToTop, let chats, let users): + if boxed { + buffer.appendInt32(1673780490) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(count, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(stories.count)) + for item in stories { + item.serialize(buffer, true) + } + if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(pinnedToTop!.count)) + for item in pinnedToTop! { + serializeInt32(item, buffer: buffer, boxed: false) + }} + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(chats.count)) + for item in chats { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .stories(let flags, let count, let stories, let pinnedToTop, let chats, let users): + return ("stories", [("flags", flags as Any), ("count", count as Any), ("stories", stories as Any), ("pinnedToTop", pinnedToTop as Any), ("chats", chats as Any), ("users", users as Any)]) + } + } + + public static func parse_stories(_ reader: BufferReader) -> Stories? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: [Api.StoryItem]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StoryItem.self) + } + var _4: [Int32]? + if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } } + var _5: [Api.Chat]? + if let _ = reader.readInt32() { + _5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self) + } + var _6: [Api.User]? + if let _ = reader.readInt32() { + _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.stories.Stories.stories(flags: _1!, count: _2!, stories: _3!, pinnedToTop: _4, chats: _5!, users: _6!) + } + + } +} public extension Api.stories { enum StoryReactionsList: TypeConstructorDescription { case storyReactionsList(flags: Int32, count: Int32, reactions: [Api.StoryReaction], chats: [Api.Chat], users: [Api.User], nextOffset: String?) @@ -62,12 +199,13 @@ public extension Api.stories { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.stories.StoryReactionsList.storyReactionsList(flags: _1!, count: _2!, reactions: _3!, chats: _4!, users: _5!, nextOffset: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.stories.StoryReactionsList.storyReactionsList(flags: _1!, count: _2!, reactions: _3!, chats: _4!, users: _5!, nextOffset: _6) } } @@ -114,12 +252,9 @@ public extension Api.stories { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.stories.StoryViews.storyViews(views: _1!, users: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.stories.StoryViews.storyViews(views: _1!, users: _2!) } } @@ -200,12 +335,16 @@ public extension Api.stories { let _c7 = _7 != nil let _c8 = _8 != nil let _c9 = (Int(_1!) & Int(1 << 0) == 0) || _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.stories.StoryViewsList.storyViewsList(flags: _1!, count: _2!, viewsCount: _3!, forwardsCount: _4!, reactionsCount: _5!, views: _6!, chats: _7!, users: _8!, nextOffset: _9) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + return Api.stories.StoryViewsList.storyViewsList(flags: _1!, count: _2!, viewsCount: _3!, forwardsCount: _4!, reactionsCount: _5!, views: _6!, chats: _7!, users: _8!, nextOffset: _9) } } @@ -321,12 +460,14 @@ public extension Api.updates { let _c5 = _5 != nil let _c6 = _6 != nil let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.updates.ChannelDifference.channelDifference(flags: _1!, pts: _2!, timeout: _3, newMessages: _4!, otherUpdates: _5!, chats: _6!, users: _7!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.updates.ChannelDifference.channelDifference(flags: _1!, pts: _2!, timeout: _3, newMessages: _4!, otherUpdates: _5!, chats: _6!, users: _7!) } public static func parse_channelDifferenceEmpty(_ reader: BufferReader) -> ChannelDifference? { var _1: Int32? @@ -338,12 +479,10 @@ public extension Api.updates { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.updates.ChannelDifference.channelDifferenceEmpty(flags: _1!, pts: _2!, timeout: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.updates.ChannelDifference.channelDifferenceEmpty(flags: _1!, pts: _2!, timeout: _3) } public static func parse_channelDifferenceTooLong(_ reader: BufferReader) -> ChannelDifference? { var _1: Int32? @@ -372,12 +511,13 @@ public extension Api.updates { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.updates.ChannelDifference.channelDifferenceTooLong(flags: _1!, timeout: _2, dialog: _3!, messages: _4!, chats: _5!, users: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.updates.ChannelDifference.channelDifferenceTooLong(flags: _1!, timeout: _2, dialog: _3!, messages: _4!, chats: _5!, users: _6!) } } @@ -513,12 +653,13 @@ public extension Api.updates { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.updates.Difference.difference(newMessages: _1!, newEncryptedMessages: _2!, otherUpdates: _3!, chats: _4!, users: _5!, state: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.updates.Difference.difference(newMessages: _1!, newEncryptedMessages: _2!, otherUpdates: _3!, chats: _4!, users: _5!, state: _6!) } public static func parse_differenceEmpty(_ reader: BufferReader) -> Difference? { var _1: Int32? @@ -527,12 +668,9 @@ public extension Api.updates { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.updates.Difference.differenceEmpty(date: _1!, seq: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.updates.Difference.differenceEmpty(date: _1!, seq: _2!) } public static func parse_differenceSlice(_ reader: BufferReader) -> Difference? { var _1: [Api.Message]? @@ -565,23 +703,20 @@ public extension Api.updates { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.updates.Difference.differenceSlice(newMessages: _1!, newEncryptedMessages: _2!, otherUpdates: _3!, chats: _4!, users: _5!, intermediateState: _6!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.updates.Difference.differenceSlice(newMessages: _1!, newEncryptedMessages: _2!, otherUpdates: _3!, chats: _4!, users: _5!, intermediateState: _6!) } public static func parse_differenceTooLong(_ reader: BufferReader) -> Difference? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.updates.Difference.differenceTooLong(pts: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.updates.Difference.differenceTooLong(pts: _1!) } } @@ -628,12 +763,12 @@ public extension Api.updates { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.updates.State.state(pts: _1!, qts: _2!, date: _3!, seq: _4!, unreadCount: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.updates.State.state(pts: _1!, qts: _2!, date: _3!, seq: _4!, unreadCount: _5!) } } @@ -673,23 +808,15 @@ public extension Api.upload { var _1: Buffer? _1 = parseBytes(reader) let _c1 = _1 != nil - if _c1 { - return Api.upload.CdnFile.cdnFile(bytes: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.upload.CdnFile.cdnFile(bytes: _1!) } public static func parse_cdnFileReuploadNeeded(_ reader: BufferReader) -> CdnFile? { var _1: Buffer? _1 = parseBytes(reader) let _c1 = _1 != nil - if _c1 { - return Api.upload.CdnFile.cdnFileReuploadNeeded(requestToken: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.upload.CdnFile.cdnFileReuploadNeeded(requestToken: _1!) } } @@ -747,12 +874,10 @@ public extension Api.upload { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.upload.File.file(type: _1!, mtime: _2!, bytes: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.upload.File.file(type: _1!, mtime: _2!, bytes: _3!) } public static func parse_fileCdnRedirect(_ reader: BufferReader) -> File? { var _1: Int32? @@ -772,12 +897,12 @@ public extension Api.upload { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.upload.File.fileCdnRedirect(dcId: _1!, fileToken: _2!, encryptionKey: _3!, encryptionIv: _4!, fileHashes: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.upload.File.fileCdnRedirect(dcId: _1!, fileToken: _2!, encryptionKey: _3!, encryptionIv: _4!, fileHashes: _5!) } } @@ -826,12 +951,12 @@ public extension Api.upload { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.upload.WebFile.webFile(size: _1!, mimeType: _2!, fileType: _3!, mtime: _4!, bytes: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.upload.WebFile.webFile(size: _1!, mimeType: _2!, fileType: _3!, mtime: _4!, bytes: _5!) } } @@ -881,23 +1006,16 @@ public extension Api.users { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.users.SavedMusic.savedMusic(count: _1!, documents: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.users.SavedMusic.savedMusic(count: _1!, documents: _2!) } public static func parse_savedMusicNotModified(_ reader: BufferReader) -> SavedMusic? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.users.SavedMusic.savedMusicNotModified(count: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.users.SavedMusic.savedMusicNotModified(count: _1!) } } @@ -950,12 +1068,10 @@ public extension Api.users { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.users.UserFull.userFull(fullUser: _1!, chats: _2!, users: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.users.UserFull.userFull(fullUser: _1!, chats: _2!, users: _3!) } } @@ -1006,12 +1122,8 @@ public extension Api.users { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } let _c1 = _1 != nil - if _c1 { - return Api.users.Users.users(users: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.users.Users.users(users: _1!) } public static func parse_usersSlice(_ reader: BufferReader) -> Users? { var _1: Int32? @@ -1022,12 +1134,9 @@ public extension Api.users { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.users.Users.usersSlice(count: _1!, users: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.users.Users.usersSlice(count: _1!, users: _2!) } } diff --git a/submodules/TelegramApi/Sources/Api39.swift b/submodules/TelegramApi/Sources/Api39.swift index d421b4d6..c9e087f8 100644 --- a/submodules/TelegramApi/Sources/Api39.swift +++ b/submodules/TelegramApi/Sources/Api39.swift @@ -6245,6 +6245,21 @@ public extension Api.functions.messages { }) } } +public extension Api.functions.messages { + static func getEmojiGameInfo() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-75592537) + + return (FunctionDescription(name: "messages.getEmojiGameInfo", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.EmojiGameInfo? in + let reader = BufferReader(buffer) + var result: Api.messages.EmojiGameInfo? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.messages.EmojiGameInfo + } + return result + }) + } +} public extension Api.functions.messages { static func getEmojiGroups(hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -8991,6 +9006,24 @@ public extension Api.functions.messages { }) } } +public extension Api.functions.messages { + static func summarizeText(flags: Int32, peer: Api.InputPeer, id: Int32, toLang: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1656683294) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(id, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(toLang!, buffer: buffer, boxed: false)} + return (FunctionDescription(name: "messages.summarizeText", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("id", String(describing: id)), ("toLang", String(describing: toLang))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.TextWithEntities? in + let reader = BufferReader(buffer) + var result: Api.TextWithEntities? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.TextWithEntities + } + return result + }) + } +} public extension Api.functions.messages { static func toggleBotInAttachMenu(flags: Int32, bot: Api.InputUser, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() diff --git a/submodules/TelegramApi/Sources/Api4.swift b/submodules/TelegramApi/Sources/Api4.swift index fad4416f..33c86e99 100644 --- a/submodules/TelegramApi/Sources/Api4.swift +++ b/submodules/TelegramApi/Sources/Api4.swift @@ -24,12 +24,8 @@ public extension Api { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.ChannelAdminLogEventsFilter.channelAdminLogEventsFilter(flags: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelAdminLogEventsFilter.channelAdminLogEventsFilter(flags: _1!) } } @@ -75,12 +71,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelLocation.channelLocation(geoPoint: _1!, address: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelLocation.channelLocation(geoPoint: _1!, address: _2!) } public static func parse_channelLocationEmpty(_ reader: BufferReader) -> ChannelLocation? { return Api.ChannelLocation.channelLocationEmpty @@ -133,12 +126,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChannelMessagesFilter.channelMessagesFilter(flags: _1!, ranges: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChannelMessagesFilter.channelMessagesFilter(flags: _1!, ranges: _2!) } public static func parse_channelMessagesFilterEmpty(_ reader: BufferReader) -> ChannelMessagesFilter? { return Api.ChannelMessagesFilter.channelMessagesFilterEmpty @@ -246,12 +236,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.ChannelParticipant.channelParticipant(flags: _1!, userId: _2!, date: _3!, subscriptionUntilDate: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.ChannelParticipant.channelParticipant(flags: _1!, userId: _2!, date: _3!, subscriptionUntilDate: _4) } public static func parse_channelParticipantAdmin(_ reader: BufferReader) -> ChannelParticipant? { var _1: Int32? @@ -277,12 +266,14 @@ public extension Api { let _c5 = _5 != nil let _c6 = _6 != nil let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.ChannelParticipant.channelParticipantAdmin(flags: _1!, userId: _2!, inviterId: _3, promotedBy: _4!, date: _5!, adminRights: _6!, rank: _7) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.ChannelParticipant.channelParticipantAdmin(flags: _1!, userId: _2!, inviterId: _3, promotedBy: _4!, date: _5!, adminRights: _6!, rank: _7) } public static func parse_channelParticipantBanned(_ reader: BufferReader) -> ChannelParticipant? { var _1: Int32? @@ -304,12 +295,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.ChannelParticipant.channelParticipantBanned(flags: _1!, peer: _2!, kickedBy: _3!, date: _4!, bannedRights: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.ChannelParticipant.channelParticipantBanned(flags: _1!, peer: _2!, kickedBy: _3!, date: _4!, bannedRights: _5!) } public static func parse_channelParticipantCreator(_ reader: BufferReader) -> ChannelParticipant? { var _1: Int32? @@ -326,12 +317,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.ChannelParticipant.channelParticipantCreator(flags: _1!, userId: _2!, adminRights: _3!, rank: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.ChannelParticipant.channelParticipantCreator(flags: _1!, userId: _2!, adminRights: _3!, rank: _4) } public static func parse_channelParticipantLeft(_ reader: BufferReader) -> ChannelParticipant? { var _1: Api.Peer? @@ -339,12 +329,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Peer } let _c1 = _1 != nil - if _c1 { - return Api.ChannelParticipant.channelParticipantLeft(peer: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelParticipant.channelParticipantLeft(peer: _1!) } public static func parse_channelParticipantSelf(_ reader: BufferReader) -> ChannelParticipant? { var _1: Int32? @@ -362,12 +348,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.ChannelParticipant.channelParticipantSelf(flags: _1!, userId: _2!, inviterId: _3!, date: _4!, subscriptionUntilDate: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.ChannelParticipant.channelParticipantSelf(flags: _1!, userId: _2!, inviterId: _3!, date: _4!, subscriptionUntilDate: _5) } } @@ -466,12 +452,8 @@ public extension Api { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.ChannelParticipantsFilter.channelParticipantsBanned(q: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelParticipantsFilter.channelParticipantsBanned(q: _1!) } public static func parse_channelParticipantsBots(_ reader: BufferReader) -> ChannelParticipantsFilter? { return Api.ChannelParticipantsFilter.channelParticipantsBots @@ -480,23 +462,15 @@ public extension Api { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.ChannelParticipantsFilter.channelParticipantsContacts(q: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelParticipantsFilter.channelParticipantsContacts(q: _1!) } public static func parse_channelParticipantsKicked(_ reader: BufferReader) -> ChannelParticipantsFilter? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.ChannelParticipantsFilter.channelParticipantsKicked(q: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelParticipantsFilter.channelParticipantsKicked(q: _1!) } public static func parse_channelParticipantsMentions(_ reader: BufferReader) -> ChannelParticipantsFilter? { var _1: Int32? @@ -508,12 +482,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.ChannelParticipantsFilter.channelParticipantsMentions(flags: _1!, q: _2, topMsgId: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.ChannelParticipantsFilter.channelParticipantsMentions(flags: _1!, q: _2, topMsgId: _3) } public static func parse_channelParticipantsRecent(_ reader: BufferReader) -> ChannelParticipantsFilter? { return Api.ChannelParticipantsFilter.channelParticipantsRecent @@ -522,12 +494,8 @@ public extension Api { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.ChannelParticipantsFilter.channelParticipantsSearch(q: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChannelParticipantsFilter.channelParticipantsSearch(q: _1!) } } @@ -724,12 +692,30 @@ public extension Api { let _c21 = (Int(_2!) & Int(1 << 13) == 0) || _21 != nil let _c22 = (Int(_2!) & Int(1 << 14) == 0) || _22 != nil let _c23 = (Int(_2!) & Int(1 << 18) == 0) || _23 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 { - return Api.Chat.channel(flags: _1!, flags2: _2!, id: _3!, accessHash: _4, title: _5!, username: _6, photo: _7!, date: _8!, restrictionReason: _9, adminRights: _10, bannedRights: _11, defaultBannedRights: _12, participantsCount: _13, usernames: _14, storiesMaxId: _15, color: _16, profileColor: _17, emojiStatus: _18, level: _19, subscriptionUntilDate: _20, botVerificationIcon: _21, sendPaidMessagesStars: _22, linkedMonoforumId: _23) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + if !_c13 { return nil } + if !_c14 { return nil } + if !_c15 { return nil } + if !_c16 { return nil } + if !_c17 { return nil } + if !_c18 { return nil } + if !_c19 { return nil } + if !_c20 { return nil } + if !_c21 { return nil } + if !_c22 { return nil } + if !_c23 { return nil } + return Api.Chat.channel(flags: _1!, flags2: _2!, id: _3!, accessHash: _4, title: _5!, username: _6, photo: _7!, date: _8!, restrictionReason: _9, adminRights: _10, bannedRights: _11, defaultBannedRights: _12, participantsCount: _13, usernames: _14, storiesMaxId: _15, color: _16, profileColor: _17, emojiStatus: _18, level: _19, subscriptionUntilDate: _20, botVerificationIcon: _21, sendPaidMessagesStars: _22, linkedMonoforumId: _23) } public static func parse_channelForbidden(_ reader: BufferReader) -> Chat? { var _1: Int32? @@ -747,12 +733,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = (Int(_1!) & Int(1 << 16) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.Chat.channelForbidden(flags: _1!, id: _2!, accessHash: _3!, title: _4!, untilDate: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.Chat.channelForbidden(flags: _1!, id: _2!, accessHash: _3!, title: _4!, untilDate: _5) } public static func parse_chat(_ reader: BufferReader) -> Chat? { var _1: Int32? @@ -793,23 +779,24 @@ public extension Api { let _c8 = (Int(_1!) & Int(1 << 6) == 0) || _8 != nil let _c9 = (Int(_1!) & Int(1 << 14) == 0) || _9 != nil let _c10 = (Int(_1!) & Int(1 << 18) == 0) || _10 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 { - return Api.Chat.chat(flags: _1!, id: _2!, title: _3!, photo: _4!, participantsCount: _5!, date: _6!, version: _7!, migratedTo: _8, adminRights: _9, defaultBannedRights: _10) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + return Api.Chat.chat(flags: _1!, id: _2!, title: _3!, photo: _4!, participantsCount: _5!, date: _6!, version: _7!, migratedTo: _8, adminRights: _9, defaultBannedRights: _10) } public static func parse_chatEmpty(_ reader: BufferReader) -> Chat? { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.Chat.chatEmpty(id: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Chat.chatEmpty(id: _1!) } public static func parse_chatForbidden(_ reader: BufferReader) -> Chat? { var _1: Int64? @@ -818,12 +805,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Chat.chatForbidden(id: _1!, title: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Chat.chatForbidden(id: _1!, title: _2!) } } @@ -854,12 +838,8 @@ public extension Api { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.ChatAdminRights.chatAdminRights(flags: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChatAdminRights.chatAdminRights(flags: _1!) } } @@ -898,12 +878,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.ChatAdminWithInvites.chatAdminWithInvites(adminId: _1!, invitesCount: _2!, revokedInvitesCount: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.ChatAdminWithInvites.chatAdminWithInvites(adminId: _1!, invitesCount: _2!, revokedInvitesCount: _3!) } } @@ -938,12 +916,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChatBannedRights.chatBannedRights(flags: _1!, untilDate: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChatBannedRights.chatBannedRights(flags: _1!, untilDate: _2!) } } @@ -1238,12 +1213,54 @@ public extension Api { let _c45 = (Int(_2!) & Int(1 << 18) == 0) || _45 != nil let _c46 = (Int(_2!) & Int(1 << 21) == 0) || _46 != nil let _c47 = (Int(_2!) & Int(1 << 22) == 0) || _47 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 && _c28 && _c29 && _c30 && _c31 && _c32 && _c33 && _c34 && _c35 && _c36 && _c37 && _c38 && _c39 && _c40 && _c41 && _c42 && _c43 && _c44 && _c45 && _c46 && _c47 { - return Api.ChatFull.channelFull(flags: _1!, flags2: _2!, id: _3!, about: _4!, participantsCount: _5, adminsCount: _6, kickedCount: _7, bannedCount: _8, onlineCount: _9, readInboxMaxId: _10!, readOutboxMaxId: _11!, unreadCount: _12!, chatPhoto: _13!, notifySettings: _14!, exportedInvite: _15, botInfo: _16!, migratedFromChatId: _17, migratedFromMaxId: _18, pinnedMsgId: _19, stickerset: _20, availableMinId: _21, folderId: _22, linkedChatId: _23, location: _24, slowmodeSeconds: _25, slowmodeNextSendDate: _26, statsDc: _27, pts: _28!, call: _29, ttlPeriod: _30, pendingSuggestions: _31, groupcallDefaultJoinAs: _32, themeEmoticon: _33, requestsPending: _34, recentRequesters: _35, defaultSendAs: _36, availableReactions: _37, reactionsLimit: _38, stories: _39, wallpaper: _40, boostsApplied: _41, boostsUnrestrict: _42, emojiset: _43, botVerification: _44, stargiftsCount: _45, sendPaidMessagesStars: _46, mainTab: _47) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + if !_c13 { return nil } + if !_c14 { return nil } + if !_c15 { return nil } + if !_c16 { return nil } + if !_c17 { return nil } + if !_c18 { return nil } + if !_c19 { return nil } + if !_c20 { return nil } + if !_c21 { return nil } + if !_c22 { return nil } + if !_c23 { return nil } + if !_c24 { return nil } + if !_c25 { return nil } + if !_c26 { return nil } + if !_c27 { return nil } + if !_c28 { return nil } + if !_c29 { return nil } + if !_c30 { return nil } + if !_c31 { return nil } + if !_c32 { return nil } + if !_c33 { return nil } + if !_c34 { return nil } + if !_c35 { return nil } + if !_c36 { return nil } + if !_c37 { return nil } + if !_c38 { return nil } + if !_c39 { return nil } + if !_c40 { return nil } + if !_c41 { return nil } + if !_c42 { return nil } + if !_c43 { return nil } + if !_c44 { return nil } + if !_c45 { return nil } + if !_c46 { return nil } + if !_c47 { return nil } + return Api.ChatFull.channelFull(flags: _1!, flags2: _2!, id: _3!, about: _4!, participantsCount: _5, adminsCount: _6, kickedCount: _7, bannedCount: _8, onlineCount: _9, readInboxMaxId: _10!, readOutboxMaxId: _11!, unreadCount: _12!, chatPhoto: _13!, notifySettings: _14!, exportedInvite: _15, botInfo: _16!, migratedFromChatId: _17, migratedFromMaxId: _18, pinnedMsgId: _19, stickerset: _20, availableMinId: _21, folderId: _22, linkedChatId: _23, location: _24, slowmodeSeconds: _25, slowmodeNextSendDate: _26, statsDc: _27, pts: _28!, call: _29, ttlPeriod: _30, pendingSuggestions: _31, groupcallDefaultJoinAs: _32, themeEmoticon: _33, requestsPending: _34, recentRequesters: _35, defaultSendAs: _36, availableReactions: _37, reactionsLimit: _38, stories: _39, wallpaper: _40, boostsApplied: _41, boostsUnrestrict: _42, emojiset: _43, botVerification: _44, stargiftsCount: _45, sendPaidMessagesStars: _46, mainTab: _47) } public static func parse_chatFull(_ reader: BufferReader) -> ChatFull? { var _1: Int32? @@ -1318,12 +1335,25 @@ public extension Api { let _c16 = (Int(_1!) & Int(1 << 17) == 0) || _16 != nil let _c17 = (Int(_1!) & Int(1 << 18) == 0) || _17 != nil let _c18 = (Int(_1!) & Int(1 << 20) == 0) || _18 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 { - return Api.ChatFull.chatFull(flags: _1!, id: _2!, about: _3!, participants: _4!, chatPhoto: _5, notifySettings: _6!, exportedInvite: _7, botInfo: _8, pinnedMsgId: _9, folderId: _10, call: _11, ttlPeriod: _12, groupcallDefaultJoinAs: _13, themeEmoticon: _14, requestsPending: _15, recentRequesters: _16, availableReactions: _17, reactionsLimit: _18) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + if !_c13 { return nil } + if !_c14 { return nil } + if !_c15 { return nil } + if !_c16 { return nil } + if !_c17 { return nil } + if !_c18 { return nil } + return Api.ChatFull.chatFull(flags: _1!, id: _2!, about: _3!, participants: _4!, chatPhoto: _5, notifySettings: _6!, exportedInvite: _7, botInfo: _8, pinnedMsgId: _9, folderId: _10, call: _11, ttlPeriod: _12, groupcallDefaultJoinAs: _13, themeEmoticon: _14, requestsPending: _15, recentRequesters: _16, availableReactions: _17, reactionsLimit: _18) } } @@ -1421,12 +1451,17 @@ public extension Api { let _c8 = (Int(_1!) & Int(1 << 10) == 0) || _8 != nil let _c9 = (Int(_1!) & Int(1 << 12) == 0) || _9 != nil let _c10 = (Int(_1!) & Int(1 << 13) == 0) || _10 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 { - return Api.ChatInvite.chatInvite(flags: _1!, title: _2!, about: _3, photo: _4!, participantsCount: _5!, participants: _6, color: _7!, subscriptionPricing: _8, subscriptionFormId: _9, botVerification: _10) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + return Api.ChatInvite.chatInvite(flags: _1!, title: _2!, about: _3, photo: _4!, participantsCount: _5!, participants: _6, color: _7!, subscriptionPricing: _8, subscriptionFormId: _9, botVerification: _10) } public static func parse_chatInviteAlready(_ reader: BufferReader) -> ChatInvite? { var _1: Api.Chat? @@ -1434,12 +1469,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Chat } let _c1 = _1 != nil - if _c1 { - return Api.ChatInvite.chatInviteAlready(chat: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChatInvite.chatInviteAlready(chat: _1!) } public static func parse_chatInvitePeek(_ reader: BufferReader) -> ChatInvite? { var _1: Api.Chat? @@ -1450,12 +1481,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChatInvite.chatInvitePeek(chat: _1!, expires: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChatInvite.chatInvitePeek(chat: _1!, expires: _2!) } } diff --git a/submodules/TelegramApi/Sources/Api5.swift b/submodules/TelegramApi/Sources/Api5.swift index 2ce916e3..87fba995 100644 --- a/submodules/TelegramApi/Sources/Api5.swift +++ b/submodules/TelegramApi/Sources/Api5.swift @@ -40,12 +40,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.ChatInviteImporter.chatInviteImporter(flags: _1!, userId: _2!, date: _3!, about: _4, approvedBy: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.ChatInviteImporter.chatInviteImporter(flags: _1!, userId: _2!, date: _3!, about: _4, approvedBy: _5) } } @@ -76,12 +76,8 @@ public extension Api { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.ChatOnlines.chatOnlines(onlines: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChatOnlines.chatOnlines(onlines: _1!) } } @@ -140,12 +136,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.ChatParticipant.chatParticipant(userId: _1!, inviterId: _2!, date: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.ChatParticipant.chatParticipant(userId: _1!, inviterId: _2!, date: _3!) } public static func parse_chatParticipantAdmin(_ reader: BufferReader) -> ChatParticipant? { var _1: Int64? @@ -157,23 +151,17 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.ChatParticipant.chatParticipantAdmin(userId: _1!, inviterId: _2!, date: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.ChatParticipant.chatParticipantAdmin(userId: _1!, inviterId: _2!, date: _3!) } public static func parse_chatParticipantCreator(_ reader: BufferReader) -> ChatParticipant? { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.ChatParticipant.chatParticipantCreator(userId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChatParticipant.chatParticipantCreator(userId: _1!) } } @@ -229,12 +217,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.ChatParticipants.chatParticipants(chatId: _1!, participants: _2!, version: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.ChatParticipants.chatParticipants(chatId: _1!, participants: _2!, version: _3!) } public static func parse_chatParticipantsForbidden(_ reader: BufferReader) -> ChatParticipants? { var _1: Int32? @@ -248,12 +234,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.ChatParticipants.chatParticipantsForbidden(flags: _1!, chatId: _2!, selfParticipant: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.ChatParticipants.chatParticipantsForbidden(flags: _1!, chatId: _2!, selfParticipant: _3) } } @@ -305,12 +289,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.ChatPhoto.chatPhoto(flags: _1!, photoId: _2!, strippedThumb: _3, dcId: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.ChatPhoto.chatPhoto(flags: _1!, photoId: _2!, strippedThumb: _3, dcId: _4!) } public static func parse_chatPhotoEmpty(_ reader: BufferReader) -> ChatPhoto? { return Api.ChatPhoto.chatPhotoEmpty @@ -366,12 +349,8 @@ public extension Api { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.ChatReactions.chatReactionsAll(flags: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChatReactions.chatReactionsAll(flags: _1!) } public static func parse_chatReactionsNone(_ reader: BufferReader) -> ChatReactions? { return Api.ChatReactions.chatReactionsNone @@ -382,12 +361,8 @@ public extension Api { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Reaction.self) } let _c1 = _1 != nil - if _c1 { - return Api.ChatReactions.chatReactionsSome(reactions: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChatReactions.chatReactionsSome(reactions: _1!) } } @@ -432,12 +407,8 @@ public extension Api { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.ChatTheme.chatTheme(emoticon: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ChatTheme.chatTheme(emoticon: _1!) } public static func parse_chatThemeUniqueGift(_ reader: BufferReader) -> ChatTheme? { var _1: Api.StarGift? @@ -450,12 +421,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ChatTheme.chatThemeUniqueGift(gift: _1!, themeSettings: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ChatTheme.chatThemeUniqueGift(gift: _1!, themeSettings: _2!) } } @@ -506,12 +474,11 @@ public extension Api { let _c2 = (Int(_1!) & Int(1 << 6) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 8) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 8) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.CodeSettings.codeSettings(flags: _1!, logoutTokens: _2, token: _3, appSandbox: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.CodeSettings.codeSettings(flags: _1!, logoutTokens: _2, token: _3, appSandbox: _4) } } @@ -720,12 +687,50 @@ public extension Api { let _c41 = (Int(_1!) & Int(1 << 2) == 0) || _41 != nil let _c42 = (Int(_1!) & Int(1 << 15) == 0) || _42 != nil let _c43 = (Int(_1!) & Int(1 << 16) == 0) || _43 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 && _c28 && _c29 && _c30 && _c31 && _c32 && _c33 && _c34 && _c35 && _c36 && _c37 && _c38 && _c39 && _c40 && _c41 && _c42 && _c43 { - return Api.Config.config(flags: _1!, date: _2!, expires: _3!, testMode: _4!, thisDc: _5!, dcOptions: _6!, dcTxtDomainName: _7!, chatSizeMax: _8!, megagroupSizeMax: _9!, forwardedCountMax: _10!, onlineUpdatePeriodMs: _11!, offlineBlurTimeoutMs: _12!, offlineIdleTimeoutMs: _13!, onlineCloudTimeoutMs: _14!, notifyCloudDelayMs: _15!, notifyDefaultDelayMs: _16!, pushChatPeriodMs: _17!, pushChatLimit: _18!, editTimeLimit: _19!, revokeTimeLimit: _20!, revokePmTimeLimit: _21!, ratingEDecay: _22!, stickersRecentLimit: _23!, channelsReadMediaPeriod: _24!, tmpSessions: _25, callReceiveTimeoutMs: _26!, callRingTimeoutMs: _27!, callConnectTimeoutMs: _28!, callPacketTimeoutMs: _29!, meUrlPrefix: _30!, autoupdateUrlPrefix: _31, gifSearchUsername: _32, venueSearchUsername: _33, imgSearchUsername: _34, staticMapsProvider: _35, captionLengthMax: _36!, messageLengthMax: _37!, webfileDcId: _38!, suggestedLangCode: _39, langPackVersion: _40, baseLangPackVersion: _41, reactionsDefault: _42, autologinToken: _43) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + if !_c13 { return nil } + if !_c14 { return nil } + if !_c15 { return nil } + if !_c16 { return nil } + if !_c17 { return nil } + if !_c18 { return nil } + if !_c19 { return nil } + if !_c20 { return nil } + if !_c21 { return nil } + if !_c22 { return nil } + if !_c23 { return nil } + if !_c24 { return nil } + if !_c25 { return nil } + if !_c26 { return nil } + if !_c27 { return nil } + if !_c28 { return nil } + if !_c29 { return nil } + if !_c30 { return nil } + if !_c31 { return nil } + if !_c32 { return nil } + if !_c33 { return nil } + if !_c34 { return nil } + if !_c35 { return nil } + if !_c36 { return nil } + if !_c37 { return nil } + if !_c38 { return nil } + if !_c39 { return nil } + if !_c40 { return nil } + if !_c41 { return nil } + if !_c42 { return nil } + if !_c43 { return nil } + return Api.Config.config(flags: _1!, date: _2!, expires: _3!, testMode: _4!, thisDc: _5!, dcOptions: _6!, dcTxtDomainName: _7!, chatSizeMax: _8!, megagroupSizeMax: _9!, forwardedCountMax: _10!, onlineUpdatePeriodMs: _11!, offlineBlurTimeoutMs: _12!, offlineIdleTimeoutMs: _13!, onlineCloudTimeoutMs: _14!, notifyCloudDelayMs: _15!, notifyDefaultDelayMs: _16!, pushChatPeriodMs: _17!, pushChatLimit: _18!, editTimeLimit: _19!, revokeTimeLimit: _20!, revokePmTimeLimit: _21!, ratingEDecay: _22!, stickersRecentLimit: _23!, channelsReadMediaPeriod: _24!, tmpSessions: _25, callReceiveTimeoutMs: _26!, callRingTimeoutMs: _27!, callConnectTimeoutMs: _28!, callPacketTimeoutMs: _29!, meUrlPrefix: _30!, autoupdateUrlPrefix: _31, gifSearchUsername: _32, venueSearchUsername: _33, imgSearchUsername: _34, staticMapsProvider: _35, captionLengthMax: _36!, messageLengthMax: _37!, webfileDcId: _38!, suggestedLangCode: _39, langPackVersion: _40, baseLangPackVersion: _41, reactionsDefault: _42, autologinToken: _43) } } @@ -772,12 +777,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.ConnectedBot.connectedBot(flags: _1!, botId: _2!, recipients: _3!, rights: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.ConnectedBot.connectedBot(flags: _1!, botId: _2!, recipients: _3!, rights: _4!) } } @@ -836,12 +840,15 @@ public extension Api { let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil let _c7 = _7 != nil let _c8 = _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.ConnectedBotStarRef.connectedBotStarRef(flags: _1!, url: _2!, date: _3!, botId: _4!, commissionPermille: _5!, durationMonths: _6, participants: _7!, revenue: _8!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.ConnectedBotStarRef.connectedBotStarRef(flags: _1!, url: _2!, date: _3!, botId: _4!, commissionPermille: _5!, durationMonths: _6, participants: _7!, revenue: _8!) } } @@ -878,12 +885,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.Contact.contact(userId: _1!, mutual: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.Contact.contact(userId: _1!, mutual: _2!) } } @@ -920,12 +924,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ContactBirthday.contactBirthday(contactId: _1!, birthday: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ContactBirthday.contactBirthday(contactId: _1!, birthday: _2!) } } @@ -962,12 +963,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ContactStatus.contactStatus(userId: _1!, status: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ContactStatus.contactStatus(userId: _1!, status: _2!) } } @@ -998,12 +996,8 @@ public extension Api { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.DataJSON.dataJSON(data: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.DataJSON.dataJSON(data: _1!) } } @@ -1050,12 +1044,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = (Int(_1!) & Int(1 << 10) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.DcOption.dcOption(flags: _1!, id: _2!, ipAddress: _3!, port: _4!, secret: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.DcOption.dcOption(flags: _1!, id: _2!, ipAddress: _3!, port: _4!, secret: _5) } } @@ -1086,12 +1080,8 @@ public extension Api { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.DefaultHistoryTTL.defaultHistoryTTL(period: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.DefaultHistoryTTL.defaultHistoryTTL(period: _1!) } } @@ -1192,12 +1182,20 @@ public extension Api { let _c11 = (Int(_1!) & Int(1 << 1) == 0) || _11 != nil let _c12 = (Int(_1!) & Int(1 << 4) == 0) || _12 != nil let _c13 = (Int(_1!) & Int(1 << 5) == 0) || _13 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 { - return Api.Dialog.dialog(flags: _1!, peer: _2!, topMessage: _3!, readInboxMaxId: _4!, readOutboxMaxId: _5!, unreadCount: _6!, unreadMentionsCount: _7!, unreadReactionsCount: _8!, notifySettings: _9!, pts: _10, draft: _11, folderId: _12, ttlPeriod: _13) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + if !_c13 { return nil } + return Api.Dialog.dialog(flags: _1!, peer: _2!, topMessage: _3!, readInboxMaxId: _4!, readOutboxMaxId: _5!, unreadCount: _6!, unreadMentionsCount: _7!, unreadReactionsCount: _8!, notifySettings: _9!, pts: _10, draft: _11, folderId: _12, ttlPeriod: _13) } public static func parse_dialogFolder(_ reader: BufferReader) -> Dialog? { var _1: Int32? @@ -1228,12 +1226,15 @@ public extension Api { let _c6 = _6 != nil let _c7 = _7 != nil let _c8 = _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.Dialog.dialogFolder(flags: _1!, folder: _2!, peer: _3!, topMessage: _4!, unreadMutedPeersCount: _5!, unreadUnmutedPeersCount: _6!, unreadMutedMessagesCount: _7!, unreadUnmutedMessagesCount: _8!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.Dialog.dialogFolder(flags: _1!, folder: _2!, peer: _3!, topMessage: _4!, unreadMutedPeersCount: _5!, unreadUnmutedPeersCount: _6!, unreadMutedMessagesCount: _7!, unreadUnmutedMessagesCount: _8!) } } @@ -1344,12 +1345,15 @@ public extension Api { let _c6 = _6 != nil let _c7 = _7 != nil let _c8 = _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.DialogFilter.dialogFilter(flags: _1!, id: _2!, title: _3!, emoticon: _4, color: _5, pinnedPeers: _6!, includePeers: _7!, excludePeers: _8!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.DialogFilter.dialogFilter(flags: _1!, id: _2!, title: _3!, emoticon: _4, color: _5, pinnedPeers: _6!, includePeers: _7!, excludePeers: _8!) } public static func parse_dialogFilterChatlist(_ reader: BufferReader) -> DialogFilter? { var _1: Int32? @@ -1379,12 +1383,14 @@ public extension Api { let _c5 = (Int(_1!) & Int(1 << 27) == 0) || _5 != nil let _c6 = _6 != nil let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.DialogFilter.dialogFilterChatlist(flags: _1!, id: _2!, title: _3!, emoticon: _4, color: _5, pinnedPeers: _6!, includePeers: _7!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.DialogFilter.dialogFilterChatlist(flags: _1!, id: _2!, title: _3!, emoticon: _4, color: _5, pinnedPeers: _6!, includePeers: _7!) } public static func parse_dialogFilterDefault(_ reader: BufferReader) -> DialogFilter? { return Api.DialogFilter.dialogFilterDefault @@ -1424,12 +1430,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.DialogFilterSuggested.dialogFilterSuggested(filter: _1!, description: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.DialogFilterSuggested.dialogFilterSuggested(filter: _1!, description: _2!) } } @@ -1471,23 +1474,15 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.Peer } let _c1 = _1 != nil - if _c1 { - return Api.DialogPeer.dialogPeer(peer: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.DialogPeer.dialogPeer(peer: _1!) } public static func parse_dialogPeerFolder(_ reader: BufferReader) -> DialogPeer? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.DialogPeer.dialogPeerFolder(folderId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.DialogPeer.dialogPeerFolder(folderId: _1!) } } diff --git a/submodules/TelegramApi/Sources/Api6.swift b/submodules/TelegramApi/Sources/Api6.swift index ca7545a0..5cb12927 100644 --- a/submodules/TelegramApi/Sources/Api6.swift +++ b/submodules/TelegramApi/Sources/Api6.swift @@ -24,12 +24,8 @@ public extension Api { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.DisallowedGiftsSettings.disallowedGiftsSettings(flags: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.DisallowedGiftsSettings.disallowedGiftsSettings(flags: _1!) } } @@ -127,23 +123,25 @@ public extension Api { let _c9 = (Int(_1!) & Int(1 << 1) == 0) || _9 != nil let _c10 = _10 != nil let _c11 = _11 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 { - return Api.Document.document(flags: _1!, id: _2!, accessHash: _3!, fileReference: _4!, date: _5!, mimeType: _6!, size: _7!, thumbs: _8, videoThumbs: _9, dcId: _10!, attributes: _11!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + return Api.Document.document(flags: _1!, id: _2!, accessHash: _3!, fileReference: _4!, date: _5!, mimeType: _6!, size: _7!, thumbs: _8, videoThumbs: _9, dcId: _10!, attributes: _11!) } public static func parse_documentEmpty(_ reader: BufferReader) -> Document? { var _1: Int64? _1 = reader.readInt64() let _c1 = _1 != nil - if _c1 { - return Api.Document.documentEmpty(id: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.Document.documentEmpty(id: _1!) } } @@ -268,12 +266,12 @@ public extension Api { let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.DocumentAttribute.documentAttributeAudio(flags: _1!, duration: _2!, title: _3, performer: _4, waveform: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.DocumentAttribute.documentAttributeAudio(flags: _1!, duration: _2!, title: _3, performer: _4, waveform: _5) } public static func parse_documentAttributeCustomEmoji(_ reader: BufferReader) -> DocumentAttribute? { var _1: Int32? @@ -287,23 +285,17 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.DocumentAttribute.documentAttributeCustomEmoji(flags: _1!, alt: _2!, stickerset: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.DocumentAttribute.documentAttributeCustomEmoji(flags: _1!, alt: _2!, stickerset: _3!) } public static func parse_documentAttributeFilename(_ reader: BufferReader) -> DocumentAttribute? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.DocumentAttribute.documentAttributeFilename(fileName: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.DocumentAttribute.documentAttributeFilename(fileName: _1!) } public static func parse_documentAttributeHasStickers(_ reader: BufferReader) -> DocumentAttribute? { return Api.DocumentAttribute.documentAttributeHasStickers @@ -315,12 +307,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.DocumentAttribute.documentAttributeImageSize(w: _1!, h: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.DocumentAttribute.documentAttributeImageSize(w: _1!, h: _2!) } public static func parse_documentAttributeSticker(_ reader: BufferReader) -> DocumentAttribute? { var _1: Int32? @@ -339,12 +328,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.DocumentAttribute.documentAttributeSticker(flags: _1!, alt: _2!, stickerset: _3!, maskCoords: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.DocumentAttribute.documentAttributeSticker(flags: _1!, alt: _2!, stickerset: _3!, maskCoords: _4) } public static func parse_documentAttributeVideo(_ reader: BufferReader) -> DocumentAttribute? { var _1: Int32? @@ -368,12 +356,14 @@ public extension Api { let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 4) == 0) || _6 != nil let _c7 = (Int(_1!) & Int(1 << 5) == 0) || _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.DocumentAttribute.documentAttributeVideo(flags: _1!, duration: _2!, w: _3!, h: _4!, preloadPrefixSize: _5, videoStartTs: _6, videoCodec: _7) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.DocumentAttribute.documentAttributeVideo(flags: _1!, duration: _2!, w: _3!, h: _4!, preloadPrefixSize: _5, videoStartTs: _6, videoCodec: _7) } } @@ -454,12 +444,15 @@ public extension Api { let _c6 = _6 != nil let _c7 = (Int(_1!) & Int(1 << 7) == 0) || _7 != nil let _c8 = (Int(_1!) & Int(1 << 8) == 0) || _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.DraftMessage.draftMessage(flags: _1!, replyTo: _2, message: _3!, entities: _4, media: _5, date: _6!, effect: _7, suggestedPost: _8) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.DraftMessage.draftMessage(flags: _1!, replyTo: _2, message: _3!, entities: _4, media: _5, date: _6!, effect: _7, suggestedPost: _8) } public static func parse_draftMessageEmpty(_ reader: BufferReader) -> DraftMessage? { var _1: Int32? @@ -468,12 +461,9 @@ public extension Api { if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil - if _c1 && _c2 { - return Api.DraftMessage.draftMessageEmpty(flags: _1!, date: _2) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.DraftMessage.draftMessageEmpty(flags: _1!, date: _2) } } @@ -522,34 +512,22 @@ public extension Api { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.EmailVerification.emailVerificationApple(token: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.EmailVerification.emailVerificationApple(token: _1!) } public static func parse_emailVerificationCode(_ reader: BufferReader) -> EmailVerification? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.EmailVerification.emailVerificationCode(code: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.EmailVerification.emailVerificationCode(code: _1!) } public static func parse_emailVerificationGoogle(_ reader: BufferReader) -> EmailVerification? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.EmailVerification.emailVerificationGoogle(token: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.EmailVerification.emailVerificationGoogle(token: _1!) } } @@ -605,12 +583,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.EmailVerifyPurpose.emailVerifyPurposeLoginSetup(phoneNumber: _1!, phoneCodeHash: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.EmailVerifyPurpose.emailVerifyPurposeLoginSetup(phoneNumber: _1!, phoneCodeHash: _2!) } public static func parse_emailVerifyPurposePassport(_ reader: BufferReader) -> EmailVerifyPurpose? { return Api.EmailVerifyPurpose.emailVerifyPurposePassport @@ -683,12 +658,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.EmojiGroup.emojiGroup(title: _1!, iconEmojiId: _2!, emoticons: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.EmojiGroup.emojiGroup(title: _1!, iconEmojiId: _2!, emoticons: _3!) } public static func parse_emojiGroupGreeting(_ reader: BufferReader) -> EmojiGroup? { var _1: String? @@ -702,12 +675,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.EmojiGroup.emojiGroupGreeting(title: _1!, iconEmojiId: _2!, emoticons: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.EmojiGroup.emojiGroupGreeting(title: _1!, iconEmojiId: _2!, emoticons: _3!) } public static func parse_emojiGroupPremium(_ reader: BufferReader) -> EmojiGroup? { var _1: String? @@ -716,12 +687,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.EmojiGroup.emojiGroupPremium(title: _1!, iconEmojiId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.EmojiGroup.emojiGroupPremium(title: _1!, iconEmojiId: _2!) } } @@ -776,12 +744,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.EmojiKeyword.emojiKeyword(keyword: _1!, emoticons: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.EmojiKeyword.emojiKeyword(keyword: _1!, emoticons: _2!) } public static func parse_emojiKeywordDeleted(_ reader: BufferReader) -> EmojiKeyword? { var _1: String? @@ -792,12 +757,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.EmojiKeyword.emojiKeywordDeleted(keyword: _1!, emoticons: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.EmojiKeyword.emojiKeywordDeleted(keyword: _1!, emoticons: _2!) } } @@ -846,12 +808,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.EmojiKeywordsDifference.emojiKeywordsDifference(langCode: _1!, fromVersion: _2!, version: _3!, keywords: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.EmojiKeywordsDifference.emojiKeywordsDifference(langCode: _1!, fromVersion: _2!, version: _3!, keywords: _4!) } } @@ -882,12 +843,8 @@ public extension Api { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.EmojiLanguage.emojiLanguage(langCode: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.EmojiLanguage.emojiLanguage(langCode: _1!) } } @@ -937,12 +894,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.EmojiList.emojiList(hash: _1!, documentId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.EmojiList.emojiList(hash: _1!, documentId: _2!) } public static func parse_emojiListNotModified(_ reader: BufferReader) -> EmojiList? { return Api.EmojiList.emojiListNotModified @@ -1023,12 +977,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.EmojiStatus.emojiStatus(flags: _1!, documentId: _2!, until: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.EmojiStatus.emojiStatus(flags: _1!, documentId: _2!, until: _3) } public static func parse_emojiStatusCollectible(_ reader: BufferReader) -> EmojiStatus? { var _1: Int32? @@ -1064,12 +1016,18 @@ public extension Api { let _c9 = _9 != nil let _c10 = _10 != nil let _c11 = (Int(_1!) & Int(1 << 0) == 0) || _11 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 { - return Api.EmojiStatus.emojiStatusCollectible(flags: _1!, collectibleId: _2!, documentId: _3!, title: _4!, slug: _5!, patternDocumentId: _6!, centerColor: _7!, edgeColor: _8!, patternColor: _9!, textColor: _10!, until: _11) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + return Api.EmojiStatus.emojiStatusCollectible(flags: _1!, collectibleId: _2!, documentId: _3!, title: _4!, slug: _5!, patternDocumentId: _6!, centerColor: _7!, edgeColor: _8!, patternColor: _9!, textColor: _10!, until: _11) } public static func parse_emojiStatusEmpty(_ reader: BufferReader) -> EmojiStatus? { return Api.EmojiStatus.emojiStatusEmpty @@ -1084,12 +1042,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.EmojiStatus.inputEmojiStatusCollectible(flags: _1!, collectibleId: _2!, until: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.EmojiStatus.inputEmojiStatusCollectible(flags: _1!, collectibleId: _2!, until: _3) } } diff --git a/submodules/TelegramApi/Sources/Api7.swift b/submodules/TelegramApi/Sources/Api7.swift index 1ad2e7de..2a85bd69 100644 --- a/submodules/TelegramApi/Sources/Api7.swift +++ b/submodules/TelegramApi/Sources/Api7.swift @@ -24,12 +24,8 @@ public extension Api { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.EmojiURL.emojiURL(url: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.EmojiURL.emojiURL(url: _1!) } } @@ -132,12 +128,14 @@ public extension Api { let _c5 = _5 != nil let _c6 = _6 != nil let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.EncryptedChat.encryptedChat(id: _1!, accessHash: _2!, date: _3!, adminId: _4!, participantId: _5!, gAOrB: _6!, keyFingerprint: _7!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.EncryptedChat.encryptedChat(id: _1!, accessHash: _2!, date: _3!, adminId: _4!, participantId: _5!, gAOrB: _6!, keyFingerprint: _7!) } public static func parse_encryptedChatDiscarded(_ reader: BufferReader) -> EncryptedChat? { var _1: Int32? @@ -146,23 +144,16 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.EncryptedChat.encryptedChatDiscarded(flags: _1!, id: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.EncryptedChat.encryptedChatDiscarded(flags: _1!, id: _2!) } public static func parse_encryptedChatEmpty(_ reader: BufferReader) -> EncryptedChat? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.EncryptedChat.encryptedChatEmpty(id: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.EncryptedChat.encryptedChatEmpty(id: _1!) } public static func parse_encryptedChatRequested(_ reader: BufferReader) -> EncryptedChat? { var _1: Int32? @@ -189,12 +180,15 @@ public extension Api { let _c6 = _6 != nil let _c7 = _7 != nil let _c8 = _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.EncryptedChat.encryptedChatRequested(flags: _1!, folderId: _2, id: _3!, accessHash: _4!, date: _5!, adminId: _6!, participantId: _7!, gA: _8!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.EncryptedChat.encryptedChatRequested(flags: _1!, folderId: _2, id: _3!, accessHash: _4!, date: _5!, adminId: _6!, participantId: _7!, gA: _8!) } public static func parse_encryptedChatWaiting(_ reader: BufferReader) -> EncryptedChat? { var _1: Int32? @@ -212,12 +206,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.EncryptedChat.encryptedChatWaiting(id: _1!, accessHash: _2!, date: _3!, adminId: _4!, participantId: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.EncryptedChat.encryptedChatWaiting(id: _1!, accessHash: _2!, date: _3!, adminId: _4!, participantId: _5!) } } @@ -273,12 +267,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.EncryptedFile.encryptedFile(id: _1!, accessHash: _2!, size: _3!, dcId: _4!, keyFingerprint: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.EncryptedFile.encryptedFile(id: _1!, accessHash: _2!, size: _3!, dcId: _4!, keyFingerprint: _5!) } public static func parse_encryptedFileEmpty(_ reader: BufferReader) -> EncryptedFile? { return Api.EncryptedFile.encryptedFileEmpty @@ -342,12 +336,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.EncryptedMessage.encryptedMessage(randomId: _1!, chatId: _2!, date: _3!, bytes: _4!, file: _5!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.EncryptedMessage.encryptedMessage(randomId: _1!, chatId: _2!, date: _3!, bytes: _4!, file: _5!) } public static func parse_encryptedMessageService(_ reader: BufferReader) -> EncryptedMessage? { var _1: Int64? @@ -362,12 +356,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.EncryptedMessage.encryptedMessageService(randomId: _1!, chatId: _2!, date: _3!, bytes: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.EncryptedMessage.encryptedMessageService(randomId: _1!, chatId: _2!, date: _3!, bytes: _4!) } } @@ -453,12 +446,19 @@ public extension Api { let _c10 = (Int(_1!) & Int(1 << 10) == 0) || _10 != nil let _c11 = (Int(_1!) & Int(1 << 8) == 0) || _11 != nil let _c12 = (Int(_1!) & Int(1 << 9) == 0) || _12 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 { - return Api.ExportedChatInvite.chatInviteExported(flags: _1!, link: _2!, adminId: _3!, date: _4!, startDate: _5, expireDate: _6, usageLimit: _7, usage: _8, requested: _9, subscriptionExpired: _10, title: _11, subscriptionPricing: _12) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + return Api.ExportedChatInvite.chatInviteExported(flags: _1!, link: _2!, adminId: _3!, date: _4!, startDate: _5, expireDate: _6, usageLimit: _7, usage: _8, requested: _9, subscriptionExpired: _10, title: _11, subscriptionPricing: _12) } public static func parse_chatInvitePublicJoinRequests(_ reader: BufferReader) -> ExportedChatInvite? { return Api.ExportedChatInvite.chatInvitePublicJoinRequests @@ -510,12 +510,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.ExportedChatlistInvite.exportedChatlistInvite(flags: _1!, title: _2!, url: _3!, peers: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.ExportedChatlistInvite.exportedChatlistInvite(flags: _1!, title: _2!, url: _3!, peers: _4!) } } @@ -550,12 +549,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ExportedContactToken.exportedContactToken(url: _1!, expires: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ExportedContactToken.exportedContactToken(url: _1!, expires: _2!) } } @@ -590,12 +586,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ExportedMessageLink.exportedMessageLink(link: _1!, html: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ExportedMessageLink.exportedMessageLink(link: _1!, html: _2!) } } @@ -626,12 +619,8 @@ public extension Api { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.ExportedStoryLink.exportedStoryLink(link: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ExportedStoryLink.exportedStoryLink(link: _1!) } } @@ -676,12 +665,11 @@ public extension Api { let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.FactCheck.factCheck(flags: _1!, country: _2, text: _3, hash: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.FactCheck.factCheck(flags: _1!, country: _2, text: _3, hash: _4!) } } @@ -720,12 +708,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.FileHash.fileHash(offset: _1!, limit: _2!, hash: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.FileHash.fileHash(offset: _1!, limit: _2!, hash: _3!) } } @@ -770,12 +756,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 3) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.Folder.folder(flags: _1!, id: _2!, title: _3!, photo: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.Folder.folder(flags: _1!, id: _2!, title: _3!, photo: _4) } } @@ -812,12 +797,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.FolderPeer.folderPeer(peer: _1!, folderId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.FolderPeer.folderPeer(peer: _1!, folderId: _2!) } } @@ -925,23 +907,30 @@ public extension Api { let _c14 = _14 != nil let _c15 = _15 != nil let _c16 = (Int(_1!) & Int(1 << 4) == 0) || _16 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 { - return Api.ForumTopic.forumTopic(flags: _1!, id: _2!, date: _3!, peer: _4!, title: _5!, iconColor: _6!, iconEmojiId: _7, topMessage: _8!, readInboxMaxId: _9!, readOutboxMaxId: _10!, unreadCount: _11!, unreadMentionsCount: _12!, unreadReactionsCount: _13!, fromId: _14!, notifySettings: _15!, draft: _16) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + if !_c13 { return nil } + if !_c14 { return nil } + if !_c15 { return nil } + if !_c16 { return nil } + return Api.ForumTopic.forumTopic(flags: _1!, id: _2!, date: _3!, peer: _4!, title: _5!, iconColor: _6!, iconEmojiId: _7, topMessage: _8!, readInboxMaxId: _9!, readOutboxMaxId: _10!, unreadCount: _11!, unreadMentionsCount: _12!, unreadReactionsCount: _13!, fromId: _14!, notifySettings: _15!, draft: _16) } public static func parse_forumTopicDeleted(_ reader: BufferReader) -> ForumTopic? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.ForumTopic.forumTopicDeleted(id: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.ForumTopic.forumTopicDeleted(id: _1!) } } @@ -980,12 +969,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.FoundStory.foundStory(peer: _1!, story: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.FoundStory.foundStory(peer: _1!, story: _2!) } } @@ -1048,12 +1034,15 @@ public extension Api { let _c6 = _6 != nil let _c7 = _7 != nil let _c8 = (Int(_1!) & Int(1 << 0) == 0) || _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.Game.game(flags: _1!, id: _2!, accessHash: _3!, shortName: _4!, title: _5!, description: _6!, photo: _7!, document: _8) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.Game.game(flags: _1!, id: _2!, accessHash: _3!, shortName: _4!, title: _5!, description: _6!, photo: _7!, document: _8) } } @@ -1109,12 +1098,12 @@ public extension Api { let _c3 = _3 != nil let _c4 = _4 != nil let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.GeoPoint.geoPoint(flags: _1!, long: _2!, lat: _3!, accessHash: _4!, accuracyRadius: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.GeoPoint.geoPoint(flags: _1!, long: _2!, lat: _3!, accessHash: _4!, accuracyRadius: _5) } public static func parse_geoPointEmpty(_ reader: BufferReader) -> GeoPoint? { return Api.GeoPoint.geoPointEmpty @@ -1164,12 +1153,12 @@ public extension Api { let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.GeoPointAddress.geoPointAddress(flags: _1!, countryIso2: _2!, state: _3, city: _4, street: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.GeoPointAddress.geoPointAddress(flags: _1!, countryIso2: _2!, state: _3, city: _4, street: _5) } } @@ -1210,12 +1199,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 5) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 6) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.GlobalPrivacySettings.globalPrivacySettings(flags: _1!, noncontactPeersPaidStars: _2, disallowedGifts: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.GlobalPrivacySettings.globalPrivacySettings(flags: _1!, noncontactPeersPaidStars: _2, disallowedGifts: _3) } } @@ -1311,12 +1298,21 @@ public extension Api { let _c12 = (Int(_1!) & Int(1 << 16) == 0) || _12 != nil let _c13 = (Int(_1!) & Int(1 << 20) == 0) || _13 != nil let _c14 = (Int(_1!) & Int(1 << 21) == 0) || _14 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 { - return Api.GroupCall.groupCall(flags: _1!, id: _2!, accessHash: _3!, participantsCount: _4!, title: _5, streamDcId: _6, recordStartDate: _7, scheduleDate: _8, unmutedVideoCount: _9, unmutedVideoLimit: _10!, version: _11!, inviteLink: _12, sendPaidMessagesStars: _13, defaultSendAs: _14) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + if !_c12 { return nil } + if !_c13 { return nil } + if !_c14 { return nil } + return Api.GroupCall.groupCall(flags: _1!, id: _2!, accessHash: _3!, participantsCount: _4!, title: _5, streamDcId: _6, recordStartDate: _7, scheduleDate: _8, unmutedVideoCount: _9, unmutedVideoLimit: _10!, version: _11!, inviteLink: _12, sendPaidMessagesStars: _13, defaultSendAs: _14) } public static func parse_groupCallDiscarded(_ reader: BufferReader) -> GroupCall? { var _1: Int64? @@ -1328,12 +1324,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.GroupCall.groupCallDiscarded(id: _1!, accessHash: _2!, duration: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.GroupCall.groupCallDiscarded(id: _1!, accessHash: _2!, duration: _3!) } } @@ -1374,12 +1368,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 3) == 0) || _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.GroupCallDonor.groupCallDonor(flags: _1!, peerId: _2, stars: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.GroupCallDonor.groupCallDonor(flags: _1!, peerId: _2, stars: _3!) } } @@ -1434,12 +1426,13 @@ public extension Api { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.GroupCallMessage.groupCallMessage(flags: _1!, id: _2!, fromId: _3!, date: _4!, message: _5!, paidMessageStars: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.GroupCallMessage.groupCallMessage(flags: _1!, id: _2!, fromId: _3!, date: _4!, message: _5!, paidMessageStars: _6) } } diff --git a/submodules/TelegramApi/Sources/Api8.swift b/submodules/TelegramApi/Sources/Api8.swift index 0b617cce..78862824 100644 --- a/submodules/TelegramApi/Sources/Api8.swift +++ b/submodules/TelegramApi/Sources/Api8.swift @@ -70,12 +70,18 @@ public extension Api { let _c9 = (Int(_1!) & Int(1 << 6) == 0) || _9 != nil let _c10 = (Int(_1!) & Int(1 << 14) == 0) || _10 != nil let _c11 = (Int(_1!) & Int(1 << 16) == 0) || _11 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 { - return Api.GroupCallParticipant.groupCallParticipant(flags: _1!, peer: _2!, date: _3!, activeDate: _4, source: _5!, volume: _6, about: _7, raiseHandRating: _8, video: _9, presentation: _10, paidStarsTotal: _11) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + if !_c10 { return nil } + if !_c11 { return nil } + return Api.GroupCallParticipant.groupCallParticipant(flags: _1!, peer: _2!, date: _3!, activeDate: _4, source: _5!, volume: _6, about: _7, raiseHandRating: _8, video: _9, presentation: _10, paidStarsTotal: _11) } } @@ -124,12 +130,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.GroupCallParticipantVideo.groupCallParticipantVideo(flags: _1!, endpoint: _2!, sourceGroups: _3!, audioSource: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.GroupCallParticipantVideo.groupCallParticipantVideo(flags: _1!, endpoint: _2!, sourceGroups: _3!, audioSource: _4) } } @@ -170,12 +175,9 @@ public extension Api { } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.GroupCallParticipantVideoSourceGroup.groupCallParticipantVideoSourceGroup(semantics: _1!, sources: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.GroupCallParticipantVideoSourceGroup.groupCallParticipantVideoSourceGroup(semantics: _1!, sources: _2!) } } @@ -214,12 +216,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.GroupCallStreamChannel.groupCallStreamChannel(channel: _1!, scale: _2!, lastTimestampMs: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.GroupCallStreamChannel.groupCallStreamChannel(channel: _1!, scale: _2!, lastTimestampMs: _3!) } } @@ -258,12 +258,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.HighScore.highScore(pos: _1!, userId: _2!, score: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.HighScore.highScore(pos: _1!, userId: _2!, score: _3!) } } @@ -298,12 +296,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.ImportedContact.importedContact(userId: _1!, clientId: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.ImportedContact.importedContact(userId: _1!, clientId: _2!) } } @@ -338,12 +333,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InlineBotSwitchPM.inlineBotSwitchPM(text: _1!, startParam: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InlineBotSwitchPM.inlineBotSwitchPM(text: _1!, startParam: _2!) } } @@ -378,12 +370,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InlineBotWebView.inlineBotWebView(text: _1!, url: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InlineBotWebView.inlineBotWebView(text: _1!, url: _2!) } } @@ -516,12 +505,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputAppEvent.inputAppEvent(time: _1!, type: _2!, peer: _3!, data: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.InputAppEvent.inputAppEvent(time: _1!, type: _2!, peer: _3!, data: _4!) } } @@ -566,12 +554,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputBotApp.inputBotAppID(id: _1!, accessHash: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputBotApp.inputBotAppID(id: _1!, accessHash: _2!) } public static func parse_inputBotAppShortName(_ reader: BufferReader) -> InputBotApp? { var _1: Api.InputUser? @@ -582,12 +567,9 @@ public extension Api { _2 = parseString(reader) let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputBotApp.inputBotAppShortName(botId: _1!, shortName: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputBotApp.inputBotAppShortName(botId: _1!, shortName: _2!) } } @@ -734,12 +716,9 @@ public extension Api { } } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 2) == 0) || _2 != nil - if _c1 && _c2 { - return Api.InputBotInlineMessage.inputBotInlineMessageGame(flags: _1!, replyMarkup: _2) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputBotInlineMessage.inputBotInlineMessageGame(flags: _1!, replyMarkup: _2) } public static func parse_inputBotInlineMessageMediaAuto(_ reader: BufferReader) -> InputBotInlineMessage? { var _1: Int32? @@ -758,12 +737,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputBotInlineMessage.inputBotInlineMessageMediaAuto(flags: _1!, message: _2!, entities: _3, replyMarkup: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.InputBotInlineMessage.inputBotInlineMessageMediaAuto(flags: _1!, message: _2!, entities: _3, replyMarkup: _4) } public static func parse_inputBotInlineMessageMediaContact(_ reader: BufferReader) -> InputBotInlineMessage? { var _1: Int32? @@ -786,12 +764,13 @@ public extension Api { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.InputBotInlineMessage.inputBotInlineMessageMediaContact(flags: _1!, phoneNumber: _2!, firstName: _3!, lastName: _4!, vcard: _5!, replyMarkup: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.InputBotInlineMessage.inputBotInlineMessageMediaContact(flags: _1!, phoneNumber: _2!, firstName: _3!, lastName: _4!, vcard: _5!, replyMarkup: _6) } public static func parse_inputBotInlineMessageMediaGeo(_ reader: BufferReader) -> InputBotInlineMessage? { var _1: Int32? @@ -816,12 +795,13 @@ public extension Api { let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.InputBotInlineMessage.inputBotInlineMessageMediaGeo(flags: _1!, geoPoint: _2!, heading: _3, period: _4, proximityNotificationRadius: _5, replyMarkup: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.InputBotInlineMessage.inputBotInlineMessageMediaGeo(flags: _1!, geoPoint: _2!, heading: _3, period: _4, proximityNotificationRadius: _5, replyMarkup: _6) } public static func parse_inputBotInlineMessageMediaInvoice(_ reader: BufferReader) -> InputBotInlineMessage? { var _1: Int32? @@ -859,12 +839,16 @@ public extension Api { let _c7 = _7 != nil let _c8 = _8 != nil let _c9 = (Int(_1!) & Int(1 << 2) == 0) || _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.InputBotInlineMessage.inputBotInlineMessageMediaInvoice(flags: _1!, title: _2!, description: _3!, photo: _4, invoice: _5!, payload: _6!, provider: _7!, providerData: _8!, replyMarkup: _9) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + return Api.InputBotInlineMessage.inputBotInlineMessageMediaInvoice(flags: _1!, title: _2!, description: _3!, photo: _4, invoice: _5!, payload: _6!, provider: _7!, providerData: _8!, replyMarkup: _9) } public static func parse_inputBotInlineMessageMediaVenue(_ reader: BufferReader) -> InputBotInlineMessage? { var _1: Int32? @@ -895,12 +879,15 @@ public extension Api { let _c6 = _6 != nil let _c7 = _7 != nil let _c8 = (Int(_1!) & Int(1 << 2) == 0) || _8 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { - return Api.InputBotInlineMessage.inputBotInlineMessageMediaVenue(flags: _1!, geoPoint: _2!, title: _3!, address: _4!, provider: _5!, venueId: _6!, venueType: _7!, replyMarkup: _8) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + return Api.InputBotInlineMessage.inputBotInlineMessageMediaVenue(flags: _1!, geoPoint: _2!, title: _3!, address: _4!, provider: _5!, venueId: _6!, venueType: _7!, replyMarkup: _8) } public static func parse_inputBotInlineMessageMediaWebPage(_ reader: BufferReader) -> InputBotInlineMessage? { var _1: Int32? @@ -922,12 +909,12 @@ public extension Api { let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil let _c4 = _4 != nil let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.InputBotInlineMessage.inputBotInlineMessageMediaWebPage(flags: _1!, message: _2!, entities: _3, url: _4!, replyMarkup: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.InputBotInlineMessage.inputBotInlineMessageMediaWebPage(flags: _1!, message: _2!, entities: _3, url: _4!, replyMarkup: _5) } public static func parse_inputBotInlineMessageText(_ reader: BufferReader) -> InputBotInlineMessage? { var _1: Int32? @@ -946,12 +933,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputBotInlineMessage.inputBotInlineMessageText(flags: _1!, message: _2!, entities: _3, replyMarkup: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.InputBotInlineMessage.inputBotInlineMessageText(flags: _1!, message: _2!, entities: _3, replyMarkup: _4) } } @@ -1002,12 +988,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.InputBotInlineMessageID.inputBotInlineMessageID(dcId: _1!, id: _2!, accessHash: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.InputBotInlineMessageID.inputBotInlineMessageID(dcId: _1!, id: _2!, accessHash: _3!) } public static func parse_inputBotInlineMessageID64(_ reader: BufferReader) -> InputBotInlineMessageID? { var _1: Int32? @@ -1022,12 +1006,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputBotInlineMessageID.inputBotInlineMessageID64(dcId: _1!, ownerId: _2!, id: _3!, accessHash: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.InputBotInlineMessageID.inputBotInlineMessageID64(dcId: _1!, ownerId: _2!, id: _3!, accessHash: _4!) } } @@ -1134,12 +1117,16 @@ public extension Api { let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil let _c8 = (Int(_1!) & Int(1 << 5) == 0) || _8 != nil let _c9 = _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.InputBotInlineResult.inputBotInlineResult(flags: _1!, id: _2!, type: _3!, title: _4, description: _5, url: _6, thumb: _7, content: _8, sendMessage: _9!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + if !_c8 { return nil } + if !_c9 { return nil } + return Api.InputBotInlineResult.inputBotInlineResult(flags: _1!, id: _2!, type: _3!, title: _4, description: _5, url: _6, thumb: _7, content: _8, sendMessage: _9!) } public static func parse_inputBotInlineResultDocument(_ reader: BufferReader) -> InputBotInlineResult? { var _1: Int32? @@ -1167,12 +1154,14 @@ public extension Api { let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil let _c6 = _6 != nil let _c7 = _7 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.InputBotInlineResult.inputBotInlineResultDocument(flags: _1!, id: _2!, type: _3!, title: _4, description: _5, document: _6!, sendMessage: _7!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + if !_c7 { return nil } + return Api.InputBotInlineResult.inputBotInlineResultDocument(flags: _1!, id: _2!, type: _3!, title: _4, description: _5, document: _6!, sendMessage: _7!) } public static func parse_inputBotInlineResultGame(_ reader: BufferReader) -> InputBotInlineResult? { var _1: String? @@ -1186,12 +1175,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.InputBotInlineResult.inputBotInlineResultGame(id: _1!, shortName: _2!, sendMessage: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.InputBotInlineResult.inputBotInlineResultGame(id: _1!, shortName: _2!, sendMessage: _3!) } public static func parse_inputBotInlineResultPhoto(_ reader: BufferReader) -> InputBotInlineResult? { var _1: String? @@ -1210,12 +1197,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputBotInlineResult.inputBotInlineResultPhoto(id: _1!, type: _2!, photo: _3!, sendMessage: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.InputBotInlineResult.inputBotInlineResultPhoto(id: _1!, type: _2!, photo: _3!, sendMessage: _4!) } } @@ -1262,12 +1248,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputBusinessAwayMessage.inputBusinessAwayMessage(flags: _1!, shortcutId: _2!, schedule: _3!, recipients: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.InputBusinessAwayMessage.inputBusinessAwayMessage(flags: _1!, shortcutId: _2!, schedule: _3!, recipients: _4!) } } diff --git a/submodules/TelegramApi/Sources/Api9.swift b/submodules/TelegramApi/Sources/Api9.swift index a5bae125..2bdcfb3e 100644 --- a/submodules/TelegramApi/Sources/Api9.swift +++ b/submodules/TelegramApi/Sources/Api9.swift @@ -44,12 +44,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 4) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 6) == 0) || _3 != nil - if _c1 && _c2 && _c3 { - return Api.InputBusinessBotRecipients.inputBusinessBotRecipients(flags: _1!, users: _2, excludeUsers: _3) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.InputBusinessBotRecipients.inputBusinessBotRecipients(flags: _1!, users: _2, excludeUsers: _3) } } @@ -98,12 +96,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputBusinessChatLink.inputBusinessChatLink(flags: _1!, message: _2!, entities: _3, title: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.InputBusinessChatLink.inputBusinessChatLink(flags: _1!, message: _2!, entities: _3, title: _4) } } @@ -144,12 +141,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.InputBusinessGreetingMessage.inputBusinessGreetingMessage(shortcutId: _1!, recipients: _2!, noActivityDays: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.InputBusinessGreetingMessage.inputBusinessGreetingMessage(shortcutId: _1!, recipients: _2!, noActivityDays: _3!) } } @@ -194,12 +189,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputBusinessIntro.inputBusinessIntro(flags: _1!, title: _2!, description: _3!, sticker: _4) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.InputBusinessIntro.inputBusinessIntro(flags: _1!, title: _2!, description: _3!, sticker: _4) } } @@ -240,12 +234,9 @@ public extension Api { } } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 4) == 0) || _2 != nil - if _c1 && _c2 { - return Api.InputBusinessRecipients.inputBusinessRecipients(flags: _1!, users: _2) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputBusinessRecipients.inputBusinessRecipients(flags: _1!, users: _2) } } @@ -300,12 +291,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputChannel.inputChannel(channelId: _1!, accessHash: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputChannel.inputChannel(channelId: _1!, accessHash: _2!) } public static func parse_inputChannelEmpty(_ reader: BufferReader) -> InputChannel? { return Api.InputChannel.inputChannelEmpty @@ -322,12 +310,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.InputChannel.inputChannelFromMessage(peer: _1!, msgId: _2!, channelId: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.InputChannel.inputChannelFromMessage(peer: _1!, msgId: _2!, channelId: _3!) } } @@ -382,12 +368,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.InputPhoto } let _c1 = _1 != nil - if _c1 { - return Api.InputChatPhoto.inputChatPhoto(id: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputChatPhoto.inputChatPhoto(id: _1!) } public static func parse_inputChatPhotoEmpty(_ reader: BufferReader) -> InputChatPhoto? { return Api.InputChatPhoto.inputChatPhotoEmpty @@ -414,12 +396,12 @@ public extension Api { let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.InputChatPhoto.inputChatUploadedPhoto(flags: _1!, file: _2, video: _3, videoStartTs: _4, videoEmojiMarkup: _5) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + return Api.InputChatPhoto.inputChatUploadedPhoto(flags: _1!, file: _2, video: _3, videoStartTs: _4, videoEmojiMarkup: _5) } } @@ -468,12 +450,8 @@ public extension Api { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.InputChatTheme.inputChatTheme(emoticon: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputChatTheme.inputChatTheme(emoticon: _1!) } public static func parse_inputChatThemeEmpty(_ reader: BufferReader) -> InputChatTheme? { return Api.InputChatTheme.inputChatThemeEmpty @@ -482,12 +460,8 @@ public extension Api { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.InputChatTheme.inputChatThemeUniqueGift(slug: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputChatTheme.inputChatThemeUniqueGift(slug: _1!) } } @@ -518,12 +492,8 @@ public extension Api { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.InputChatlist.inputChatlistDialogFilter(filterId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputChatlist.inputChatlistDialogFilter(filterId: _1!) } } @@ -574,12 +544,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.InputCheckPasswordSRP.inputCheckPasswordSRP(srpId: _1!, A: _2!, M1: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.InputCheckPasswordSRP.inputCheckPasswordSRP(srpId: _1!, A: _2!, M1: _3!) } } @@ -614,12 +582,9 @@ public extension Api { _2 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputClientProxy.inputClientProxy(address: _1!, port: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputClientProxy.inputClientProxy(address: _1!, port: _2!) } } @@ -659,23 +624,15 @@ public extension Api { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.InputCollectible.inputCollectiblePhone(phone: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputCollectible.inputCollectiblePhone(phone: _1!) } public static func parse_inputCollectibleUsername(_ reader: BufferReader) -> InputCollectible? { var _1: String? _1 = parseString(reader) let _c1 = _1 != nil - if _c1 { - return Api.InputCollectible.inputCollectibleUsername(username: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputCollectible.inputCollectibleUsername(username: _1!) } } @@ -728,12 +685,13 @@ public extension Api { let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.InputContact.inputPhoneContact(flags: _1!, clientId: _2!, phone: _3!, firstName: _4!, lastName: _5!, note: _6) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + if !_c5 { return nil } + if !_c6 { return nil } + return Api.InputContact.inputPhoneContact(flags: _1!, clientId: _2!, phone: _3!, firstName: _4!, lastName: _5!, note: _6) } } @@ -775,23 +733,15 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.InputPeer } let _c1 = _1 != nil - if _c1 { - return Api.InputDialogPeer.inputDialogPeer(peer: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputDialogPeer.inputDialogPeer(peer: _1!) } public static func parse_inputDialogPeerFolder(_ reader: BufferReader) -> InputDialogPeer? { var _1: Int32? _1 = reader.readInt32() let _c1 = _1 != nil - if _c1 { - return Api.InputDialogPeer.inputDialogPeerFolder(folderId: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputDialogPeer.inputDialogPeerFolder(folderId: _1!) } } @@ -839,12 +789,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.InputDocument.inputDocument(id: _1!, accessHash: _2!, fileReference: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.InputDocument.inputDocument(id: _1!, accessHash: _2!, fileReference: _3!) } public static func parse_inputDocumentEmpty(_ reader: BufferReader) -> InputDocument? { return Api.InputDocument.inputDocumentEmpty @@ -882,12 +830,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputEncryptedChat.inputEncryptedChat(chatId: _1!, accessHash: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputEncryptedChat.inputEncryptedChat(chatId: _1!, accessHash: _2!) } } @@ -954,12 +899,9 @@ public extension Api { _2 = reader.readInt64() let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.InputEncryptedFile.inputEncryptedFile(id: _1!, accessHash: _2!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + return Api.InputEncryptedFile.inputEncryptedFile(id: _1!, accessHash: _2!) } public static func parse_inputEncryptedFileBigUploaded(_ reader: BufferReader) -> InputEncryptedFile? { var _1: Int64? @@ -971,12 +913,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.InputEncryptedFile.inputEncryptedFileBigUploaded(id: _1!, parts: _2!, keyFingerprint: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.InputEncryptedFile.inputEncryptedFileBigUploaded(id: _1!, parts: _2!, keyFingerprint: _3!) } public static func parse_inputEncryptedFileEmpty(_ reader: BufferReader) -> InputEncryptedFile? { return Api.InputEncryptedFile.inputEncryptedFileEmpty @@ -994,12 +934,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputEncryptedFile.inputEncryptedFileUploaded(id: _1!, parts: _2!, md5Checksum: _3!, keyFingerprint: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.InputEncryptedFile.inputEncryptedFileUploaded(id: _1!, parts: _2!, md5Checksum: _3!, keyFingerprint: _4!) } } @@ -1062,12 +1001,11 @@ public extension Api { let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputFile.inputFile(id: _1!, parts: _2!, name: _3!, md5Checksum: _4!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + if !_c4 { return nil } + return Api.InputFile.inputFile(id: _1!, parts: _2!, name: _3!, md5Checksum: _4!) } public static func parse_inputFileBig(_ reader: BufferReader) -> InputFile? { var _1: Int64? @@ -1079,12 +1017,10 @@ public extension Api { let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.InputFile.inputFileBig(id: _1!, parts: _2!, name: _3!) - } - else { - return nil - } + if !_c1 { return nil } + if !_c2 { return nil } + if !_c3 { return nil } + return Api.InputFile.inputFileBig(id: _1!, parts: _2!, name: _3!) } public static func parse_inputFileStoryDocument(_ reader: BufferReader) -> InputFile? { var _1: Api.InputDocument? @@ -1092,12 +1028,8 @@ public extension Api { _1 = Api.parse(reader, signature: signature) as? Api.InputDocument } let _c1 = _1 != nil - if _c1 { - return Api.InputFile.inputFileStoryDocument(id: _1!) - } - else { - return nil - } + if !_c1 { return nil } + return Api.InputFile.inputFileStoryDocument(id: _1!) } } diff --git a/submodules/TelegramBaseController/Sources/TelegramBaseController.swift b/submodules/TelegramBaseController/Sources/TelegramBaseController.swift index 74588d00..87cad945 100644 --- a/submodules/TelegramBaseController/Sources/TelegramBaseController.swift +++ b/submodules/TelegramBaseController/Sources/TelegramBaseController.swift @@ -14,18 +14,6 @@ import PresentationDataUtils import TelegramCallsUI import UndoUI -public enum MediaAccessoryPanelVisibility { - case none - case specific(size: ContainerViewLayoutSizeClass) - case always -} - -public enum LocationBroadcastPanelSource { - case none - case summary - case peer(PeerId) -} - private func presentLiveLocationController(context: AccountContext, peerId: PeerId, controller: ViewController) { let presentImpl: (EngineMessage?) -> Void = { [weak controller] message in if let message = message, let strongController = controller { @@ -65,332 +53,31 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { public var accessoryPanelContainer: ASDisplayNode? public private(set) var accessoryPanelContainerHeight: CGFloat = 0.0 - public let mediaAccessoryPanelVisibility: MediaAccessoryPanelVisibility public var tempHideAccessoryPanels: Bool = false - public let locationBroadcastPanelSource: LocationBroadcastPanelSource - public let groupCallPanelSource: GroupCallPanelSource - - private var mediaStatusDisposable: Disposable? - private var locationBroadcastDisposable: Disposable? - private var currentGroupCallDisposable: Disposable? - - public private(set) var playlistStateAndType: (SharedMediaPlaylistItem, SharedMediaPlaylistItem?, SharedMediaPlaylistItem?, MusicPlaybackSettingsOrder, MediaManagerPlayerType, Account)? - private var playlistLocation: SharedMediaPlaylistLocation? - - public var tempVoicePlaylistEnded: (() -> Void)? - public var tempVoicePlaylistItemChanged: ((SharedMediaPlaylistItem?, SharedMediaPlaylistItem?) -> Void)? - public var tempVoicePlaylistCurrentItem: SharedMediaPlaylistItem? - - public private(set) var mediaAccessoryPanel: (MediaNavigationAccessoryPanel, MediaManagerPlayerType)? - - private var locationBroadcastMode: LocationBroadcastNavigationAccessoryPanelMode? - private var locationBroadcastPeers: [EnginePeer]? - private var locationBroadcastMessages: [EngineMessage.Id: EngineMessage]? - private var locationBroadcastAccessoryPanel: LocationBroadcastNavigationAccessoryPanel? - private var giftAuctionAccessoryPanel: GiftAuctionAccessoryPanel? private var giftAuctionStates: [GiftAuctionContext.State] = [] private var giftAuctionDisposable: Disposable? - private var groupCallPanelData: GroupCallPanelData? - public private(set) var groupCallAccessoryPanel: GroupCallNavigationAccessoryPanel? - private var dismissingPanel: ASDisplayNode? - private weak var audioRateTooltipController: UndoOverlayController? - private var presentationData: PresentationData private var presentationDataDisposable: Disposable? - private var playlistPreloadDisposable: Disposable? override open var additionalNavigationBarHeight: CGFloat { - var height: CGFloat = 0.0 - if self.accessoryPanelContainer == nil { - if let _ = self.groupCallAccessoryPanel { - height += 50.0 - } - if let _ = self.mediaAccessoryPanel { - height += MediaNavigationAccessoryHeaderNode.minimizedHeight - } - if let _ = self.locationBroadcastAccessoryPanel { - height += MediaNavigationAccessoryHeaderNode.minimizedHeight - } - } - return height + return 0.0 } - public init(context: AccountContext, navigationBarPresentationData: NavigationBarPresentationData?, mediaAccessoryPanelVisibility: MediaAccessoryPanelVisibility, locationBroadcastPanelSource: LocationBroadcastPanelSource, groupCallPanelSource: GroupCallPanelSource) { + public init(context: AccountContext, navigationBarPresentationData: NavigationBarPresentationData?) { self.context = context self.presentationData = context.sharedContext.currentPresentationData.with { $0 } - self.mediaAccessoryPanelVisibility = mediaAccessoryPanelVisibility - self.locationBroadcastPanelSource = locationBroadcastPanelSource - self.groupCallPanelSource = groupCallPanelSource super.init(navigationBarPresentationData: navigationBarPresentationData) - if case .none = mediaAccessoryPanelVisibility { - } else { - self.mediaStatusDisposable = (context.sharedContext.mediaManager.globalMediaPlayerState - |> mapToSignal { playlistStateAndType -> Signal<(Account, SharedMediaPlayerItemPlaybackState, MediaManagerPlayerType)?, NoError> in - if let (account, state, type) = playlistStateAndType { - switch state { - case let .state(state): - return .single((account, state, type)) - case .loading: - return .single(nil) |> delay(0.2, queue: .mainQueue()) - } - } else { - return .single(nil) - } - } - |> deliverOnMainQueue).start(next: { [weak self] playlistStateAndType in - guard let strongSelf = self else { - return - } - if !arePlaylistItemsEqual(strongSelf.playlistStateAndType?.0, playlistStateAndType?.1.item) || - !arePlaylistItemsEqual(strongSelf.playlistStateAndType?.1, playlistStateAndType?.1.previousItem) || - !arePlaylistItemsEqual(strongSelf.playlistStateAndType?.2, playlistStateAndType?.1.nextItem) || - strongSelf.playlistStateAndType?.3 != playlistStateAndType?.1.order || strongSelf.playlistStateAndType?.4 != playlistStateAndType?.2 { - var previousVoiceItem: SharedMediaPlaylistItem? - if let playlistStateAndType = strongSelf.playlistStateAndType, playlistStateAndType.4 == .voice { - previousVoiceItem = playlistStateAndType.0 - } - - var updatedVoiceItem: SharedMediaPlaylistItem? - if let playlistStateAndType = playlistStateAndType, playlistStateAndType.2 == .voice { - updatedVoiceItem = playlistStateAndType.1.item - } - - strongSelf.tempVoicePlaylistCurrentItem = updatedVoiceItem - strongSelf.tempVoicePlaylistItemChanged?(previousVoiceItem, updatedVoiceItem) - if let playlistStateAndType = playlistStateAndType { - strongSelf.playlistStateAndType = (playlistStateAndType.1.item, playlistStateAndType.1.previousItem, playlistStateAndType.1.nextItem, playlistStateAndType.1.order, playlistStateAndType.2, playlistStateAndType.0) - } else { - var voiceEnded = false - if strongSelf.playlistStateAndType?.4 == .voice { - voiceEnded = true - } - strongSelf.playlistStateAndType = nil - if voiceEnded { - strongSelf.tempVoicePlaylistEnded?() - } - } - strongSelf.requestLayout(transition: .animated(duration: 0.4, curve: .spring)) - } - strongSelf.playlistLocation = playlistStateAndType?.1.playlistLocation - }) - } - - if let liveLocationManager = context.liveLocationManager { - switch locationBroadcastPanelSource { - case .none: - self.locationBroadcastMode = nil - case .summary, .peer: - let signal: Signal<([EnginePeer]?, [EngineMessage.Id: EngineMessage]?), NoError> - switch locationBroadcastPanelSource { - case let .peer(peerId): - self.locationBroadcastMode = .peer - signal = combineLatest(liveLocationManager.summaryManager.peersBroadcastingTo(peerId: peerId), liveLocationManager.summaryManager.broadcastingToMessages()) - |> map { peersAndMessages, outgoingMessages in - var peers = peersAndMessages?.map { $0.0 } - for message in outgoingMessages.values { - if message.id.peerId == peerId, let author = message.author { - if peers == nil { - peers = [] - } - peers?.append(author) - } - } - return (peers, outgoingMessages) - } - default: - self.locationBroadcastMode = .summary - signal = liveLocationManager.summaryManager.broadcastingToMessages() - |> map { messages -> ([EnginePeer]?, [EngineMessage.Id: EngineMessage]?) in - if messages.isEmpty { - return (nil, nil) - } else { - var peers: [EnginePeer] = [] - for message in messages.values.sorted(by: { $0.index < $1.index }) { - if let peer = message.peers[message.id.peerId] { - peers.append(EnginePeer(peer)) - } - } - return (peers, messages) - } - } - - } - - self.locationBroadcastDisposable = (signal - |> deliverOnMainQueue).start(next: { [weak self] peers, messages in - if let strongSelf = self { - var updated = false - if let current = strongSelf.locationBroadcastPeers, let peers = peers { - updated = current != peers - } else if (strongSelf.locationBroadcastPeers != nil) != (peers != nil) { - updated = true - } - - strongSelf.locationBroadcastMessages = messages - - if updated { - let wasEmpty = strongSelf.locationBroadcastPeers == nil - strongSelf.locationBroadcastPeers = peers - if wasEmpty != (peers == nil) { - strongSelf.requestLayout(transition: .animated(duration: 0.4, curve: .spring)) - } else if let peers = peers, let locationBroadcastMode = strongSelf.locationBroadcastMode { - var canClose = true - if case let .peer(peerId) = strongSelf.locationBroadcastPanelSource, let messages = messages { - canClose = false - for messageId in messages.keys { - if messageId.peerId == peerId { - canClose = true - } - } - } - strongSelf.locationBroadcastAccessoryPanel?.update(peers: peers, mode: locationBroadcastMode, canClose: canClose) - } - } - } - }) - } - } - - if let callManager = context.sharedContext.callManager { - switch groupCallPanelSource { - case .none, .all: - break - case let .peer(peerId): - let currentGroupCall: Signal = callManager.currentGroupCallSignal - |> distinctUntilChanged(isEqual: { lhs, rhs in - return lhs == rhs - }) - |> map { call -> PresentationGroupCall? in - guard case let .group(call) = call else { - return nil - } - guard call.peerId == peerId && call.account.peerId == context.account.peerId else { - return nil - } - return call - } - - let availableGroupCall: Signal - if case let .peer(peerId) = groupCallPanelSource { - availableGroupCall = context.account.viewTracker.peerView(peerId) - |> map { peerView -> (CachedChannelData.ActiveCall?, EnginePeer?) in - let peer = peerView.peers[peerId].flatMap(EnginePeer.init) - if let cachedData = peerView.cachedData as? CachedChannelData { - return (cachedData.activeCall, peer) - } else if let cachedData = peerView.cachedData as? CachedGroupData { - return (cachedData.activeCall, peer) - } else { - return (nil, peer) - } - } - |> distinctUntilChanged(isEqual: { lhs, rhs in - return lhs.0 == rhs.0 - }) - |> mapToSignal { activeCall, peer -> Signal in - guard let activeCall = activeCall else { - return .single(nil) - } - - var isChannel = false - if let peer = peer, case let .channel(channel) = peer, case .broadcast = channel.info { - isChannel = true - } - - return Signal { [weak context] subscriber in - guard let context = context, let callContextCache = context.cachedGroupCallContexts as? AccountGroupCallContextCacheImpl else { - return EmptyDisposable - } - - let disposable = MetaDisposable() - - callContextCache.impl.syncWith { impl in - let callContext = impl.get(account: context.account, engine: context.engine, peerId: peerId, isChannel: isChannel, call: EngineGroupCallDescription(activeCall)) - disposable.set((callContext.context.panelData - |> deliverOnMainQueue).start(next: { panelData in - callContext.keep() - var updatedPanelData = panelData - if let panelData { - var updatedInfo = panelData.info - updatedInfo.subscribedToScheduled = activeCall.subscribedToScheduled - updatedPanelData = panelData.withInfo(updatedInfo) - } - subscriber.putNext(updatedPanelData) - })) - } - - return ActionDisposable { - disposable.dispose() - } - } - |> runOn(.mainQueue()) - } - } else { - availableGroupCall = .single(nil) - } - - let previousCurrentGroupCall = Atomic(value: nil) - self.currentGroupCallDisposable = combineLatest(queue: .mainQueue(), availableGroupCall, currentGroupCall).start(next: { [weak self] availableState, currentGroupCall in - guard let strongSelf = self else { - return - } - - let previousCurrentGroupCall = previousCurrentGroupCall.swap(currentGroupCall) - - let panelData: GroupCallPanelData? - if previousCurrentGroupCall != nil && currentGroupCall == nil && availableState?.participantCount == 1 { - panelData = nil - } else { - panelData = currentGroupCall != nil || (availableState?.participantCount == 0 && availableState?.info.scheduleTimestamp == nil && availableState?.info.isStream == false) ? nil : availableState - } - - let wasEmpty = strongSelf.groupCallPanelData == nil - strongSelf.groupCallPanelData = panelData - let isEmpty = strongSelf.groupCallPanelData == nil - if wasEmpty != isEmpty { - strongSelf.requestLayout(transition: .animated(duration: 0.4, curve: .spring)) - } else if let groupCallPanelData = strongSelf.groupCallPanelData { - strongSelf.groupCallAccessoryPanel?.update(data: groupCallPanelData) - } - }) - } - } - - if let giftAuctionsManager = context.giftAuctionsManager, case .summary = locationBroadcastPanelSource { - self.giftAuctionDisposable = (giftAuctionsManager.state - |> deliverOnMainQueue).start(next: { [weak self] states in - guard let self else { - return - } - self.giftAuctionStates = states.filter { state in - if case .ongoing = state.auctionState { - return true - } else { - return false - } - } - }) - } - self.presentationDataDisposable = (self.updatedPresentationData.1 |> deliverOnMainQueue).start(next: { [weak self] presentationData in if let strongSelf = self { - let previousTheme = strongSelf.presentationData.theme - let previousStrings = strongSelf.presentationData.strings - strongSelf.presentationData = presentationData - - if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings { - strongSelf.mediaAccessoryPanel?.0.containerNode.updatePresentationData(presentationData) - strongSelf.locationBroadcastAccessoryPanel?.updatePresentationData(presentationData) - strongSelf.groupCallAccessoryPanel?.updatePresentationData(presentationData) - } } }) } @@ -400,11 +87,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { } deinit { - self.mediaStatusDisposable?.dispose() - self.locationBroadcastDisposable?.dispose() - self.currentGroupCallDisposable?.dispose() self.presentationDataDisposable?.dispose() - self.playlistPreloadDisposable?.dispose() } required public init(coder aDecoder: NSCoder) { @@ -429,118 +112,8 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { super.containerLayoutUpdated(layout, transition: transition) - let navigationHeight = super.navigationLayout(layout: layout).navigationFrame.height - self.additionalNavigationBarHeight - - let mediaAccessoryPanelHidden: Bool - if self.tempHideAccessoryPanels { - mediaAccessoryPanelHidden = true - } else { - switch self.mediaAccessoryPanelVisibility { - case .always: - mediaAccessoryPanelHidden = false - case .none: - mediaAccessoryPanelHidden = true - case let .specific(size): - mediaAccessoryPanelHidden = size != layout.metrics.widthClass - } - } - var additionalHeight: CGFloat = 0.0 var panelStartY: CGFloat = 0.0 - if self.accessoryPanelContainer == nil { - var negativeHeight: CGFloat = 0.0 - if let _ = self.groupCallPanelData { - negativeHeight += 50.0 - } - if let _ = self.locationBroadcastPeers, let _ = self.locationBroadcastMode { - negativeHeight += MediaNavigationAccessoryHeaderNode.minimizedHeight - } - if let _ = self.playlistStateAndType, !mediaAccessoryPanelHidden { - negativeHeight += MediaNavigationAccessoryHeaderNode.minimizedHeight - } - panelStartY = navigationHeight.isZero ? (-negativeHeight) : (navigationHeight + additionalHeight + UIScreenPixel) - } - - if let groupCallPanelData = self.groupCallPanelData { - let panelHeight: CGFloat = 50.0 - let panelFrame = CGRect(origin: CGPoint(x: 0.0, y: panelStartY), size: CGSize(width: layout.size.width, height: panelHeight)) - additionalHeight += panelHeight - panelStartY += panelHeight - - let groupCallAccessoryPanel: GroupCallNavigationAccessoryPanel - if let current = self.groupCallAccessoryPanel { - groupCallAccessoryPanel = current - transition.updateFrame(node: groupCallAccessoryPanel, frame: panelFrame) - groupCallAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, isHidden: !self.displayNavigationBar, transition: transition) - } else { - let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - groupCallAccessoryPanel = GroupCallNavigationAccessoryPanel(context: self.context, presentationData: presentationData, tapAction: { [weak self] in - guard let strongSelf = self, let groupCallPanelData = strongSelf.groupCallPanelData else { - return - } - strongSelf.joinGroupCall( - peerId: groupCallPanelData.peerId, - invite: nil, - activeCall: EngineGroupCallDescription(id: groupCallPanelData.info.id, accessHash: groupCallPanelData.info.accessHash, title: groupCallPanelData.info.title, scheduleTimestamp: groupCallPanelData.info.scheduleTimestamp, subscribedToScheduled: groupCallPanelData.info.subscribedToScheduled, isStream: groupCallPanelData.info.isStream) - ) - }, notifyScheduledTapAction: { [weak self] in - guard let self, let groupCallPanelData = self.groupCallPanelData else { - return - } - if groupCallPanelData.info.scheduleTimestamp != nil && !groupCallPanelData.info.subscribedToScheduled { - let _ = self.context.engine.calls.toggleScheduledGroupCallSubscription(peerId: groupCallPanelData.peerId, reference: .id(id: groupCallPanelData.info.id, accessHash: groupCallPanelData.info.accessHash), subscribe: true).startStandalone() - - let controller = UndoOverlayController( - presentationData: presentationData, - content: .universal( - animation: "anim_set_notification", - scale: 0.06, - colors: [ - "Middle.Group 1.Fill 1": UIColor.white, - "Top.Group 1.Fill 1": UIColor.white, - "Bottom.Group 1.Fill 1": UIColor.white, - "EXAMPLE.Group 1.Fill 1": UIColor.white, - "Line.Group 1.Stroke 1": UIColor.white - ], - title: nil, - text: presentationData.strings.Chat_ToastSubscribedToScheduledLiveStream_Text, - customUndoText: nil, - timeout: nil - ), - elevatedLayout: false, - animateInAsReplacement: false, - action: { _ in - return true - } - ) - self.audioRateTooltipController = controller - self.present(controller, in: .current) - } - }) - if let accessoryPanelContainer = self.accessoryPanelContainer { - accessoryPanelContainer.addSubnode(groupCallAccessoryPanel) - } else { - self.navigationBar?.additionalContentNode.addSubnode(groupCallAccessoryPanel) - } - self.groupCallAccessoryPanel = groupCallAccessoryPanel - groupCallAccessoryPanel.frame = panelFrame - - groupCallAccessoryPanel.update(data: groupCallPanelData) - groupCallAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, isHidden: !self.displayNavigationBar, transition: .immediate) - if transition.isAnimated { - groupCallAccessoryPanel.animateIn(transition) - } - } - } else if let groupCallAccessoryPanel = self.groupCallAccessoryPanel { - self.groupCallAccessoryPanel = nil - if transition.isAnimated { - groupCallAccessoryPanel.animateOut(transition, completion: { [weak groupCallAccessoryPanel] in - groupCallAccessoryPanel?.removeFromSupernode() - }) - } else { - groupCallAccessoryPanel.removeFromSupernode() - } - } if !self.giftAuctionStates.isEmpty { let panelHeight: CGFloat = 56.0 @@ -598,454 +171,6 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { giftAuctionAccessoryPanel.removeFromSupernode() } } - - if let locationBroadcastPeers = self.locationBroadcastPeers, let locationBroadcastMode = self.locationBroadcastMode { - let panelHeight = MediaNavigationAccessoryHeaderNode.minimizedHeight - let panelFrame = CGRect(origin: CGPoint(x: 0.0, y: panelStartY), size: CGSize(width: layout.size.width, height: panelHeight)) - additionalHeight += panelHeight - panelStartY += panelHeight - - let locationBroadcastAccessoryPanel: LocationBroadcastNavigationAccessoryPanel - if let current = self.locationBroadcastAccessoryPanel { - locationBroadcastAccessoryPanel = current - transition.updateFrame(node: locationBroadcastAccessoryPanel, frame: panelFrame) - locationBroadcastAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, isHidden: !self.displayNavigationBar, transition: transition) - } else { - let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - locationBroadcastAccessoryPanel = LocationBroadcastNavigationAccessoryPanel(accountPeerId: self.context.account.peerId, theme: presentationData.theme, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, tapAction: { [weak self] in - if let strongSelf = self { - switch strongSelf.locationBroadcastPanelSource { - case .none: - break - case .summary: - if let locationBroadcastMessages = strongSelf.locationBroadcastMessages { - let messages = locationBroadcastMessages.values.sorted(by: { $0.index > $1.index }) - - if messages.count == 1 { - presentLiveLocationController(context: strongSelf.context, peerId: messages[0].id.peerId, controller: strongSelf) - } else { - let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - let controller = ActionSheetController(presentationData: presentationData) - let dismissAction: () -> Void = { [weak controller] in - controller?.dismissAnimated() - } - var items: [ActionSheetItem] = [] - if !messages.isEmpty { - items.append(ActionSheetTextItem(title: presentationData.strings.LiveLocation_MenuChatsCount(Int32(messages.count)))) - for message in messages { - if let peer = message.peers[message.id.peerId] { - var beginTimeAndTimeout: (Double, Double)? - for media in message.media { - if let media = media as? TelegramMediaMap, let timeout = media.liveBroadcastingTimeout { - beginTimeAndTimeout = (Double(message.timestamp), Double(timeout)) - } - } - - if let beginTimeAndTimeout = beginTimeAndTimeout { - items.append(LocationBroadcastActionSheetItem(context: strongSelf.context, peer: peer, title: EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), beginTimestamp: beginTimeAndTimeout.0, timeout: beginTimeAndTimeout.1, strings: presentationData.strings, action: { - dismissAction() - if let strongSelf = self { - presentLiveLocationController(context: strongSelf.context, peerId: peer.id, controller: strongSelf) - } - })) - } - } - } - items.append(ActionSheetButtonItem(title: presentationData.strings.LiveLocation_MenuStopAll, color: .destructive, action: { - dismissAction() - if let locationBroadcastPeers = strongSelf.locationBroadcastPeers { - for peer in locationBroadcastPeers { - self?.context.liveLocationManager?.cancelLiveLocation(peerId: peer.id) - } - } - })) - } - controller.setItemGroups([ - ActionSheetItemGroup(items: items), - ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) - ]) - strongSelf.view.endEditing(true) - strongSelf.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) - } - } - case let .peer(peerId): - presentLiveLocationController(context: strongSelf.context, peerId: peerId, controller: strongSelf) - } - } - }, close: { [weak self] in - if let strongSelf = self { - var closePeers: [EnginePeer]? - var closePeerId: EnginePeer.Id? - switch strongSelf.locationBroadcastPanelSource { - case .none: - break - case .summary: - if let locationBroadcastPeers = strongSelf.locationBroadcastPeers { - if locationBroadcastPeers.count > 1 { - closePeers = locationBroadcastPeers - } else { - closePeerId = locationBroadcastPeers.first?.id - } - } - case let .peer(peerId): - closePeerId = peerId - } - let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - let controller = ActionSheetController(presentationData: presentationData) - let dismissAction: () -> Void = { [weak controller] in - controller?.dismissAnimated() - } - var items: [ActionSheetItem] = [] - if let closePeers = closePeers, !closePeers.isEmpty { - items.append(ActionSheetTextItem(title: presentationData.strings.LiveLocation_MenuChatsCount(Int32(closePeers.count)))) - for peer in closePeers { - items.append(ActionSheetButtonItem(title: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), action: { - dismissAction() - if let strongSelf = self { - presentLiveLocationController(context: strongSelf.context, peerId: peer.id, controller: strongSelf) - } - })) - } - items.append(ActionSheetButtonItem(title: presentationData.strings.LiveLocation_MenuStopAll, color: .destructive, action: { - dismissAction() - for peer in closePeers { - self?.context.liveLocationManager?.cancelLiveLocation(peerId: peer.id) - } - })) - } else if let closePeerId = closePeerId { - items.append(ActionSheetButtonItem(title: presentationData.strings.Map_StopLiveLocation, color: .destructive, action: { - dismissAction() - self?.context.liveLocationManager?.cancelLiveLocation(peerId: closePeerId) - })) - } - controller.setItemGroups([ - ActionSheetItemGroup(items: items), - ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) - ]) - strongSelf.view.endEditing(true) - strongSelf.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) - } - }) - if let accessoryPanelContainer = self.accessoryPanelContainer { - accessoryPanelContainer.addSubnode(locationBroadcastAccessoryPanel) - } else { - self.navigationBar?.additionalContentNode.addSubnode(locationBroadcastAccessoryPanel) - } - self.locationBroadcastAccessoryPanel = locationBroadcastAccessoryPanel - locationBroadcastAccessoryPanel.frame = panelFrame - - var canClose = true - if case let .peer(peerId) = self.locationBroadcastPanelSource, let messages = self.locationBroadcastMessages { - canClose = false - for messageId in messages.keys { - if messageId.peerId == peerId { - canClose = true - } - } - } - - locationBroadcastAccessoryPanel.update(peers: locationBroadcastPeers, mode: locationBroadcastMode, canClose: canClose) - locationBroadcastAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, isHidden: !self.displayNavigationBar, transition: .immediate) - if transition.isAnimated { - locationBroadcastAccessoryPanel.animateIn(transition) - } - } - } else if let locationBroadcastAccessoryPanel = self.locationBroadcastAccessoryPanel { - self.locationBroadcastAccessoryPanel = nil - if transition.isAnimated { - locationBroadcastAccessoryPanel.animateOut(transition, completion: { [weak locationBroadcastAccessoryPanel] in - locationBroadcastAccessoryPanel?.removeFromSupernode() - }) - } else { - locationBroadcastAccessoryPanel.removeFromSupernode() - } - } - - var isViewOnceMessage = false - if let (item, _, _, _, _, _) = self.playlistStateAndType, let source = item.playbackData?.source, case let .telegramFile(_, _, isViewOnce) = source, isViewOnce { - isViewOnceMessage = true - } - - if let (item, previousItem, nextItem, order, type, _) = self.playlistStateAndType, !mediaAccessoryPanelHidden && !isViewOnceMessage { - let panelHeight = MediaNavigationAccessoryHeaderNode.minimizedHeight - let panelFrame = CGRect(origin: CGPoint(x: 0.0, y: panelStartY), size: CGSize(width: layout.size.width, height: panelHeight)) - additionalHeight += panelHeight - panelStartY += panelHeight - - if let (mediaAccessoryPanel, mediaType) = self.mediaAccessoryPanel, mediaType == type { - transition.updateFrame(layer: mediaAccessoryPanel.layer, frame: panelFrame) - mediaAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, isHidden: !self.displayNavigationBar, transition: transition) - switch order { - case .regular: - mediaAccessoryPanel.containerNode.headerNode.playbackItems = (item, previousItem, nextItem) - case .reversed: - mediaAccessoryPanel.containerNode.headerNode.playbackItems = (item, nextItem, previousItem) - case .random: - mediaAccessoryPanel.containerNode.headerNode.playbackItems = (item, nil, nil) - } - let delayedStatus = self.context.sharedContext.mediaManager.globalMediaPlayerState - |> mapToSignal { value -> Signal<(Account, SharedMediaPlayerItemPlaybackStateOrLoading, MediaManagerPlayerType)?, NoError> in - guard let value = value else { - return .single(nil) - } - switch value.1 { - case .state: - return .single(value) - case .loading: - return .single(value) |> delay(0.1, queue: .mainQueue()) - } - } - - mediaAccessoryPanel.containerNode.headerNode.playbackStatus = delayedStatus - |> map { state -> MediaPlayerStatus in - if let stateOrLoading = state?.1, case let .state(state) = stateOrLoading { - return state.status - } else { - return MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .paused, soundEnabled: true) - } - } - } else { - if let (mediaAccessoryPanel, _) = self.mediaAccessoryPanel { - self.mediaAccessoryPanel = nil - self.dismissingPanel = mediaAccessoryPanel - self.audioRateTooltipController?.dismissWithCommitAction() - mediaAccessoryPanel.animateOut(transition: transition, completion: { [weak self, weak mediaAccessoryPanel] in - mediaAccessoryPanel?.removeFromSupernode() - if let strongSelf = self, strongSelf.dismissingPanel === mediaAccessoryPanel { - strongSelf.dismissingPanel = nil - } - }) - } - - let mediaAccessoryPanel = MediaNavigationAccessoryPanel(context: self.context, presentationData: self.updatedPresentationData.0) - mediaAccessoryPanel.containerNode.headerNode.displayScrubber = item.playbackData?.type != .instantVideo - mediaAccessoryPanel.getController = { [weak self] in - return self - } - mediaAccessoryPanel.presentInGlobalOverlay = { [weak self] c in - self?.presentInGlobalOverlay(c) - } - mediaAccessoryPanel.close = { [weak self] in - if let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType { - strongSelf.context.sharedContext.mediaManager.setPlaylist(nil, type: type, control: SharedMediaPlayerControlAction.playback(.pause)) - } - } - mediaAccessoryPanel.setRate = { [weak self] rate, changeType in - guard let strongSelf = self else { - return - } - let _ = (strongSelf.context.sharedContext.accountManager.transaction { transaction -> AudioPlaybackRate in - let settings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings)?.get(MusicPlaybackSettings.self) ?? MusicPlaybackSettings.defaultSettings - - transaction.updateSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings, { _ in - return PreferencesEntry(settings.withUpdatedVoicePlaybackRate(rate)) - }) - return rate - } - |> deliverOnMainQueue).start(next: { baseRate in - guard let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType else { - return - } - strongSelf.context.sharedContext.mediaManager.playlistControl(.setBaseRate(baseRate), type: type) - - var hasTooltip = false - strongSelf.forEachController({ controller in - if let controller = controller as? UndoOverlayController { - hasTooltip = true - controller.dismissWithCommitAction() - } - return true - }) - - let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - let text: String? - let rate: CGFloat? - if case let .sliderCommit(previousValue, newValue) = changeType { - let value = String(format: "%0.1f", baseRate.doubleValue) - if baseRate == .x1 { - text = presentationData.strings.Conversation_AudioRateTooltipNormal - } else { - text = presentationData.strings.Conversation_AudioRateTooltipCustom(value).string - } - if newValue > previousValue { - rate = .infinity - } else if newValue < previousValue { - rate = -.infinity - } else { - rate = nil - } - } else if baseRate == .x1 { - text = presentationData.strings.Conversation_AudioRateTooltipNormal - rate = 1.0 - } else if baseRate == .x1_5 { - text = presentationData.strings.Conversation_AudioRateTooltip15X - rate = 1.5 - } else if baseRate == .x2 { - text = presentationData.strings.Conversation_AudioRateTooltipSpeedUp - rate = 2.0 - } else { - text = nil - rate = nil - } - var showTooltip = true - if case .sliderChange = changeType { - showTooltip = false - } - if let rate, let text, showTooltip { - let controller = UndoOverlayController( - presentationData: presentationData, - content: .audioRate( - rate: rate, - text: text - ), - elevatedLayout: false, - animateInAsReplacement: hasTooltip, - action: { action in - return true - } - ) - strongSelf.audioRateTooltipController = controller - strongSelf.present(controller, in: .current) - } - }) - } - mediaAccessoryPanel.togglePlayPause = { [weak self] in - if let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType { - strongSelf.context.sharedContext.mediaManager.playlistControl(.playback(.togglePlayPause), type: type) - } - } - mediaAccessoryPanel.playPrevious = { [weak self] in - if let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType { - strongSelf.context.sharedContext.mediaManager.playlistControl(.next, type: type) - } - } - mediaAccessoryPanel.playNext = { [weak self] in - if let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType { - strongSelf.context.sharedContext.mediaManager.playlistControl(.previous, type: type) - } - } - mediaAccessoryPanel.tapAction = { [weak self] in - guard let strongSelf = self, let _ = strongSelf.navigationController as? NavigationController, let (state, _, _, order, type, account) = strongSelf.playlistStateAndType else { - return - } - if let id = state.id as? PeerMessagesMediaPlaylistItemId, let playlistLocation = strongSelf.playlistLocation as? PeerMessagesPlaylistLocation { - if type == .music { - switch playlistLocation { - case .custom, .savedMusic: - let controllerContext: AccountContext - if account.id == strongSelf.context.account.id { - controllerContext = strongSelf.context - } else { - controllerContext = strongSelf.context.sharedContext.makeTempAccountContext(account: account) - } - let controller = strongSelf.context.sharedContext.makeOverlayAudioPlayerController(context: controllerContext, chatLocation: .peer(id: id.messageId.peerId), type: type, initialMessageId: id.messageId, initialOrder: order, playlistLocation: playlistLocation, parentNavigationController: strongSelf.navigationController as? NavigationController) - strongSelf.displayNode.view.window?.endEditing(true) - strongSelf.present(controller, in: .window(.root)) - case let .messages(chatLocation, _, _): - let signal = strongSelf.context.sharedContext.messageFromPreloadedChatHistoryViewForLocation(id: id.messageId, location: ChatHistoryLocationInput(content: .InitialSearch(subject: MessageHistoryInitialSearchSubject(location: .id(id.messageId)), count: 60, highlight: true, setupReply: false), id: 0), context: strongSelf.context, chatLocation: chatLocation, subject: nil, chatLocationContextHolder: Atomic(value: nil), tag: .tag(MessageTags.music)) - - var cancelImpl: (() -> Void)? - let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - let progressSignal = Signal { subscriber in - let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: { - cancelImpl?() - })) - self?.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 = MetaDisposable() - var progressStarted = false - strongSelf.playlistPreloadDisposable?.dispose() - strongSelf.playlistPreloadDisposable = (signal - |> afterDisposed { - Queue.mainQueue().async { - progressDisposable.dispose() - } - } - |> deliverOnMainQueue).start(next: { index in - guard let strongSelf = self else { - return - } - if let _ = index.0 { - let controllerContext: AccountContext - if account.id == strongSelf.context.account.id { - controllerContext = strongSelf.context - } else { - controllerContext = strongSelf.context.sharedContext.makeTempAccountContext(account: account) - } - let controller = strongSelf.context.sharedContext.makeOverlayAudioPlayerController(context: controllerContext, chatLocation: chatLocation, type: type, initialMessageId: id.messageId, initialOrder: order, playlistLocation: nil, parentNavigationController: strongSelf.navigationController as? NavigationController) - strongSelf.displayNode.view.window?.endEditing(true) - strongSelf.present(controller, in: .window(.root)) - } else if index.1 { - if !progressStarted { - progressStarted = true - progressDisposable.set(progressSignal.start()) - } - } - }, completed: { - }) - cancelImpl = { - self?.playlistPreloadDisposable?.dispose() - } - default: - break - } - } else { - strongSelf.context.sharedContext.navigateToChat(accountId: strongSelf.context.account.id, peerId: id.messageId.peerId, messageId: id.messageId) - } - } - } - mediaAccessoryPanel.frame = panelFrame - if let dismissingPanel = self.dismissingPanel { - if let accessoryPanelContainer = self.accessoryPanelContainer { - accessoryPanelContainer.insertSubnode(mediaAccessoryPanel, aboveSubnode: dismissingPanel) - } else { - self.navigationBar?.additionalContentNode.insertSubnode(mediaAccessoryPanel, aboveSubnode: dismissingPanel) - } - } else { - if let accessoryPanelContainer = self.accessoryPanelContainer { - accessoryPanelContainer.addSubnode(mediaAccessoryPanel) - } else { - self.navigationBar?.additionalContentNode.addSubnode(mediaAccessoryPanel) - } - } - self.mediaAccessoryPanel = (mediaAccessoryPanel, type) - mediaAccessoryPanel.updateLayout(size: panelFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, isHidden: !self.displayNavigationBar, transition: .immediate) - switch order { - case .regular: - mediaAccessoryPanel.containerNode.headerNode.playbackItems = (item, previousItem, nextItem) - case .reversed: - mediaAccessoryPanel.containerNode.headerNode.playbackItems = (item, nextItem, previousItem) - case .random: - mediaAccessoryPanel.containerNode.headerNode.playbackItems = (item, nil, nil) - } - mediaAccessoryPanel.containerNode.headerNode.playbackStatus = self.context.sharedContext.mediaManager.globalMediaPlayerState - |> map { state -> MediaPlayerStatus in - if let stateOrLoading = state?.1, case let .state(state) = stateOrLoading { - return state.status - } else { - return MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .paused, soundEnabled: true) - } - } - mediaAccessoryPanel.animateIn(transition: transition) - } - } else if let (mediaAccessoryPanel, _) = self.mediaAccessoryPanel { - self.mediaAccessoryPanel = nil - self.dismissingPanel = mediaAccessoryPanel - self.audioRateTooltipController?.dismissWithCommitAction() - mediaAccessoryPanel.animateOut(transition: transition, completion: { [weak self, weak mediaAccessoryPanel] in - mediaAccessoryPanel?.removeFromSupernode() - if let strongSelf = self, strongSelf.dismissingPanel === mediaAccessoryPanel { - strongSelf.dismissingPanel = nil - } - }) - } self.suspendNavigationBarLayout = false if let suspendedNavigationBarLayout = self.suspendedNavigationBarLayout { diff --git a/submodules/TelegramCallsUI/BUILD b/submodules/TelegramCallsUI/BUILD index bad899d0..c771b6f4 100644 --- a/submodules/TelegramCallsUI/BUILD +++ b/submodules/TelegramCallsUI/BUILD @@ -115,7 +115,6 @@ swift_library( "//submodules/TelegramUI/Components/LottieComponent", "//submodules/TelegramUI/Components/Stories/PeerListItemComponent", "//submodules/TelegramUI/Components/BackButtonComponent", - "//submodules/TelegramUI/Components/AlertComponent", "//submodules/Components/BlurredBackgroundComponent", "//submodules/DirectMediaImageCache", "//submodules/FastBlur", @@ -131,6 +130,8 @@ swift_library( "//submodules/TelegramUI/Components/EdgeEffect", "//submodules/PremiumUI", "//submodules/TelegramUI/Components/Calls/VideoChatMicButtonComponent", + "//submodules/TelegramUI/Components/AlertComponent", + "//submodules/TelegramUI/Components/AlertComponent/AlertInputFieldComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramCallsUI/Sources/AccountGroupCallContextImpl.swift b/submodules/TelegramCallsUI/Sources/AccountGroupCallContextImpl.swift index 5b348c93..44f7f27b 100644 --- a/submodules/TelegramCallsUI/Sources/AccountGroupCallContextImpl.swift +++ b/submodules/TelegramCallsUI/Sources/AccountGroupCallContextImpl.swift @@ -24,6 +24,46 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext { var disposable: Disposable? public var participantsContext: GroupCallParticipantsContext? + public final class GroupCallPanelData { + public let peerId: EnginePeer.Id + public let isChannel: Bool + public let info: GroupCallInfo + public let topParticipants: [GroupCallParticipantsContext.Participant] + public let participantCount: Int + public let activeSpeakers: Set + public let groupCall: PresentationGroupCall? + + public init( + peerId: EnginePeer.Id, + isChannel: Bool, + info: GroupCallInfo, + topParticipants: [GroupCallParticipantsContext.Participant], + participantCount: Int, + activeSpeakers: Set, + groupCall: PresentationGroupCall? + ) { + self.peerId = peerId + self.isChannel = isChannel + self.info = info + self.topParticipants = topParticipants + self.participantCount = participantCount + self.activeSpeakers = activeSpeakers + self.groupCall = groupCall + } + + public func withInfo(_ info: GroupCallInfo) -> GroupCallPanelData { + return GroupCallPanelData( + peerId: self.peerId, + isChannel: self.isChannel, + info: info, + topParticipants: self.topParticipants, + participantCount: self.participantCount, + activeSpeakers: self.activeSpeakers, + groupCall: self.groupCall + ) + } + } + private let panelDataPromise = Promise() public var panelData: Signal { return self.panelDataPromise.get() diff --git a/submodules/TelegramCallsUI/Sources/CallFeedbackController.swift b/submodules/TelegramCallsUI/Sources/CallFeedbackController.swift index 4a47a8b8..9e8a0454 100644 --- a/submodules/TelegramCallsUI/Sources/CallFeedbackController.swift +++ b/submodules/TelegramCallsUI/Sources/CallFeedbackController.swift @@ -188,11 +188,11 @@ private enum CallFeedbackControllerEntry: ItemListNodeEntry { case let .reasonsHeader(_, text): return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) case let .reason(_, reason, title, value): - return ItemListSwitchItem(presentationData: presentationData, title: title, value: value, maximumNumberOfLines: 2, sectionId: self.section, style: .blocks, updated: { value in + return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: title, value: value, maximumNumberOfLines: 2, sectionId: self.section, style: .blocks, updated: { value in arguments.toggleReason(reason, value) }) case let .comment(_, text, placeholder): - return ItemListMultilineInputItem(presentationData: presentationData, text: text, placeholder: placeholder, maxLength: nil, sectionId: self.section, style: .blocks, textUpdated: { updatedText in + return ItemListMultilineInputItem(presentationData: presentationData, systemStyle: .glass, text: text, placeholder: placeholder, maxLength: nil, sectionId: self.section, style: .blocks, textUpdated: { updatedText in arguments.updateComment(updatedText) }, updatedFocus: { focused in if focused { @@ -200,7 +200,7 @@ private enum CallFeedbackControllerEntry: ItemListNodeEntry { } }, tag: CallFeedbackControllerEntryTag.comment) case let .includeLogs(_, title, value): - return ItemListSwitchItem(presentationData: presentationData, title: title, value: value, sectionId: self.section, style: .blocks, updated: { value in + return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: title, value: value, sectionId: self.section, style: .blocks, updated: { value in arguments.toggleIncludeLogs(value) }) case let .includeLogsInfo(_, text): diff --git a/submodules/TelegramCallsUI/Sources/CallRatingController.swift b/submodules/TelegramCallsUI/Sources/CallRatingController.swift index fa86baa0..7a086261 100644 --- a/submodules/TelegramCallsUI/Sources/CallRatingController.swift +++ b/submodules/TelegramCallsUI/Sources/CallRatingController.swift @@ -3,285 +3,13 @@ import UIKit import SwiftSignalKit import AsyncDisplayKit import Display +import ComponentFlow import Postbox import TelegramCore import TelegramPresentationData import TelegramVoip import AccountContext -import AppBundle - -private final class CallRatingAlertContentNode: AlertContentNode { - private let strings: PresentationStrings - private let apply: (Int) -> Void - - var rating: Int? - - private let titleNode: ASTextNode - private var starContainerNode: ASDisplayNode - private let starNodes: [ASButtonNode] - - private let actionNodesSeparator: ASDisplayNode - private let actionNodes: [TextAlertContentActionNode] - private let actionVerticalSeparators: [ASDisplayNode] - - private let disposable = MetaDisposable() - - private var validLayout: CGSize? - - override var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled - } - - init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction], dismiss: @escaping () -> Void, apply: @escaping (Int) -> Void) { - self.strings = strings - self.apply = apply - - self.titleNode = ASTextNode() - self.titleNode.maximumNumberOfLines = 3 - - self.starContainerNode = ASDisplayNode() - - var starNodes: [ASButtonNode] = [] - for _ in 0 ..< 5 { - starNodes.append(ASButtonNode()) - } - self.starNodes = starNodes - - self.actionNodesSeparator = ASDisplayNode() - self.actionNodesSeparator.isLayerBacked = true - - self.actionNodes = actions.map { action -> TextAlertContentActionNode in - return TextAlertContentActionNode(theme: theme, action: action) - } - - var actionVerticalSeparators: [ASDisplayNode] = [] - if actions.count > 1 { - for _ in 0 ..< actions.count - 1 { - let separatorNode = ASDisplayNode() - separatorNode.isLayerBacked = true - actionVerticalSeparators.append(separatorNode) - } - } - self.actionVerticalSeparators = actionVerticalSeparators - - super.init() - - self.addSubnode(self.titleNode) - - self.addSubnode(self.starContainerNode) - - for node in self.starNodes { - node.addTarget(self, action: #selector(self.starPressed(_:)), forControlEvents: .touchDown) - node.addTarget(self, action: #selector(self.starReleased(_:)), forControlEvents: .touchUpInside) - self.starContainerNode.addSubnode(node) - } - - self.addSubnode(self.actionNodesSeparator) - - for actionNode in self.actionNodes { - self.addSubnode(actionNode) - } - - for separatorNode in self.actionVerticalSeparators { - self.addSubnode(separatorNode) - } - - self.updateTheme(theme) - } - - deinit { - self.disposable.dispose() - } - - override func didLoad() { - super.didLoad() - - self.starContainerNode.view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))) - } - - @objc func panGesture(_ gestureRecognizer: UIPanGestureRecognizer) { - let location = gestureRecognizer.location(in: self.starContainerNode.view) - var selectedNode: ASButtonNode? - for node in self.starNodes { - if node.frame.contains(location) { - selectedNode = node - break - } - } - if let selectedNode = selectedNode { - switch gestureRecognizer.state { - case .began, .changed: - self.starPressed(selectedNode) - case .ended: - self.starReleased(selectedNode) - case .cancelled: - self.resetStars() - default: - break - } - } else { - self.resetStars() - } - } - - private func resetStars() { - for i in 0 ..< self.starNodes.count { - let node = self.starNodes[i] - node.isSelected = false - } - } - - @objc func starPressed(_ sender: ASButtonNode) { - if let index = self.starNodes.firstIndex(of: sender) { - self.rating = index + 1 - for i in 0 ..< self.starNodes.count { - let node = self.starNodes[i] - node.isSelected = i <= index - } - } - } - - @objc func starReleased(_ sender: ASButtonNode) { - if let index = self.starNodes.firstIndex(of: sender) { - self.rating = index + 1 - for i in 0 ..< self.starNodes.count { - let node = self.starNodes[i] - node.isSelected = i <= index - } - if let rating = self.rating { - self.apply(rating) - } - } - } - - override func updateTheme(_ theme: AlertControllerTheme) { - self.titleNode.attributedText = NSAttributedString(string: self.strings.Calls_RatingTitle, font: Font.bold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center) - - for node in self.starNodes { - node.setImage(generateTintedImage(image: UIImage(bundleImageName: "Call/Star"), color: theme.accentColor), for: []) - let highlighted = generateTintedImage(image: UIImage(bundleImageName: "Call/StarHighlighted"), color: theme.accentColor) - node.setImage(highlighted, for: [.selected]) - node.setImage(highlighted, for: [.selected, .highlighted]) - } - - self.actionNodesSeparator.backgroundColor = theme.separatorColor - for actionNode in self.actionNodes { - actionNode.updateTheme(theme) - } - for separatorNode in self.actionVerticalSeparators { - separatorNode.backgroundColor = theme.separatorColor - } - - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) - } - } - - override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - var size = size - size.width = min(size.width , 270.0) - - self.validLayout = size - - let actionButtonHeight: CGFloat = 44.0 - var minActionsWidth: CGFloat = 0.0 - let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) - let actionTitleInsets: CGFloat = 8.0 - - var effectiveActionLayout = TextAlertContentActionLayout.horizontal - for actionNode in self.actionNodes { - let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) - if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 { - effectiveActionLayout = .vertical - } - switch effectiveActionLayout { - case .horizontal: - minActionsWidth += actionTitleSize.width + actionTitleInsets - case .vertical: - minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) - } - } - - let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0) - - var origin: CGPoint = CGPoint(x: 0.0, y: 20.0) - let titleSize = self.titleNode.measure(CGSize(width: size.width - 32.0, height: size.height)) - - var contentWidth = max(titleSize.width, minActionsWidth) - contentWidth = max(contentWidth, 234.0) - - var actionsHeight: CGFloat = 0.0 - switch effectiveActionLayout { - case .horizontal: - actionsHeight = actionButtonHeight - case .vertical: - actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) - } - - let resultWidth = contentWidth + insets.left + insets.right - - transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((resultWidth - titleSize.width) / 2.0), y: origin.y), size: titleSize)) - origin.y += titleSize.height + 13.0 - - let starSize = CGSize(width: 42.0, height: 38.0) - let starsOrigin = floorToScreenPixels((resultWidth - starSize.width * 5.0) / 2.0) - self.starContainerNode.frame = CGRect(origin: CGPoint(x: starsOrigin, y: origin.y), size: CGSize(width: starSize.width * CGFloat(self.starNodes.count), height: starSize.height)) - for i in 0 ..< self.starNodes.count { - let node = self.starNodes[i] - transition.updateFrame(node: node, frame: CGRect(x: starSize.width * CGFloat(i), y: 0.0, width: starSize.width, height: starSize.height)) - } - origin.y += titleSize.height - - let resultSize = CGSize(width: resultWidth, height: titleSize.height + actionsHeight + 56.0 + insets.top + insets.bottom) - - transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - - var actionOffset: CGFloat = 0.0 - let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) - var separatorIndex = -1 - var nodeIndex = 0 - for actionNode in self.actionNodes { - if separatorIndex >= 0 { - let separatorNode = self.actionVerticalSeparators[separatorIndex] - switch effectiveActionLayout { - case .horizontal: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) - case .vertical: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - } - } - separatorIndex += 1 - - let currentActionWidth: CGFloat - switch effectiveActionLayout { - case .horizontal: - if nodeIndex == self.actionNodes.count - 1 { - currentActionWidth = resultSize.width - actionOffset - } else { - currentActionWidth = actionWidth - } - case .vertical: - currentActionWidth = resultSize.width - } - - let actionNodeFrame: CGRect - switch effectiveActionLayout { - case .horizontal: - actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += currentActionWidth - case .vertical: - actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += actionButtonHeight - } - - transition.updateFrame(node: actionNode, frame: actionNodeFrame) - - nodeIndex += 1 - } - - return resultSize - } -} +import AlertComponent func rateCallAndSendLogs(engine: TelegramEngine, callId: CallId, starsCount: Int, comment: String, userInitiated: Bool, includeLogs: Bool) -> Signal { let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(4244000)) @@ -309,35 +37,199 @@ func rateCallAndSendLogs(engine: TelegramEngine, callId: CallId, starsCount: Int } } -public func callRatingController(sharedContext: SharedAccountContext, account: Account, callId: CallId, userInitiated: Bool, isVideo: Bool, present: @escaping (ViewController, Any) -> Void, push: @escaping (ViewController) -> Void) -> AlertController { - let presentationData = sharedContext.currentPresentationData.with { $0 } - let theme = presentationData.theme - let strings = presentationData.strings +public func callRatingController( + sharedContext: SharedAccountContext, + account: Account, + callId: CallId, + userInitiated: Bool, + isVideo: Bool, + present: @escaping (ViewController, Any) -> Void, + push: @escaping (ViewController) -> Void +) -> ViewController { + let strings = sharedContext.currentPresentationData.with { $0 }.strings - var dismissImpl: ((Bool) -> Void)? - var contentNode: CallRatingAlertContentNode? - let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_NotNow, action: { - dismissImpl?(true) - })] + var dismissImpl: (() -> Void)? - contentNode = CallRatingAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: theme, strings: strings, actions: actions, dismiss: { - dismissImpl?(true) - }, apply: { rating in - dismissImpl?(true) - if rating < 4 { - push(callFeedbackController(sharedContext: sharedContext, account: account, callId: callId, rating: rating, userInitiated: userInitiated, isVideo: isVideo)) - } else { - let _ = rateCallAndSendLogs(engine: TelegramEngine(account: account), callId: callId, starsCount: rating, comment: "", userInitiated: userInitiated, includeLogs: false).start() + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent( + title: strings.Calls_RatingTitle, + alignment: .center + ) + ) + )) + content.append(AnyComponentWithIdentity( + id: "stars", + component: AnyComponent( + AlertCallRatingComponent(completion: { rating in + dismissImpl?() + if rating < 4 { + push(callFeedbackController(sharedContext: sharedContext, account: account, callId: callId, rating: rating, userInitiated: userInitiated, isVideo: isVideo)) + } else { + let _ = rateCallAndSendLogs(engine: TelegramEngine(account: account), callId: callId, starsCount: rating, comment: "", userInitiated: userInitiated, includeLogs: false).start() + } + }) + ) + )) + + let alertController = AlertScreen( + sharedContext: sharedContext, + content: content, + actions: [ + .init(title: strings.Common_NotNow) + ] + ) + + dismissImpl = { [weak alertController] in + alertController?.dismiss(completion: nil) + } + + return alertController +} + +private final class AlertCallRatingComponent: Component { + public typealias EnvironmentType = AlertComponentEnvironment + + private let completion: (Int) -> Void + + public init(completion: @escaping (Int) -> Void) { + self.completion = completion + } + + public static func ==(lhs: AlertCallRatingComponent, rhs: AlertCallRatingComponent) -> Bool { + return true + } + + public final class View: UIView { + private var containerView: UIView + private let starButtons: [HighlightTrackingButton] + + var rating: Int? + + private var component: AlertCallRatingComponent? + private weak var state: EmptyComponentState? + + public override init(frame: CGRect) { + self.containerView = UIView() + + var starButtons: [HighlightTrackingButton] = [] + for _ in 0 ..< 5 { + starButtons.append(HighlightTrackingButton()) + } + self.starButtons = starButtons + + super.init(frame: frame) + + self.addSubview(self.containerView) + + for button in self.starButtons { + button.addTarget(self, action: #selector(self.starPressed(_:)), for: .touchDown) + button.addTarget(self, action: #selector(self.starReleased(_:)), for: .touchUpInside) + self.containerView.addSubview(button) + } + + self.containerView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))) } - }) - - let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode!) - dismissImpl = { [weak controller] animated in - if animated { - controller?.dismissAnimated() - } else { - controller?.dismiss() + + public required init?(coder: NSCoder) { + preconditionFailure() + } + + @objc func panGesture(_ gestureRecognizer: UIPanGestureRecognizer) { + let location = gestureRecognizer.location(in: self.containerView) + var selectedButton: HighlightTrackingButton? + for button in self.starButtons { + if button.frame.contains(location) { + selectedButton = button + break + } + } + if let selectedButton = selectedButton { + switch gestureRecognizer.state { + case .began, .changed: + self.starPressed(selectedButton) + case .ended: + self.starReleased(selectedButton) + case .cancelled: + self.resetStars() + default: + break + } + } else { + self.resetStars() + } + } + + private func resetStars() { + for i in 0 ..< self.starButtons.count { + let node = self.starButtons[i] + node.isSelected = false + } + } + + @objc func starPressed(_ sender: HighlightTrackingButton) { + if let index = self.starButtons.firstIndex(of: sender) { + self.rating = index + 1 + for i in 0 ..< self.starButtons.count { + let node = self.starButtons[i] + node.isSelected = i <= index + } + } + } + + @objc func starReleased(_ sender: HighlightTrackingButton) { + guard let component = self.component else { + return + } + if let index = self.starButtons.firstIndex(of: sender) { + self.rating = index + 1 + for i in 0 ..< self.starButtons.count { + let node = self.starButtons[i] + node.isSelected = i <= index + } + if let rating = self.rating { + component.completion(rating) + } + } + } + + func update(component: AlertCallRatingComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + if self.component == nil { + for i in 0 ..< self.starButtons.count { + let button = self.starButtons[i] + button.setImage(UIImage(bundleImageName: "Call/Star")?.withRenderingMode(.alwaysTemplate), for: .normal) + button.setImage(UIImage(bundleImageName: "Call/StarHighlighted")?.withRenderingMode(.alwaysTemplate), for: .selected) + button.setImage(UIImage(bundleImageName: "Call/StarHighlighted")?.withRenderingMode(.alwaysTemplate), for: [.selected, .highlighted]) + } + } + + self.component = component + self.state = state + + let environment = environment[AlertComponentEnvironment.self] + + let buttonCount = CGFloat(self.starButtons.count) + let starSize = CGSize(width: 42.0, height: 38.0) + let starsOrigin = floorToScreenPixels((availableSize.width - starSize.width * buttonCount) / 2.0) + self.containerView.frame = CGRect(origin: CGPoint(x: starsOrigin, y: 0.0), size: CGSize(width: starSize.width * buttonCount, height: starSize.height)) + for i in 0 ..< self.starButtons.count { + let button = self.starButtons[i] + button.imageView?.tintColor = environment.theme.actionSheet.controlAccentColor + + transition.setFrame(view: button, frame: CGRect(x: starSize.width * CGFloat(i), y: 0.0, width: starSize.width, height: starSize.height)) + } + + return CGSize(width: availableSize.width, height: 38.0) } } - return controller + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } } diff --git a/submodules/TelegramCallsUI/Sources/CallStatusBarNode.swift b/submodules/TelegramCallsUI/Sources/CallStatusBarNode.swift index e40dafcf..04529dbe 100644 --- a/submodules/TelegramCallsUI/Sources/CallStatusBarNode.swift +++ b/submodules/TelegramCallsUI/Sources/CallStatusBarNode.swift @@ -22,6 +22,22 @@ private let pink = UIColor(rgb: 0xef436c) private let latePurple = UIColor(rgb: 0xaa56a6) private let latePink = UIColor(rgb: 0xef476f) +private func textForTimeout(value: Int32) -> String { + if value < 3600 { + let minutes = value / 60 + let seconds = value % 60 + let secondsPadding = seconds < 10 ? "0" : "" + return "\(minutes):\(secondsPadding)\(seconds)" + } else { + let hours = value / 3600 + let minutes = (value % 3600) / 60 + let minutesPadding = minutes < 10 ? "0" : "" + let seconds = value % 60 + let secondsPadding = seconds < 10 ? "0" : "" + return "\(hours):\(minutesPadding)\(minutes):\(secondsPadding)\(seconds)" + } +} + private class CallStatusBarBackgroundNode: ASDisplayNode { enum State { case connecting diff --git a/submodules/TelegramCallsUI/Sources/CallSuggestTabController.swift b/submodules/TelegramCallsUI/Sources/CallSuggestTabController.swift index 2d3cfa22..cfb4c157 100644 --- a/submodules/TelegramCallsUI/Sources/CallSuggestTabController.swift +++ b/submodules/TelegramCallsUI/Sources/CallSuggestTabController.swift @@ -3,237 +3,117 @@ import UIKit import SwiftSignalKit import AsyncDisplayKit import Display -import Postbox import TelegramCore import TelegramPresentationData import TelegramUIPreferences import AccountContext import AppBundle +import ComponentFlow +import AlertComponent +import BundleIconComponent -private final class CallSuggestTabAlertContentNode: AlertContentNode { - private let strings: PresentationStrings +public func callSuggestTabController(sharedContext: SharedAccountContext) -> ViewController { + let strings = sharedContext.currentPresentationData.with { $0 }.strings + + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "header", + component: AnyComponent( + AlertCallSuggestHeaderComponent() + ) + )) + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: strings.Calls_CallTabTitle) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(strings.Calls_CallTabDescription)) + ) + )) - private let titleNode: ASTextNode - private let textNode: ASTextNode - private let iconNode: ASImageNode - private let accentIconNode: ASImageNode - - private let actionNodesSeparator: ASDisplayNode - private let actionNodes: [TextAlertContentActionNode] - private let actionVerticalSeparators: [ASDisplayNode] - - private var validLayout: CGSize? - - override var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled + let alertController = AlertScreen( + sharedContext: sharedContext, + content: content, + actions: [ + .init(title: strings.Common_NotNow), + .init(title: strings.Calls_AddTab, type: .default, action: { + let _ = updateCallListSettingsInteractively(accountManager: sharedContext.accountManager, { + $0.withUpdatedShowTab(true) + }).start() + }) + ] + ) + return alertController +} + +private final class AlertCallSuggestHeaderComponent: Component { + public typealias EnvironmentType = AlertComponentEnvironment + + public init() { } - init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction]) { - self.strings = strings - - self.titleNode = ASTextNode() - self.titleNode.maximumNumberOfLines = 2 - - self.textNode = ASTextNode() - self.textNode.maximumNumberOfLines = 0 - - self.iconNode = ASImageNode() - self.iconNode.displaysAsynchronously = false - self.iconNode.displayWithoutProcessing = true - - self.accentIconNode = ASImageNode() - self.accentIconNode.displaysAsynchronously = false - self.accentIconNode.displayWithoutProcessing = true - - self.actionNodesSeparator = ASDisplayNode() - self.actionNodesSeparator.isLayerBacked = true - - self.actionNodes = actions.map { action -> TextAlertContentActionNode in - return TextAlertContentActionNode(theme: theme, action: action) - } - - var actionVerticalSeparators: [ASDisplayNode] = [] - if actions.count > 1 { - for _ in 0 ..< actions.count - 1 { - let separatorNode = ASDisplayNode() - separatorNode.isLayerBacked = true - actionVerticalSeparators.append(separatorNode) - } - } - self.actionVerticalSeparators = actionVerticalSeparators - - super.init() - - self.addSubnode(self.titleNode) - self.addSubnode(self.textNode) - self.addSubnode(self.iconNode) - self.addSubnode(self.accentIconNode) - - self.addSubnode(self.actionNodesSeparator) - - for actionNode in self.actionNodes { - self.addSubnode(actionNode) - } - - for separatorNode in self.actionVerticalSeparators { - self.addSubnode(separatorNode) - } - - self.updateTheme(theme) + public static func ==(lhs: AlertCallSuggestHeaderComponent, rhs: AlertCallSuggestHeaderComponent) -> Bool { + return true } - override func updateTheme(_ theme: AlertControllerTheme) { - self.titleNode.attributedText = NSAttributedString(string: strings.Calls_CallTabTitle, font: Font.bold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center) - self.textNode.attributedText = NSAttributedString(string: strings.Calls_CallTabDescription, font: Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center) - self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Call List/AlertIcon"), color: theme.controlBorderColor) - self.accentIconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Call List/AlertAccentIcon"), color: theme.accentColor) + public final class View: UIView { + private let image = ComponentView() + private let accentImage = ComponentView() - self.actionNodesSeparator.backgroundColor = theme.separatorColor - for actionNode in self.actionNodes { - actionNode.updateTheme(theme) - } - for separatorNode in self.actionVerticalSeparators { - separatorNode.backgroundColor = theme.separatorColor - } + private var component: AlertCallSuggestHeaderComponent? + private weak var state: EmptyComponentState? - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) - } - } - - override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - var size = size - size.width = min(size.width , 270.0) - - self.validLayout = size - - var origin: CGPoint = CGPoint(x: 0.0, y: 20.0) - - let titleSize = self.titleNode.measure(size) - transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize)) - origin.y += titleSize.height + 13.0 - - var iconSize = CGSize() - if let icon = self.iconNode.image { - iconSize = icon.size - let iconFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - iconSize.width) / 2.0), y: origin.y), size: iconSize) - transition.updateFrame(node: self.iconNode, frame: iconFrame) - transition.updateFrame(node: self.accentIconNode, frame: iconFrame) - origin.y += iconSize.height + 16.0 - } - - let textSize = self.textNode.measure(size) - transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize)) - - let actionButtonHeight: CGFloat = 44.0 - var minActionsWidth: CGFloat = 0.0 - let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) - let actionTitleInsets: CGFloat = 8.0 - - var effectiveActionLayout = TextAlertContentActionLayout.horizontal - for actionNode in self.actionNodes { - let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) - if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 { - effectiveActionLayout = .vertical - } - switch effectiveActionLayout { - case .horizontal: - minActionsWidth += actionTitleSize.width + actionTitleInsets - case .vertical: - minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) - } - } - - let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0) - - var contentWidth = max(titleSize.width, minActionsWidth) - contentWidth = max(contentWidth, 234.0) - - var actionsHeight: CGFloat = 0.0 - switch effectiveActionLayout { - case .horizontal: - actionsHeight = actionButtonHeight - case .vertical: - actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) - } - - let resultWidth = contentWidth + insets.left + insets.right - let resultSize = CGSize(width: resultWidth, height: titleSize.height + iconSize.height + textSize.height + actionsHeight + 34.0 + insets.top + insets.bottom) - - transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - - var actionOffset: CGFloat = 0.0 - let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) - var separatorIndex = -1 - var nodeIndex = 0 - for actionNode in self.actionNodes { - if separatorIndex >= 0 { - let separatorNode = self.actionVerticalSeparators[separatorIndex] - switch effectiveActionLayout { - case .horizontal: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) - case .vertical: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) + func update(component: AlertCallSuggestHeaderComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + self.state = state + + let environment = environment[AlertComponentEnvironment.self] + + let imageSize = self.image.update( + transition: .immediate, + component: AnyComponent( + BundleIconComponent(name: "Call List/AlertIcon", tintColor: environment.theme.actionSheet.primaryTextColor.withMultipliedAlpha(0.2)) + ), + environment: {}, + containerSize: availableSize + ) + let imageFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - imageSize.width) / 2.0), y: 0.0), size: imageSize) + if let imageView = self.image.view { + if imageView.superview == nil { + self.addSubview(imageView) } - } - separatorIndex += 1 - - let currentActionWidth: CGFloat - switch effectiveActionLayout { - case .horizontal: - if nodeIndex == self.actionNodes.count - 1 { - currentActionWidth = resultSize.width - actionOffset - } else { - currentActionWidth = actionWidth - } - case .vertical: - currentActionWidth = resultSize.width + imageView.frame = imageFrame } - let actionNodeFrame: CGRect - switch effectiveActionLayout { - case .horizontal: - actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += currentActionWidth - case .vertical: - actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += actionButtonHeight + let _ = self.accentImage.update( + transition: .immediate, + component: AnyComponent( + BundleIconComponent(name: "Call List/AlertAccentIcon", tintColor: environment.theme.actionSheet.controlAccentColor) + ), + environment: {}, + containerSize: availableSize + ) + let accentImageFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - imageSize.width) / 2.0), y: 0.0), size: imageSize) + if let accentImageView = self.accentImage.view { + if accentImageView.superview == nil { + self.addSubview(accentImageView) + } + accentImageView.frame = accentImageFrame } - transition.updateFrame(node: actionNode, frame: actionNodeFrame) - - nodeIndex += 1 - } - - return resultSize - } -} - -func callSuggestTabController(sharedContext: SharedAccountContext) -> AlertController { - let presentationData = sharedContext.currentPresentationData.with { $0 } - let theme = presentationData.theme - let strings = presentationData.strings - - var dismissImpl: ((Bool) -> Void)? - var contentNode: CallSuggestTabAlertContentNode? - let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_NotNow, action: { - dismissImpl?(true) - }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Calls_AddTab, action: { - dismissImpl?(true) - let _ = updateCallListSettingsInteractively(accountManager: sharedContext.accountManager, { - $0.withUpdatedShowTab(true) - }).start() - })] - - contentNode = CallSuggestTabAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: theme, strings: strings, actions: actions) - - let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode!) - dismissImpl = { [weak controller] animated in - if animated { - controller?.dismissAnimated() - } else { - controller?.dismiss() + return CGSize(width: availableSize.width, height: imageSize.height) } } - return controller + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } } diff --git a/submodules/TelegramCallsUI/Sources/Components/MediaStreamComponent.swift b/submodules/TelegramCallsUI/Sources/Components/MediaStreamComponent.swift index 5edc8d27..3a62b8af 100644 --- a/submodules/TelegramCallsUI/Sources/Components/MediaStreamComponent.swift +++ b/submodules/TelegramCallsUI/Sources/Components/MediaStreamComponent.swift @@ -508,7 +508,7 @@ public final class MediaStreamComponent: CombinedComponent { let title: String = presentationData.strings.LiveStream_EditTitle let text: String = presentationData.strings.LiveStream_EditTitleText - let editController = voiceChatTitleEditController(sharedContext: call.accountContext.sharedContext, account: call.accountContext.account, forceTheme: defaultDarkPresentationTheme, title: title, text: text, placeholder: EnginePeer(chatPeer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), value: initialTitle, maxLength: 40, apply: { [weak call] title in + let editController = voiceChatTitleEditController(context: call.accountContext, forceTheme: defaultDarkPresentationTheme, title: title, text: text, placeholder: EnginePeer(chatPeer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), value: initialTitle, maxLength: 40, apply: { [weak call] title in guard let call = call else { return } @@ -574,7 +574,7 @@ public final class MediaStreamComponent: CombinedComponent { title = presentationData.strings.LiveStream_StartRecordingTitle text = presentationData.strings.LiveStream_StartRecordingTextVideo - let editController = voiceChatTitleEditController(sharedContext: call.accountContext.sharedContext, account: call.accountContext.account, forceTheme: defaultDarkPresentationTheme, title: title, text: text, placeholder: placeholder, value: nil, maxLength: 40, apply: { [weak call, weak controller] title in + let editController = voiceChatTitleEditController(context: call.accountContext, forceTheme: defaultDarkPresentationTheme, title: title, text: text, placeholder: placeholder, value: nil, maxLength: 40, apply: { [weak call, weak controller] title in guard let call = call, let controller = controller else { return } diff --git a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift index fc1fac25..4cb912c6 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift @@ -10,6 +10,7 @@ import TelegramVoip import TelegramAudio import TelegramUIPreferences import TelegramPresentationData +import PresentationDataUtils import DeviceAccess import UniversalMediaPlayer import AccountContext @@ -2134,12 +2135,12 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } if case .anonymousNotAllowed = error { let presentationData = self.accountContext.sharedContext.currentPresentationData.with { $0 } - self.accountContext.sharedContext.mainWindow?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: self.isChannel ? presentationData.strings.LiveStream_AnonymousDisabledAlertText : presentationData.strings.VoiceChat_AnonymousDisabledAlertText, actions: [ + self.accountContext.sharedContext.mainWindow?.present(textAlertController(context: self.accountContext, title: nil, text: self.isChannel ? presentationData.strings.LiveStream_AnonymousDisabledAlertText : presentationData.strings.VoiceChat_AnonymousDisabledAlertText, actions: [ TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {}) ]), on: .root, blockInteraction: false, completion: {}) } else if case .tooManyParticipants = error { let presentationData = self.accountContext.sharedContext.currentPresentationData.with { $0 } - self.accountContext.sharedContext.mainWindow?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: self.isChannel ? presentationData.strings.LiveStream_ChatFullAlertText : presentationData.strings.VoiceChat_ChatFullAlertText, actions: [ + self.accountContext.sharedContext.mainWindow?.present(textAlertController(context: self.accountContext, title: nil, text: self.isChannel ? presentationData.strings.LiveStream_ChatFullAlertText : presentationData.strings.VoiceChat_ChatFullAlertText, actions: [ TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {}) ]), on: .root, blockInteraction: false, completion: {}) } else if case .invalidJoinAsPeer = error { @@ -3805,7 +3806,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { break } - self.accountContext.sharedContext.mainWindow?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: errorText, actions: [ + self.accountContext.sharedContext.mainWindow?.present(textAlertController(context: self.accountContext, title: nil, text: errorText, actions: [ TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {}) ]), on: .root, blockInteraction: false, completion: {}) } diff --git a/submodules/TelegramCallsUI/Sources/VideoChatScheduledInfoComponent.swift b/submodules/TelegramCallsUI/Sources/VideoChatScheduledInfoComponent.swift index c9ae7d1d..1143f2ea 100644 --- a/submodules/TelegramCallsUI/Sources/VideoChatScheduledInfoComponent.swift +++ b/submodules/TelegramCallsUI/Sources/VideoChatScheduledInfoComponent.swift @@ -13,6 +13,22 @@ private let pink = UIColor(rgb: 0xef436c) private let latePurple = UIColor(rgb: 0x974aa9) private let latePink = UIColor(rgb: 0xf0436c) +private func textForTimeout(value: Int32) -> String { + if value < 3600 { + let minutes = value / 60 + let seconds = value % 60 + let secondsPadding = seconds < 10 ? "0" : "" + return "\(minutes):\(secondsPadding)\(seconds)" + } else { + let hours = value / 3600 + let minutes = (value % 3600) / 60 + let minutesPadding = minutes < 10 ? "0" : "" + let seconds = value % 60 + let secondsPadding = seconds < 10 ? "0" : "" + return "\(hours):\(minutesPadding)\(minutes):\(secondsPadding)\(seconds)" + } +} + final class VideoChatScheduledInfoComponent: Component { let timestamp: Int32 let strings: PresentationStrings diff --git a/submodules/TelegramCallsUI/Sources/VideoChatScreen.swift b/submodules/TelegramCallsUI/Sources/VideoChatScreen.swift index b5146f66..fcf96a12 100644 --- a/submodules/TelegramCallsUI/Sources/VideoChatScreen.swift +++ b/submodules/TelegramCallsUI/Sources/VideoChatScreen.swift @@ -691,7 +691,7 @@ final class VideoChatScreenComponent: Component { text = environment.strings.VoiceChat_EditTitleText } - let controller = voiceChatTitleEditController(sharedContext: groupCall.accountContext.sharedContext, account: groupCall.accountContext.account, forceTheme: environment.theme, title: title, text: text, placeholder: EnginePeer(chatPeer).displayTitle(strings: environment.strings, displayOrder: groupCall.accountContext.sharedContext.currentPresentationData.with({ $0 }).nameDisplayOrder), value: initialTitle, maxLength: 40, apply: { [weak self] title in + let controller = voiceChatTitleEditController(context: groupCall.accountContext, forceTheme: environment.theme, title: title, text: text, placeholder: EnginePeer(chatPeer).displayTitle(strings: environment.strings, displayOrder: groupCall.accountContext.sharedContext.currentPresentationData.with({ $0 }).nameDisplayOrder), value: initialTitle, maxLength: 40, apply: { [weak self] title in guard let self, let environment = self.environment, case let .group(groupCall) = self.currentCall else { return } @@ -3448,7 +3448,6 @@ final class VideoChatScreenComponent: Component { pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, - importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, diff --git a/submodules/TelegramCallsUI/Sources/VideoChatScreenMoreMenu.swift b/submodules/TelegramCallsUI/Sources/VideoChatScreenMoreMenu.swift index e77e72bf..fed8bd66 100644 --- a/submodules/TelegramCallsUI/Sources/VideoChatScreenMoreMenu.swift +++ b/submodules/TelegramCallsUI/Sources/VideoChatScreenMoreMenu.swift @@ -469,7 +469,7 @@ extension VideoChatScreenComponent.View { } } - let controller = voiceChatTitleEditController(sharedContext: currentCall.accountContext.sharedContext, account: currentCall.accountContext.account, forceTheme: environment.theme, title: title, text: text, placeholder: placeholder, value: nil, maxLength: 40, apply: { [weak self] title in + let controller = voiceChatTitleEditController(context: currentCall.accountContext, forceTheme: environment.theme, title: title, text: text, placeholder: placeholder, value: nil, maxLength: 40, apply: { [weak self] title in guard let self, let environment = self.environment, case let .group(groupCall) = self.currentCall, let peer = self.peer, let title else { return } diff --git a/submodules/TelegramCallsUI/Sources/VideoChatScreenParticipantContextMenu.swift b/submodules/TelegramCallsUI/Sources/VideoChatScreenParticipantContextMenu.swift index dff33a3b..3b031887 100644 --- a/submodules/TelegramCallsUI/Sources/VideoChatScreenParticipantContextMenu.swift +++ b/submodules/TelegramCallsUI/Sources/VideoChatScreenParticipantContextMenu.swift @@ -117,7 +117,7 @@ extension VideoChatScreenComponent.View { } else { maxBioLength = 100 } - let controller = voiceChatTitleEditController(sharedContext: currentCall.accountContext.sharedContext, account: currentCall.accountContext.account, forceTheme: environment.theme, title: environment.strings.VoiceChat_EditBioTitle, text: environment.strings.VoiceChat_EditBioText, placeholder: environment.strings.VoiceChat_EditBioPlaceholder, doneButtonTitle: environment.strings.VoiceChat_EditBioSave, value: participant.about, maxLength: maxBioLength, apply: { [weak self] bio in + let controller = voiceChatTitleEditController(context: currentCall.accountContext, forceTheme: environment.theme, title: environment.strings.VoiceChat_EditBioTitle, text: environment.strings.VoiceChat_EditBioText, placeholder: environment.strings.VoiceChat_EditBioPlaceholder, doneButtonTitle: environment.strings.VoiceChat_EditBioSave, value: participant.about, maxLength: maxBioLength, apply: { [weak self] bio in guard let self, let environment = self.environment, let currentCall = self.currentCall, let bio else { return } @@ -149,7 +149,7 @@ extension VideoChatScreenComponent.View { guard let self, let environment = self.environment, let currentCall = self.currentCall else { return } - let controller = voiceChatUserNameController(sharedContext: currentCall.accountContext.sharedContext, account: currentCall.accountContext.account, forceTheme: environment.theme, title: environment.strings.VoiceChat_ChangeNameTitle, firstNamePlaceholder: environment.strings.UserInfo_FirstNamePlaceholder, lastNamePlaceholder: environment.strings.UserInfo_LastNamePlaceholder, doneButtonTitle: environment.strings.VoiceChat_EditBioSave, firstName: peer.firstName, lastName: peer.lastName, maxLength: 128, apply: { [weak self] firstAndLastName in + let controller = voiceChatUserNameController(context: currentCall.accountContext, forceTheme: environment.theme, title: environment.strings.VoiceChat_ChangeNameTitle, firstNamePlaceholder: environment.strings.UserInfo_FirstNamePlaceholder, lastNamePlaceholder: environment.strings.UserInfo_LastNamePlaceholder, doneButtonTitle: environment.strings.VoiceChat_EditBioSave, firstName: peer.firstName, lastName: peer.lastName, maxLength: 128, apply: { [weak self] firstAndLastName in guard let self, let environment = self.environment, let currentCall = self.currentCall, let (firstName, lastName) = firstAndLastName else { return } diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatActionItem.swift b/submodules/TelegramCallsUI/Sources/VoiceChatActionItem.swift index 320041de..c2822f5f 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatActionItem.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatActionItem.swift @@ -129,7 +129,7 @@ class VoiceChatActionItemNode: ListViewItemNode { self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.highlightContainerNode.addSubnode(self.highlightedBackgroundNode) diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatFullscreenParticipantItem.swift b/submodules/TelegramCallsUI/Sources/VoiceChatFullscreenParticipantItem.swift index 58367150..fc623650 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatFullscreenParticipantItem.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatFullscreenParticipantItem.swift @@ -259,7 +259,7 @@ class VoiceChatFullscreenParticipantItemNode: ItemListRevealOptionsItemNode { self.actionContainerNode = ASDisplayNode() self.actionButtonNode = HighlightableButtonNode() - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.isAccessibilityElement = true diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatMicrophoneNode.swift b/submodules/TelegramCallsUI/Sources/VoiceChatMicrophoneNode.swift index c644942d..b296906e 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatMicrophoneNode.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatMicrophoneNode.swift @@ -23,15 +23,15 @@ private final class VoiceChatMicrophoneNodeDrawingState: NSObject { } } -final class VoiceChatMicrophoneNode: ASDisplayNode { - class State: Equatable { - let muted: Bool - let color: UIColor - let filled: Bool - let shadowColor: UIColor? - let shadowBlur: CGFloat +public final class VoiceChatMicrophoneNode: ASDisplayNode { + public class State: Equatable { + public let muted: Bool + public let color: UIColor + public let filled: Bool + public let shadowColor: UIColor? + public let shadowBlur: CGFloat - init(muted: Bool, filled: Bool, color: UIColor, shadowColor: UIColor? = nil, shadowBlur: CGFloat = 0.0) { + public init(muted: Bool, filled: Bool, color: UIColor, shadowColor: UIColor? = nil, shadowBlur: CGFloat = 0.0) { self.muted = muted self.filled = filled self.color = color @@ -39,7 +39,7 @@ final class VoiceChatMicrophoneNode: ASDisplayNode { self.shadowBlur = shadowBlur } - static func ==(lhs: State, rhs: State) -> Bool { + public static func ==(lhs: State, rhs: State) -> Bool { if lhs.muted != rhs.muted { return false } @@ -77,13 +77,13 @@ final class VoiceChatMicrophoneNode: ASDisplayNode { private var state: State = State(muted: false, filled: false, color: .black) private var transitionContext: TransitionContext? - override init() { + override public init() { super.init() self.isOpaque = false } - func update(state: State, animated: Bool) { + public func update(state: State, animated: Bool) { var animated = animated if !self.hasState { self.hasState = true diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatParticipantItem.swift b/submodules/TelegramCallsUI/Sources/VoiceChatParticipantItem.swift index 81722b4d..820b98cd 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatParticipantItem.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatParticipantItem.swift @@ -344,7 +344,7 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode { self.highlightedBackgroundNode = ASDisplayNode() - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.isAccessibilityElement = true diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatTileGridNode.swift b/submodules/TelegramCallsUI/Sources/VoiceChatTileGridNode.swift index 1b8b43df..037905bd 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatTileGridNode.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatTileGridNode.swift @@ -257,7 +257,7 @@ final class VoiceChatTilesGridItemNode: ListViewItemNode { self.limitLabel = TextNode() self.limitLabel.alpha = 0.0 - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.backgroundNode) self.addSubnode(self.cornersNode) diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatTimerNode.swift b/submodules/TelegramCallsUI/Sources/VoiceChatTimerNode.swift index 3fb3b29b..6f4abcd3 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatTimerNode.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatTimerNode.swift @@ -12,6 +12,22 @@ private let pink = UIColor(rgb: 0xef436c) private let latePurple = UIColor(rgb: 0x974aa9) private let latePink = UIColor(rgb: 0xf0436c) +private func textForTimeout(value: Int32) -> String { + if value < 3600 { + let minutes = value / 60 + let seconds = value % 60 + let secondsPadding = seconds < 10 ? "0" : "" + return "\(minutes):\(secondsPadding)\(seconds)" + } else { + let hours = value / 3600 + let minutes = (value % 3600) / 60 + let minutesPadding = minutes < 10 ? "0" : "" + let seconds = value % 60 + let secondsPadding = seconds < 10 ? "0" : "" + return "\(hours):\(minutesPadding)\(minutes):\(secondsPadding)\(seconds)" + } +} + final class VoiceChatTimerNode: ASDisplayNode { private let strings: PresentationStrings private let dateTimeFormat: PresentationDateTimeFormat diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatTitleEditController.swift b/submodules/TelegramCallsUI/Sources/VoiceChatTitleEditController.swift index 1ba276b3..505fb7d2 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatTitleEditController.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatTitleEditController.swift @@ -7,767 +7,179 @@ import TelegramCore import TelegramPresentationData import AccountContext import UrlEscaping +import ComponentFlow +import AlertComponent +import AlertInputFieldComponent -private final class VoiceChatTitleEditInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegate { - private var theme: PresentationTheme - private let backgroundNode: ASImageNode - private let textInputNode: EditableTextNode - private let placeholderNode: ASTextNode - private let clearButton: HighlightableButtonNode - - var updateHeight: (() -> Void)? - var complete: (() -> Void)? - var textChanged: ((String) -> Void)? - - private let backgroundInsets = UIEdgeInsets(top: 8.0, left: 16.0, bottom: 15.0, right: 16.0) - private let inputInsets = UIEdgeInsets(top: 5.0, left: 12.0, bottom: 5.0, right: 12.0) - - var text: String { - get { - return self.textInputNode.attributedText?.string ?? "" - } - set { - self.textInputNode.attributedText = NSAttributedString(string: newValue, font: Font.regular(17.0), textColor: self.theme.actionSheet.inputTextColor) - self.placeholderNode.isHidden = !newValue.isEmpty - if self.textInputNode.isFirstResponder() { - self.clearButton.isHidden = newValue.isEmpty - } else { - self.clearButton.isHidden = true - } - } - } - - var placeholder: String = "" { - didSet { - self.placeholderNode.attributedText = NSAttributedString(string: self.placeholder, font: Font.regular(17.0), textColor: self.theme.actionSheet.inputPlaceholderColor) - } - } - - private let maxLength: Int - - init(theme: PresentationTheme, placeholder: String, maxLength: Int, returnKeyType: UIReturnKeyType = .done) { - self.theme = theme - self.maxLength = maxLength - - self.backgroundNode = ASImageNode() - self.backgroundNode.isLayerBacked = true - self.backgroundNode.displaysAsynchronously = false - self.backgroundNode.displayWithoutProcessing = true - self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 12.0, color: theme.actionSheet.inputHollowBackgroundColor, strokeColor: theme.actionSheet.inputBorderColor, strokeWidth: 1.0) - - self.textInputNode = EditableTextNode() - self.textInputNode.typingAttributes = [NSAttributedString.Key.font.rawValue: Font.regular(17.0), NSAttributedString.Key.foregroundColor.rawValue: theme.actionSheet.inputTextColor] - self.textInputNode.clipsToBounds = true - self.textInputNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0) - self.textInputNode.textContainerInset = UIEdgeInsets(top: self.inputInsets.top, left: 0.0, bottom: self.inputInsets.bottom, right: 0.0) - self.textInputNode.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance - self.textInputNode.keyboardType = .default - self.textInputNode.autocapitalizationType = .sentences - self.textInputNode.returnKeyType = returnKeyType - self.textInputNode.autocorrectionType = .default - self.textInputNode.tintColor = theme.actionSheet.controlAccentColor - - self.placeholderNode = ASTextNode() - self.placeholderNode.isUserInteractionEnabled = false - self.placeholderNode.displaysAsynchronously = false - self.placeholderNode.attributedText = NSAttributedString(string: placeholder, font: Font.regular(17.0), textColor: self.theme.actionSheet.inputPlaceholderColor) - - self.clearButton = HighlightableButtonNode() - self.clearButton.imageNode.displaysAsynchronously = false - self.clearButton.imageNode.displayWithoutProcessing = true - self.clearButton.displaysAsynchronously = false - self.clearButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: theme.actionSheet.inputClearButtonColor), for: []) - self.clearButton.isHidden = true - - super.init() - - self.textInputNode.delegate = self - - self.addSubnode(self.backgroundNode) - self.addSubnode(self.textInputNode) - self.addSubnode(self.placeholderNode) - self.addSubnode(self.clearButton) - - self.clearButton.addTarget(self, action: #selector(self.clearPressed), forControlEvents: .touchUpInside) - } - - func updateTheme(_ theme: PresentationTheme) { - self.theme = theme - - self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 12.0, color: self.theme.actionSheet.inputHollowBackgroundColor, strokeColor: self.theme.actionSheet.inputBorderColor, strokeWidth: 1.0) - self.textInputNode.keyboardAppearance = self.theme.rootController.keyboardColor.keyboardAppearance - self.placeholderNode.attributedText = NSAttributedString(string: self.placeholderNode.attributedText?.string ?? "", font: Font.regular(17.0), textColor: self.theme.actionSheet.inputPlaceholderColor) - self.textInputNode.tintColor = self.theme.actionSheet.controlAccentColor - self.clearButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: theme.actionSheet.inputClearButtonColor), for: []) - } - - func updateLayout(width: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat { - let backgroundInsets = self.backgroundInsets - let inputInsets = self.inputInsets - - let textFieldHeight = self.calculateTextFieldMetrics(width: width) - let panelHeight = textFieldHeight + backgroundInsets.top + backgroundInsets.bottom - - let backgroundFrame = CGRect(origin: CGPoint(x: backgroundInsets.left, y: backgroundInsets.top), size: CGSize(width: width - backgroundInsets.left - backgroundInsets.right, height: panelHeight - backgroundInsets.top - backgroundInsets.bottom)) - transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame) - - let placeholderSize = self.placeholderNode.measure(backgroundFrame.size) - transition.updateFrame(node: self.placeholderNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + inputInsets.left, y: backgroundFrame.minY + floor((backgroundFrame.size.height - placeholderSize.height) / 2.0)), size: placeholderSize)) - - transition.updateFrame(node: self.textInputNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + inputInsets.left, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.size.width - inputInsets.left - inputInsets.right - 20.0, height: backgroundFrame.size.height))) - - if let image = self.clearButton.image(for: []) { - transition.updateFrame(node: self.clearButton, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX - 8.0 - image.size.width, y: backgroundFrame.minY + floor((backgroundFrame.size.height - image.size.height) / 2.0)), size: image.size)) - } - - return panelHeight - } - - func activateInput() { - self.textInputNode.becomeFirstResponder() - } - - func deactivateInput() { - self.textInputNode.resignFirstResponder() - } - - @objc func editableTextNodeDidUpdateText(_ editableTextNode: ASEditableTextNode) { - self.updateTextNodeText(animated: true) - self.textChanged?(editableTextNode.textView.text) - self.placeholderNode.isHidden = !(editableTextNode.textView.text ?? "").isEmpty - self.clearButton.isHidden = !self.placeholderNode.isHidden - } - - func editableTextNodeDidBeginEditing(_ editableTextNode: ASEditableTextNode) { - self.clearButton.isHidden = (editableTextNode.textView.text ?? "").isEmpty - } - - func editableTextNodeDidFinishEditing(_ editableTextNode: ASEditableTextNode) { - self.clearButton.isHidden = true - } - - func editableTextNode(_ editableTextNode: ASEditableTextNode, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { - let updatedText = (editableTextNode.textView.text as NSString).replacingCharacters(in: range, with: text) - if updatedText.count > maxLength { - self.textInputNode.layer.addShakeAnimation() - return false - } - if text == "\n" { - self.complete?() - return false - } - return true - } - - private func calculateTextFieldMetrics(width: CGFloat) -> CGFloat { - let backgroundInsets = self.backgroundInsets - let inputInsets = self.inputInsets - - let unboundTextFieldHeight = max(33.0, ceil(self.textInputNode.measure(CGSize(width: width - backgroundInsets.left - backgroundInsets.right - inputInsets.left - inputInsets.right - 20.0, height: CGFloat.greatestFiniteMagnitude)).height)) - - return min(61.0, max(33.0, unboundTextFieldHeight)) - } - - private func updateTextNodeText(animated: Bool) { - let backgroundInsets = self.backgroundInsets - - let textFieldHeight = self.calculateTextFieldMetrics(width: self.bounds.size.width) - - let panelHeight = textFieldHeight + backgroundInsets.top + backgroundInsets.bottom - if !self.bounds.size.height.isEqual(to: panelHeight) { - self.updateHeight?() - } - } - - @objc func clearPressed() { - self.placeholderNode.isHidden = false - self.clearButton.isHidden = true - - self.textInputNode.attributedText = nil - self.updateHeight?() - } -} - -private final class VoiceChatTitleEditAlertContentNode: AlertContentNode { - private let strings: PresentationStrings - private let title: String - private let text: String - - private let titleNode: ASTextNode - private let textNode: ASTextNode - let inputFieldNode: VoiceChatTitleEditInputFieldNode - - private let actionNodesSeparator: ASDisplayNode - private let actionNodes: [TextAlertContentActionNode] - private let actionVerticalSeparators: [ASDisplayNode] - - private let disposable = MetaDisposable() - - private var validLayout: CGSize? - - private let hapticFeedback = HapticFeedback() - - var complete: (() -> Void)? { - didSet { - self.inputFieldNode.complete = self.complete - } - } - - override var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled - } - - init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction], title: String, text: String, placeholder: String, value: String?, maxLength: Int) { - self.strings = strings - self.title = title - self.text = text - - self.titleNode = ASTextNode() - self.titleNode.maximumNumberOfLines = 2 - self.textNode = ASTextNode() - self.textNode.maximumNumberOfLines = 8 - - self.inputFieldNode = VoiceChatTitleEditInputFieldNode(theme: ptheme, placeholder: placeholder, maxLength: maxLength) - self.inputFieldNode.text = value ?? "" - - self.actionNodesSeparator = ASDisplayNode() - self.actionNodesSeparator.isLayerBacked = true - - self.actionNodes = actions.map { action -> TextAlertContentActionNode in - return TextAlertContentActionNode(theme: theme, action: action) - } - - var actionVerticalSeparators: [ASDisplayNode] = [] - if actions.count > 1 { - for _ in 0 ..< actions.count - 1 { - let separatorNode = ASDisplayNode() - separatorNode.isLayerBacked = true - actionVerticalSeparators.append(separatorNode) - } - } - self.actionVerticalSeparators = actionVerticalSeparators - - super.init() - - self.addSubnode(self.titleNode) - self.addSubnode(self.textNode) - - self.addSubnode(self.inputFieldNode) - - self.addSubnode(self.actionNodesSeparator) - - for actionNode in self.actionNodes { - self.addSubnode(actionNode) - } - - for separatorNode in self.actionVerticalSeparators { - self.addSubnode(separatorNode) - } - - self.inputFieldNode.updateHeight = { [weak self] in - if let strongSelf = self { - if let _ = strongSelf.validLayout { - strongSelf.requestLayout?(.animated(duration: 0.15, curve: .spring)) - } - } - } - - self.updateTheme(theme) - } - - deinit { - self.disposable.dispose() - } - - var value: String { - return self.inputFieldNode.text - } - - override func updateTheme(_ theme: AlertControllerTheme) { - self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.bold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center) - self.textNode.attributedText = NSAttributedString(string: self.text, font: Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center) - - self.actionNodesSeparator.backgroundColor = theme.separatorColor - for actionNode in self.actionNodes { - actionNode.updateTheme(theme) - } - for separatorNode in self.actionVerticalSeparators { - separatorNode.backgroundColor = theme.separatorColor - } - - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) - } - } - - override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - var size = size - size.width = min(size.width, 270.0) - let measureSize = CGSize(width: size.width - 16.0 * 2.0, height: CGFloat.greatestFiniteMagnitude) - - let hadValidLayout = self.validLayout != nil - - self.validLayout = size - - var origin: CGPoint = CGPoint(x: 0.0, y: 20.0) - let spacing: CGFloat = 5.0 - - let titleSize = self.titleNode.measure(measureSize) - transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize)) - origin.y += titleSize.height + 4.0 - - let textSize = self.textNode.measure(measureSize) - transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize)) - origin.y += textSize.height + 6.0 + spacing - - let actionButtonHeight: CGFloat = 44.0 - var minActionsWidth: CGFloat = 0.0 - let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) - let actionTitleInsets: CGFloat = 8.0 - - var effectiveActionLayout = TextAlertContentActionLayout.horizontal - for actionNode in self.actionNodes { - let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) - if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 { - effectiveActionLayout = .vertical - } - switch effectiveActionLayout { - case .horizontal: - minActionsWidth += actionTitleSize.width + actionTitleInsets - case .vertical: - minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) - } - } - - let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 9.0, right: 18.0) - - var contentWidth = max(titleSize.width, minActionsWidth) - contentWidth = max(contentWidth, 234.0) - - var actionsHeight: CGFloat = 0.0 - switch effectiveActionLayout { - case .horizontal: - actionsHeight = actionButtonHeight - case .vertical: - actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) - } - - let resultWidth = contentWidth + insets.left + insets.right - - let inputFieldWidth = resultWidth - let inputFieldHeight = self.inputFieldNode.updateLayout(width: inputFieldWidth, transition: transition) - let inputHeight = inputFieldHeight - transition.updateFrame(node: self.inputFieldNode, frame: CGRect(x: 0.0, y: origin.y, width: resultWidth, height: inputFieldHeight)) - transition.updateAlpha(node: self.inputFieldNode, alpha: inputHeight > 0.0 ? 1.0 : 0.0) - - let resultSize = CGSize(width: resultWidth, height: titleSize.height + textSize.height + spacing + inputHeight + actionsHeight + insets.top + insets.bottom) - - transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - - var actionOffset: CGFloat = 0.0 - let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) - var separatorIndex = -1 - var nodeIndex = 0 - for actionNode in self.actionNodes { - if separatorIndex >= 0 { - let separatorNode = self.actionVerticalSeparators[separatorIndex] - switch effectiveActionLayout { - case .horizontal: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) - case .vertical: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - } - } - separatorIndex += 1 - - let currentActionWidth: CGFloat - switch effectiveActionLayout { - case .horizontal: - if nodeIndex == self.actionNodes.count - 1 { - currentActionWidth = resultSize.width - actionOffset - } else { - currentActionWidth = actionWidth - } - case .vertical: - currentActionWidth = resultSize.width - } - - let actionNodeFrame: CGRect - switch effectiveActionLayout { - case .horizontal: - actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += currentActionWidth - case .vertical: - actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += actionButtonHeight - } - - transition.updateFrame(node: actionNode, frame: actionNodeFrame) - - nodeIndex += 1 - } - - if !hadValidLayout { - self.inputFieldNode.activateInput() - } - - return resultSize - } - - func animateError() { - self.inputFieldNode.layer.addShakeAnimation() - self.hapticFeedback.error() - } -} - -func voiceChatTitleEditController(sharedContext: SharedAccountContext, account: Account, forceTheme: PresentationTheme?, title: String, text: String, placeholder: String, doneButtonTitle: String? = nil, value: String?, maxLength: Int, apply: @escaping (String?) -> Void) -> AlertController { - var presentationData = sharedContext.currentPresentationData.with { $0 } - if let forceTheme = forceTheme { +func voiceChatTitleEditController( + context: AccountContext, + forceTheme: PresentationTheme?, + title: String, + text: String, + placeholder: String, + doneButtonTitle: String? = nil, + value: String?, + maxLength: Int, + apply: @escaping (String?) -> Void +) -> ViewController { + var presentationData = context.sharedContext.currentPresentationData.with { $0 } + if let forceTheme { presentationData = presentationData.withUpdated(theme: forceTheme) } - - var dismissImpl: ((Bool) -> Void)? + let strings = presentationData.strings + + let inputState = AlertInputFieldComponent.ExternalState() + + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: title) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(text)) + ) + )) + var applyImpl: (() -> Void)? - - let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - dismissImpl?(true) - }), TextAlertAction(type: .defaultAction, title: doneButtonTitle ?? presentationData.strings.Common_Done, action: { - applyImpl?() - })] - - let contentNode = VoiceChatTitleEditAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: actions, title: title, text: text, placeholder: placeholder, value: value, maxLength: maxLength) - contentNode.complete = { - applyImpl?() - } - applyImpl = { [weak contentNode] in - guard let contentNode = contentNode else { - return - } - dismissImpl?(true) - + content.append(AnyComponentWithIdentity( + id: "input", + component: AnyComponent( + AlertInputFieldComponent( + context: context, + initialValue: value, + placeholder: placeholder, + characterLimit: maxLength, + hasClearButton: true, + isInitiallyFocused: true, + externalState: inputState, + returnKeyAction: { + applyImpl?() + } + ) + ) + )) + + let alertController = AlertScreen( + configuration: AlertScreen.Configuration(allowInputInset: true), + content: content, + actions: [ + .init(title: strings.Common_Cancel), + .init(title: doneButtonTitle ?? strings.Common_Done, type: .default, action: { + applyImpl?() + }) + ], + updatedPresentationData: (presentationData, .single(presentationData)) + ) + applyImpl = { let previousValue = value ?? "" - let newValue = contentNode.value.trimmingCharacters(in: .whitespacesAndNewlines) + let newValue = inputState.value.trimmingCharacters(in: .whitespacesAndNewlines) apply(previousValue != newValue || value == nil ? newValue : nil) } - - let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode) - let presentationDataDisposable = sharedContext.presentationData.start(next: { [weak controller, weak contentNode] presentationData in - var presentationData = presentationData - if let forceTheme = forceTheme { - presentationData = presentationData.withUpdated(theme: forceTheme) - } - controller?.theme = AlertControllerTheme(presentationData: presentationData) - contentNode?.inputFieldNode.updateTheme(presentationData.theme) - }) - controller.dismissed = { _ in - presentationDataDisposable.dispose() - } - dismissImpl = { [weak controller, weak contentNode] animated in - contentNode?.inputFieldNode.deactivateInput() - if animated { - controller?.dismissAnimated() - } else { - controller?.dismiss() - } - } - return controller + return alertController } -private final class VoiceChatUserNameEditAlertContentNode: AlertContentNode { - private let strings: PresentationStrings - private let title: String - - private let titleNode: ASTextNode - let firstNameInputFieldNode: VoiceChatTitleEditInputFieldNode - let lastNameInputFieldNode: VoiceChatTitleEditInputFieldNode - - private let actionNodesSeparator: ASDisplayNode - private let actionNodes: [TextAlertContentActionNode] - private let actionVerticalSeparators: [ASDisplayNode] - - private let disposable = MetaDisposable() - - private var validLayout: CGSize? - - private let hapticFeedback = HapticFeedback() - - var complete: (() -> Void)? { - didSet { - self.lastNameInputFieldNode.complete = self.complete - } - } - - override var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled - } - - init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction], title: String, firstNamePlaceholder: String, lastNamePlaceholder: String, firstNameValue: String?, lastNameValue: String?, maxLength: Int) { - self.strings = strings - self.title = title - - self.titleNode = ASTextNode() - self.titleNode.maximumNumberOfLines = 2 - - self.firstNameInputFieldNode = VoiceChatTitleEditInputFieldNode(theme: ptheme, placeholder: firstNamePlaceholder, maxLength: maxLength, returnKeyType: .next) - self.firstNameInputFieldNode.text = firstNameValue ?? "" - - self.lastNameInputFieldNode = VoiceChatTitleEditInputFieldNode(theme: ptheme, placeholder: lastNamePlaceholder, maxLength: maxLength) - self.lastNameInputFieldNode.text = lastNameValue ?? "" - - self.actionNodesSeparator = ASDisplayNode() - self.actionNodesSeparator.isLayerBacked = true - - self.actionNodes = actions.map { action -> TextAlertContentActionNode in - return TextAlertContentActionNode(theme: theme, action: action) - } - - var actionVerticalSeparators: [ASDisplayNode] = [] - if actions.count > 1 { - for _ in 0 ..< actions.count - 1 { - let separatorNode = ASDisplayNode() - separatorNode.isLayerBacked = true - actionVerticalSeparators.append(separatorNode) - } - } - self.actionVerticalSeparators = actionVerticalSeparators - - super.init() - - self.addSubnode(self.titleNode) - - self.addSubnode(self.firstNameInputFieldNode) - self.addSubnode(self.lastNameInputFieldNode) - - self.addSubnode(self.actionNodesSeparator) - - for actionNode in self.actionNodes { - self.addSubnode(actionNode) - } - - for separatorNode in self.actionVerticalSeparators { - self.addSubnode(separatorNode) - } - - self.updateTheme(theme) - - self.firstNameInputFieldNode.complete = { [weak self] in - self?.lastNameInputFieldNode.activateInput() - } - } - - deinit { - self.disposable.dispose() - } - - var firstName: String { - return self.firstNameInputFieldNode.text - } - - var lastName: String { - return self.lastNameInputFieldNode.text - } - - override func updateTheme(_ theme: AlertControllerTheme) { - self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.bold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center) - - self.actionNodesSeparator.backgroundColor = theme.separatorColor - for actionNode in self.actionNodes { - actionNode.updateTheme(theme) - } - for separatorNode in self.actionVerticalSeparators { - separatorNode.backgroundColor = theme.separatorColor - } - - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) - } - } - - override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - var size = size - size.width = min(size.width, 270.0) - let measureSize = CGSize(width: size.width - 16.0 * 2.0, height: CGFloat.greatestFiniteMagnitude) - - let hadValidLayout = self.validLayout != nil - - self.validLayout = size - - var origin: CGPoint = CGPoint(x: 0.0, y: 20.0) - let spacing: CGFloat = 0.0 - - let titleSize = self.titleNode.measure(measureSize) - transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize)) - origin.y += titleSize.height + 4.0 - - let actionButtonHeight: CGFloat = 44.0 - var minActionsWidth: CGFloat = 0.0 - let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) - let actionTitleInsets: CGFloat = 8.0 - - var effectiveActionLayout = TextAlertContentActionLayout.horizontal - for actionNode in self.actionNodes { - let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) - if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 { - effectiveActionLayout = .vertical - } - switch effectiveActionLayout { - case .horizontal: - minActionsWidth += actionTitleSize.width + actionTitleInsets - case .vertical: - minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) - } - } - - let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 9.0, right: 18.0) - - var contentWidth = max(titleSize.width, minActionsWidth) - contentWidth = max(contentWidth, 234.0) - - var actionsHeight: CGFloat = 0.0 - switch effectiveActionLayout { - case .horizontal: - actionsHeight = actionButtonHeight - case .vertical: - actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) - } - - let resultWidth = contentWidth + insets.left + insets.right - - let inputFieldWidth = resultWidth - let firstInputFieldHeight = self.firstNameInputFieldNode.updateLayout(width: inputFieldWidth, transition: transition) - transition.updateFrame(node: self.firstNameInputFieldNode, frame: CGRect(x: 0.0, y: origin.y, width: resultWidth, height: firstInputFieldHeight)) - - origin.y += firstInputFieldHeight + spacing - - let lastInputFieldHeight = self.lastNameInputFieldNode.updateLayout(width: inputFieldWidth, transition: transition) - transition.updateFrame(node: self.lastNameInputFieldNode, frame: CGRect(x: 0.0, y: origin.y, width: resultWidth, height: lastInputFieldHeight)) - - let resultSize = CGSize(width: resultWidth, height: titleSize.height + firstInputFieldHeight + spacing + lastInputFieldHeight + actionsHeight + insets.top + insets.bottom) - - transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - - var actionOffset: CGFloat = 0.0 - let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) - var separatorIndex = -1 - var nodeIndex = 0 - for actionNode in self.actionNodes { - if separatorIndex >= 0 { - let separatorNode = self.actionVerticalSeparators[separatorIndex] - switch effectiveActionLayout { - case .horizontal: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) - case .vertical: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - } - } - separatorIndex += 1 - - let currentActionWidth: CGFloat - switch effectiveActionLayout { - case .horizontal: - if nodeIndex == self.actionNodes.count - 1 { - currentActionWidth = resultSize.width - actionOffset - } else { - currentActionWidth = actionWidth - } - case .vertical: - currentActionWidth = resultSize.width - } - - let actionNodeFrame: CGRect - switch effectiveActionLayout { - case .horizontal: - actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += currentActionWidth - case .vertical: - actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += actionButtonHeight - } - - transition.updateFrame(node: actionNode, frame: actionNodeFrame) - - nodeIndex += 1 - } - - if !hadValidLayout { - self.firstNameInputFieldNode.activateInput() - } - - return resultSize - } - - func animateError() { - if self.firstNameInputFieldNode.text.isEmpty { - self.firstNameInputFieldNode.layer.addShakeAnimation() - } - self.hapticFeedback.error() - } -} - -func voiceChatUserNameController(sharedContext: SharedAccountContext, account: Account, forceTheme: PresentationTheme?, title: String, firstNamePlaceholder: String, lastNamePlaceholder: String, doneButtonTitle: String? = nil, firstName: String?, lastName: String?, maxLength: Int, apply: @escaping ((String, String)?) -> Void) -> AlertController { - var presentationData = sharedContext.currentPresentationData.with { $0 } - if let forceTheme = forceTheme { +func voiceChatUserNameController( + context: AccountContext, + forceTheme: PresentationTheme?, + title: String, + firstNamePlaceholder: String, + lastNamePlaceholder: String, + doneButtonTitle: String? = nil, + firstName: String?, + lastName: String?, + maxLength: Int, + apply: @escaping ((String, String)?) -> Void +) -> ViewController { + var presentationData = context.sharedContext.currentPresentationData.with { $0 } + if let forceTheme { presentationData = presentationData.withUpdated(theme: forceTheme) } + let strings = presentationData.strings + + let firstNameState = AlertInputFieldComponent.ExternalState() + let lastNameState = AlertInputFieldComponent.ExternalState() + + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: title) + ) + )) - var dismissImpl: ((Bool) -> Void)? + var nextImpl: (() -> Void)? var applyImpl: (() -> Void)? + content.append(AnyComponentWithIdentity( + id: "firstName", + component: AnyComponent( + AlertInputFieldComponent( + context: context, + initialValue: firstName, + placeholder: firstNamePlaceholder, + characterLimit: maxLength, + hasClearButton: true, + returnKeyType: .next, + isInitiallyFocused: true, + externalState: firstNameState, + returnKeyAction: { + nextImpl?() + } + ) + ) + )) - let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - dismissImpl?(true) - }), TextAlertAction(type: .defaultAction, title: doneButtonTitle ?? presentationData.strings.Common_Done, action: { - applyImpl?() - })] - - let contentNode = VoiceChatUserNameEditAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: actions, title: title, firstNamePlaceholder: firstNamePlaceholder, lastNamePlaceholder: lastNamePlaceholder, firstNameValue: firstName, lastNameValue: lastName, maxLength: maxLength) - contentNode.complete = { - applyImpl?() + content.append(AnyComponentWithIdentity( + id: "lastName", + component: AnyComponent( + AlertInputFieldComponent( + context: context, + initialValue: lastName, + placeholder: lastNamePlaceholder, + characterLimit: maxLength, + hasClearButton: true, + isInitiallyFocused: false, + externalState: lastNameState, + returnKeyAction: { + applyImpl?() + } + ) + ) + )) + + let alertController = AlertScreen( + configuration: AlertScreen.Configuration(allowInputInset: true), + content: content, + actions: [ + .init(title: strings.Common_Cancel), + .init(title: doneButtonTitle ?? strings.Common_Done, type: .default, action: { + applyImpl?() + }) + ], + updatedPresentationData: (presentationData, .single(presentationData)) + ) + nextImpl = { + lastNameState.activateInput() } - applyImpl = { [weak contentNode] in - guard let contentNode = contentNode else { - return - } - + applyImpl = { let previousFirstName = firstName ?? "" let previousLastName = lastName ?? "" - let newFirstName = contentNode.firstName.trimmingCharacters(in: .whitespacesAndNewlines) - let newLastName = contentNode.lastName.trimmingCharacters(in: .whitespacesAndNewlines) + let newFirstName = firstNameState.value.trimmingCharacters(in: .whitespacesAndNewlines) + let newLastName = lastNameState.value.trimmingCharacters(in: .whitespacesAndNewlines) if newFirstName.isEmpty { - contentNode.animateError() + firstNameState.animateError() return } - - dismissImpl?(true) - + if previousFirstName != newFirstName || previousLastName != newLastName { apply((newFirstName, newLastName)) } else { apply(nil) } } - - let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode) - let presentationDataDisposable = sharedContext.presentationData.start(next: { [weak controller, weak contentNode] presentationData in - var presentationData = presentationData - if let forceTheme = forceTheme { - presentationData = presentationData.withUpdated(theme: forceTheme) - } - controller?.theme = AlertControllerTheme(presentationData: presentationData) - contentNode?.firstNameInputFieldNode.updateTheme(presentationData.theme) - contentNode?.lastNameInputFieldNode.updateTheme(presentationData.theme) - }) - controller.dismissed = { _ in - presentationDataDisposable.dispose() - } - dismissImpl = { [weak controller, weak contentNode] animated in - contentNode?.firstNameInputFieldNode.deactivateInput() - contentNode?.lastNameInputFieldNode.deactivateInput() - if animated { - controller?.dismissAnimated() - } else { - controller?.dismiss() - } - } - return controller + return alertController } diff --git a/submodules/TelegramCore/BUILD b/submodules/TelegramCore/BUILD index fe9bc15d..53c9a721 100644 --- a/submodules/TelegramCore/BUILD +++ b/submodules/TelegramCore/BUILD @@ -25,6 +25,7 @@ swift_library( "//submodules/Emoji", "//submodules/TelegramCore/FlatBuffers", "//submodules/TelegramCore/FlatSerialization", + "//submodules/MurMurHash32" ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift b/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift index 7c0a3db5..fbc85e9e 100644 --- a/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift +++ b/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift @@ -139,6 +139,7 @@ enum AccountStateMutationOperation { case UpdateMonoForumNoPaidException(peerId: PeerId, threadId: Int64, isFree: Bool) case UpdateStarGiftAuctionState(giftId: Int64, state: GiftAuctionContext.State.AuctionState) case UpdateStarGiftAuctionMyState(giftId: Int64, state: GiftAuctionContext.State.MyState) + case UpdateEmojiGameInfo(info: EmojiGameInfo) } struct HoleFromPreviousState { @@ -737,9 +738,13 @@ struct AccountMutableState { self.addOperation(.UpdateStarGiftAuctionMyState(giftId: giftId, state: state)) } + mutating func updateEmojiGameInfo(info: EmojiGameInfo) { + self.addOperation(.UpdateEmojiGameInfo(info: info)) + } + mutating func addOperation(_ operation: AccountStateMutationOperation) { switch operation { - case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .AddPeerLiveTypingDraftUpdate, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedSavedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .UpdateWallpaper, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateGroupCallChainBlocks, .UpdateGroupCallMessage, .UpdateGroupCallOpaqueMessage, .UpdateMessagesPinned, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateStarsBalance, .UpdateStarsRevenueStatus, .UpdateStarsReactionsDefaultPrivacy, .ReportMessageDelivery, .UpdateMonoForumNoPaidException, .UpdateStarGiftAuctionState, .UpdateStarGiftAuctionMyState: + case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .AddPeerLiveTypingDraftUpdate, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedSavedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .UpdateWallpaper, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateGroupCallChainBlocks, .UpdateGroupCallMessage, .UpdateGroupCallOpaqueMessage, .UpdateMessagesPinned, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateStarsBalance, .UpdateStarsRevenueStatus, .UpdateStarsReactionsDefaultPrivacy, .ReportMessageDelivery, .UpdateMonoForumNoPaidException, .UpdateStarGiftAuctionState, .UpdateStarGiftAuctionMyState, .UpdateEmojiGameInfo: break case let .AddMessages(messages, location): for message in messages { @@ -892,6 +897,7 @@ struct AccountReplayedFinalState { let addedConferenceInvitationMessagesIds: [MessageId] let updatedStarGiftAuctionState: [Int64: GiftAuctionContext.State.AuctionState] let updatedStarGiftAuctionMyState: [Int64: GiftAuctionContext.State.MyState] + let updatedEmojiGameInfo: EmojiGameInfo? } struct AccountFinalStateEvents { @@ -927,12 +933,13 @@ struct AccountFinalStateEvents { let addedConferenceInvitationMessagesIds: [MessageId] let updatedStarGiftAuctionState: [Int64: GiftAuctionContext.State.AuctionState] let updatedStarGiftAuctionMyState: [Int64: GiftAuctionContext.State.MyState] + let updatedEmojiGameInfo: EmojiGameInfo? var isEmpty: Bool { - return self.addedIncomingMessageIds.isEmpty && self.addedReactionEvents.isEmpty && self.wasScheduledMessageIds.isEmpty && self.deletedMessageIds.isEmpty && self.sentScheduledMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.addedCallSignalingData.isEmpty && self.updatedGroupCallParticipants.isEmpty && self.groupCallMessageUpdates.isEmpty && self.storyUpdates.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && self.dismissBotWebViews.isEmpty && self.delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty && !authorizationListUpdated && self.updatedIncomingThreadReadStates.isEmpty && self.updatedOutgoingThreadReadStates.isEmpty && !self.updateConfig && !self.isPremiumUpdated && self.updatedStarsBalance.isEmpty && self.updatedTonBalance.isEmpty && self.updatedStarsRevenueStatus.isEmpty && self.reportMessageDelivery.isEmpty && self.addedConferenceInvitationMessagesIds.isEmpty && self.updatedStarGiftAuctionState.isEmpty && self.updatedStarGiftAuctionMyState.isEmpty + return self.addedIncomingMessageIds.isEmpty && self.addedReactionEvents.isEmpty && self.wasScheduledMessageIds.isEmpty && self.deletedMessageIds.isEmpty && self.sentScheduledMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.addedCallSignalingData.isEmpty && self.updatedGroupCallParticipants.isEmpty && self.groupCallMessageUpdates.isEmpty && self.storyUpdates.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && self.dismissBotWebViews.isEmpty && self.delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty && !authorizationListUpdated && self.updatedIncomingThreadReadStates.isEmpty && self.updatedOutgoingThreadReadStates.isEmpty && !self.updateConfig && !self.isPremiumUpdated && self.updatedStarsBalance.isEmpty && self.updatedTonBalance.isEmpty && self.updatedStarsRevenueStatus.isEmpty && self.reportMessageDelivery.isEmpty && self.addedConferenceInvitationMessagesIds.isEmpty && self.updatedStarGiftAuctionState.isEmpty && self.updatedStarGiftAuctionMyState.isEmpty && self.updatedEmojiGameInfo == nil } - init(addedIncomingMessageIds: [MessageId] = [], addedReactionEvents: [(reactionAuthor: Peer, reaction: MessageReaction.Reaction, message: Message, timestamp: Int32)] = [], wasScheduledMessageIds: [MessageId] = [], deletedMessageIds: [DeletedMessageId] = [], updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] = [], groupCallMessageUpdates: [GroupCallMessageUpdate] = [], storyUpdates: [InternalStoryUpdate] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], dismissBotWebViews: [Int64] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set = Set(), authorizationListUpdated: Bool = false, updatedIncomingThreadReadStates: [PeerAndBoundThreadId: MessageId.Id] = [:], updatedOutgoingThreadReadStates: [PeerAndBoundThreadId: MessageId.Id] = [:], updateConfig: Bool = false, isPremiumUpdated: Bool = false, updatedStarsBalance: [PeerId: StarsAmount] = [:], updatedTonBalance: [PeerId: StarsAmount] = [:], updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances] = [:], sentScheduledMessageIds: Set = Set(), reportMessageDelivery: Set = Set(), addedConferenceInvitationMessagesIds: [MessageId] = [], updatedStarGiftAuctionState: [Int64: GiftAuctionContext.State.AuctionState] = [:], updatedStarGiftAuctionMyState: [Int64: GiftAuctionContext.State.MyState] = [:]) { + init(addedIncomingMessageIds: [MessageId] = [], addedReactionEvents: [(reactionAuthor: Peer, reaction: MessageReaction.Reaction, message: Message, timestamp: Int32)] = [], wasScheduledMessageIds: [MessageId] = [], deletedMessageIds: [DeletedMessageId] = [], updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] = [], groupCallMessageUpdates: [GroupCallMessageUpdate] = [], storyUpdates: [InternalStoryUpdate] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], dismissBotWebViews: [Int64] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set = Set(), authorizationListUpdated: Bool = false, updatedIncomingThreadReadStates: [PeerAndBoundThreadId: MessageId.Id] = [:], updatedOutgoingThreadReadStates: [PeerAndBoundThreadId: MessageId.Id] = [:], updateConfig: Bool = false, isPremiumUpdated: Bool = false, updatedStarsBalance: [PeerId: StarsAmount] = [:], updatedTonBalance: [PeerId: StarsAmount] = [:], updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances] = [:], sentScheduledMessageIds: Set = Set(), reportMessageDelivery: Set = Set(), addedConferenceInvitationMessagesIds: [MessageId] = [], updatedStarGiftAuctionState: [Int64: GiftAuctionContext.State.AuctionState] = [:], updatedStarGiftAuctionMyState: [Int64: GiftAuctionContext.State.MyState] = [:], updatedEmojiGameInfo: EmojiGameInfo? = nil) { self.addedIncomingMessageIds = addedIncomingMessageIds self.addedReactionEvents = addedReactionEvents self.wasScheduledMessageIds = wasScheduledMessageIds @@ -965,6 +972,7 @@ struct AccountFinalStateEvents { self.addedConferenceInvitationMessagesIds = addedConferenceInvitationMessagesIds self.updatedStarGiftAuctionState = updatedStarGiftAuctionState self.updatedStarGiftAuctionMyState = updatedStarGiftAuctionMyState + self.updatedEmojiGameInfo = updatedEmojiGameInfo } init(state: AccountReplayedFinalState) { @@ -1000,6 +1008,7 @@ struct AccountFinalStateEvents { self.addedConferenceInvitationMessagesIds = state.addedConferenceInvitationMessagesIds self.updatedStarGiftAuctionState = state.updatedStarGiftAuctionState self.updatedStarGiftAuctionMyState = state.updatedStarGiftAuctionMyState + self.updatedEmojiGameInfo = state.updatedEmojiGameInfo } func union(with other: AccountFinalStateEvents) -> AccountFinalStateEvents { @@ -1068,7 +1077,8 @@ struct AccountFinalStateEvents { reportMessageDelivery: reportMessageDelivery, addedConferenceInvitationMessagesIds: addedConferenceInvitationMessagesIds, updatedStarGiftAuctionState: self.updatedStarGiftAuctionState.merging(other.updatedStarGiftAuctionState, uniquingKeysWith: { lhs, _ in lhs }), - updatedStarGiftAuctionMyState: self.updatedStarGiftAuctionMyState.merging(other.updatedStarGiftAuctionMyState, uniquingKeysWith: { lhs, _ in lhs }) + updatedStarGiftAuctionMyState: self.updatedStarGiftAuctionMyState.merging(other.updatedStarGiftAuctionMyState, uniquingKeysWith: { lhs, _ in lhs }), + updatedEmojiGameInfo: self.updatedEmojiGameInfo ) } } diff --git a/submodules/TelegramCore/Sources/Account/AccountManager.swift b/submodules/TelegramCore/Sources/Account/AccountManager.swift index d2031258..9897b02c 100644 --- a/submodules/TelegramCore/Sources/Account/AccountManager.swift +++ b/submodules/TelegramCore/Sources/Account/AccountManager.swift @@ -240,6 +240,7 @@ private var declaredEncodables: Void = { declareEncodable(PublishedSuggestedPostMessageAttribute.self, f: { PublishedSuggestedPostMessageAttribute(decoder: $0) }) declareEncodable(TelegramMediaLiveStream.self, f: { TelegramMediaLiveStream(decoder: $0) }) declareEncodable(ScheduledRepeatAttribute.self, f: { ScheduledRepeatAttribute(decoder: $0) }) + declareEncodable(SummarizationMessageAttribute.self, f: { SummarizationMessageAttribute(decoder: $0) }) return }() diff --git a/submodules/TelegramCore/Sources/AntiDelete/LocalEditManager.swift b/submodules/TelegramCore/Sources/AntiDelete/LocalEditManager.swift new file mode 100644 index 00000000..75f9af07 --- /dev/null +++ b/submodules/TelegramCore/Sources/AntiDelete/LocalEditManager.swift @@ -0,0 +1,85 @@ +import Foundation + +/// Менеджер для локального редактирования сообщений (только на стороне клиента) +/// Хранит редактирования в памяти - сбрасываются при перезапуске приложения +public final class LocalEditManager { + + public static let shared = LocalEditManager() + + // MARK: - Storage + + /// Хранилище редактирований: "peerId_messageId" -> новый текст + private var edits: [String: String] = [:] + private let lock = NSLock() + + private init() {} + + // MARK: - Public API + + /// Установить локальное редактирование для сообщения + /// - Parameters: + /// - peerId: ID чата + /// - messageId: ID сообщения + /// - newText: Новый текст сообщения + public func setLocalEdit(peerId: Int64, messageId: Int32, newText: String) { + let key = makeKey(peerId: peerId, messageId: messageId) + lock.lock() + defer { lock.unlock() } + edits[key] = newText + } + + /// Получить локальное редактирование для сообщения + /// - Parameters: + /// - peerId: ID чата + /// - messageId: ID сообщения + /// - Returns: Отредактированный текст или nil если редактирования нет + public func getLocalEdit(peerId: Int64, messageId: Int32) -> String? { + let key = makeKey(peerId: peerId, messageId: messageId) + lock.lock() + defer { lock.unlock() } + return edits[key] + } + + /// Удалить локальное редактирование для сообщения + /// - Parameters: + /// - peerId: ID чата + /// - messageId: ID сообщения + public func removeLocalEdit(peerId: Int64, messageId: Int32) { + let key = makeKey(peerId: peerId, messageId: messageId) + lock.lock() + defer { lock.unlock() } + edits.removeValue(forKey: key) + } + + /// Проверить наличие локального редактирования + /// - Parameters: + /// - peerId: ID чата + /// - messageId: ID сообщения + /// - Returns: true если есть редактирование + public func hasLocalEdit(peerId: Int64, messageId: Int32) -> Bool { + let key = makeKey(peerId: peerId, messageId: messageId) + lock.lock() + defer { lock.unlock() } + return edits[key] != nil + } + + /// Очистить все локальные редактирования + public func clearAllEdits() { + lock.lock() + defer { lock.unlock() } + edits.removeAll() + } + + /// Количество активных редактирований + public var editCount: Int { + lock.lock() + defer { lock.unlock() } + return edits.count + } + + // MARK: - Private + + private func makeKey(peerId: Int64, messageId: Int32) -> String { + return "\(peerId)_\(messageId)" + } +} diff --git a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift index f32fbbe7..ca8956b5 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift @@ -128,7 +128,7 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute], func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? { switch messsage { - case let .message(_, _, _, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, _, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): let chatPeerId = messagePeerId return chatPeerId.peerId case let .messageEmpty(_, _, peerId): @@ -144,7 +144,7 @@ func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? { func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] { switch message { - case let .message(_, _, _, fromId, _, chatPeerId, savedPeerId, fwdHeader, viaBotId, viaBusinessBotId, replyTo, _, _, media, _, entities, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, _, fromId, _, chatPeerId, savedPeerId, fwdHeader, viaBotId, viaBusinessBotId, replyTo, _, _, media, _, entities, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): let peerId: PeerId = chatPeerId.peerId var result = [peerId] @@ -279,7 +279,7 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] { func apiMessageAssociatedMessageIds(_ message: Api.Message) -> (replyIds: ReferencedReplyMessageIds, generalIds: [MessageId])? { switch message { - case let .message(_, _, id, _, _, chatPeerId, _, _, _, _, replyTo, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, id, _, _, chatPeerId, _, _, _, _, replyTo, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): if let replyTo = replyTo { let peerId: PeerId = chatPeerId.peerId @@ -457,8 +457,17 @@ func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerI } return (TelegramMediaTodo(flags: flags, text: todoText, textEntities: todoEntities, items: list.map(TelegramMediaTodo.Item.init(apiItem:)), completions: todoCompletions), nil, nil, nil, nil, nil) } - case let .messageMediaDice(value, emoticon): - return (TelegramMediaDice(emoji: emoticon, value: value), nil, nil, nil, nil, nil) + case let .messageMediaDice(_, value, emoticon, apiGameOutcome): + var gameOutcome: TelegramMediaDice.GameOutcome? + var tonAmount: Int64? + switch apiGameOutcome { + case let .emojiGameOutcome(seed, stakeTonAmount, outcomeTonAmount): + gameOutcome = TelegramMediaDice.GameOutcome(seed: seed.makeData(), tonAmount: outcomeTonAmount) + tonAmount = stakeTonAmount + default: + break + } + return (TelegramMediaDice(emoji: emoticon, tonAmount: tonAmount, value: value, gameOutcome: gameOutcome), nil, nil, nil, nil, nil) case let .messageMediaStory(flags, peerId, id, _): let isMention = (flags & (1 << 1)) != 0 return (TelegramMediaStory(storyId: StoryId(peerId: peerId.peerId, id: id), isMention: isMention), nil, nil, nil, nil, nil) @@ -703,7 +712,7 @@ func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [Mes extension StoreMessage { convenience init?(apiMessage: Api.Message, accountPeerId: PeerId, peerIsForum: Bool, namespace: MessageId.Namespace = Namespaces.Message.Cloud) { switch apiMessage { - case let .message(flags, flags2, id, fromId, boosts, chatPeerId, savedPeerId, fwdFrom, viaBotId, viaBusinessBotId, replyTo, date, message, media, replyMarkup, entities, views, forwards, replies, editDate, postAuthor, groupingId, reactions, restrictionReason, ttlPeriod, quickReplyShortcutId, messageEffectId, factCheck, reportDeliveryUntilDate, paidMessageStars, suggestedPost, scheduledRepeatPeriod): + case let .message(flags, flags2, id, fromId, boosts, chatPeerId, savedPeerId, fwdFrom, viaBotId, viaBusinessBotId, replyTo, date, message, media, replyMarkup, entities, views, forwards, replies, editDate, postAuthor, groupingId, reactions, restrictionReason, ttlPeriod, quickReplyShortcutId, messageEffectId, factCheck, reportDeliveryUntilDate, paidMessageStars, suggestedPost, scheduledRepeatPeriod, summaryFromLanguage): var attributes: [MessageAttribute] = [] if (flags2 & (1 << 4)) != 0 { @@ -964,6 +973,10 @@ extension StoreMessage { attributes.append(ScheduledRepeatAttribute(repeatPeriod: scheduledRepeatPeriod)) } + if let summaryFromLanguage { + attributes.append(SummarizationMessageAttribute(fromLang: summaryFromLanguage)) + } + var entitiesAttribute: TextEntitiesMessageAttribute? if let entities = entities, !entities.isEmpty { let attribute = TextEntitiesMessageAttribute(entities: messageTextEntitiesFromApiEntities(entities)) @@ -988,7 +1001,7 @@ extension StoreMessage { } } - if (flags & (1 << 17)) != 0 { + if (flags & (1 << 19)) != 0 { attributes.append(ContentRequiresValidationMessageAttribute()) } @@ -1174,7 +1187,7 @@ extension StoreMessage { threadId = 1 } - if (flags & (1 << 17)) != 0 { + if (flags & (1 << 19)) != 0 { attributes.append(ContentRequiresValidationMessageAttribute()) } diff --git a/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift b/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift index 1940b8d0..4f78188e 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift @@ -343,8 +343,26 @@ func mediaContentToUpload(accountPeerId: PeerId, network: Network, postbox: Post let inputTodo = Api.InputMedia.inputMediaTodo(todo: .todoList(flags: flags, title: .textWithEntities(text: todo.text, entities: apiEntitiesFromMessageTextEntities(todo.textEntities, associatedPeers: SimpleDictionary())), list: todo.items.map { $0.apiItem })) return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(inputTodo, text), reuploadInfo: nil, cacheReferenceKey: nil))) } else if let dice = media as? TelegramMediaDice { - let inputDice = Api.InputMedia.inputMediaDice(emoticon: dice.emoji) - return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(inputDice, text), reuploadInfo: nil, cacheReferenceKey: nil))) + if let tonAmount = dice.tonAmount { + let seedBytes = malloc(32)! + let _ = SecRandomCopyBytes(nil, 32, seedBytes.assumingMemoryBound(to: UInt8.self)) + let clientSeed = MemoryBuffer(memory: seedBytes, capacity: 32, length: 32, freeWhenDone: true) + + return postbox.transaction { transaction -> Signal in + let gameInfo = currentEmojiGameInfo(transaction: transaction) + if case let .available(info) = gameInfo { + let inputStakeDice = Api.InputMedia.inputMediaStakeDice(gameHash: info.gameHash, tonAmount: tonAmount, clientSeed: Buffer(buffer: clientSeed)) + return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(inputStakeDice, text), reuploadInfo: nil, cacheReferenceKey: nil))) + } else { + return .fail(.generic) + } + } + |> castError(PendingMessageUploadError.self) + |> switchToLatest + } else { + let inputDice = Api.InputMedia.inputMediaDice(emoticon: dice.emoji) + return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(inputDice, text), reuploadInfo: nil, cacheReferenceKey: nil))) + } } else if let webPage = media as? TelegramMediaWebpage, case let .Loaded(content) = webPage.content { var flags: Int32 = 0 flags |= 1 << 2 diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index 4c823903..0f6e050e 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -1020,6 +1020,8 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox: let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(channelId)) updatedState.updateMinAvailableMessage(MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: minId)) case let .updateDeleteMessages(messages, _, _): + // Note: Actual archiving happens in DeleteMessagesWithGlobalIds handler + // where we have access to transaction and can get full message content updatedState.deleteMessagesWithGlobalIds(messages) case let .updatePinnedMessages(flags, peer, messages, _, _): let peerId = peer.peerId @@ -1906,6 +1908,8 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox: } case let .updateStarGiftAuctionUserState(giftId, userState): updatedState.updateStarGiftAuctionMyState(giftId: giftId, state: GiftAuctionContext.State.MyState(apiAuctionUserState: userState)) + case let .updateEmojiGameInfo(info): + updatedState.updateEmojiGameInfo(info: EmojiGameInfo(apiEmojiGameInfo: info)) default: break } @@ -3629,7 +3633,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation]) var currentAddQuickReplyMessages: OptimizeAddMessagesState? for operation in operations { switch operation { - case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .AddPeerLiveTypingDraftUpdate, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedSavedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateGroupCallChainBlocks, .UpdateGroupCallMessage, .UpdateGroupCallOpaqueMessage, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateWallpaper, .UpdateStarsBalance, .UpdateStarsRevenueStatus, .UpdateStarsReactionsDefaultPrivacy, .ReportMessageDelivery, .UpdateMonoForumNoPaidException, .UpdateStarGiftAuctionState, .UpdateStarGiftAuctionMyState: + case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .AddPeerLiveTypingDraftUpdate, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedSavedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateGroupCallChainBlocks, .UpdateGroupCallMessage, .UpdateGroupCallOpaqueMessage, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateWallpaper, .UpdateStarsBalance, .UpdateStarsRevenueStatus, .UpdateStarsReactionsDefaultPrivacy, .ReportMessageDelivery, .UpdateMonoForumNoPaidException, .UpdateStarGiftAuctionState, .UpdateStarGiftAuctionMyState, .UpdateEmojiGameInfo: if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty { result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location)) } @@ -3772,6 +3776,7 @@ func replayFinalState( var reportMessageDelivery = Set() var updatedStarGiftAuctionState: [Int64: GiftAuctionContext.State.AuctionState] = [:] var updatedStarGiftAuctionMyState: [Int64: GiftAuctionContext.State.MyState] = [:] + var updatedEmojiGameInfo: EmojiGameInfo? var holesFromPreviousStateMessageIds: [MessageId] = [] var clearHolesFromPreviousStateForChannelMessagesWithPts: [PeerIdAndMessageNamespace: Int32] = [:] @@ -4224,18 +4229,166 @@ func replayFinalState( } } case let .DeleteMessagesWithGlobalIds(ids): - var resourceIds: [MediaResourceId] = [] - transaction.deleteMessagesWithGlobalIds(ids, forEachMedia: { media in - addMessageMediaResourceIdsToRemove(media: media, resourceIds: &resourceIds) - }) - if !resourceIds.isEmpty { - let _ = mediaBox.removeCachedResources(Array(Set(resourceIds)), force: true).start() + // ANTI-DELETE: Archive messages with full content before deletion + if AntiDeleteManager.shared.isEnabled { + let messageIds = transaction.messageIdsForGlobalIds(ids) + for (index, messageId) in messageIds.enumerated() { + if let message = transaction.getMessage(messageId) { + let globalId = index < ids.count ? ids[index] : 0 + + // Extract text content + let textContent = message.text + + // Extract media description + var mediaDesc: String? = nil + for media in message.media { + switch media { + case let image as TelegramMediaImage: + mediaDesc = "📷 Photo" + if let largest = image.representations.last { + mediaDesc = "📷 Photo \(largest.dimensions.width)x\(largest.dimensions.height)" + } + case let file as TelegramMediaFile: + if file.isVideo { + mediaDesc = "🎬 Video" + } else if file.isVoice { + mediaDesc = "🎤 Voice Message" + } else if file.isInstantVideo { + mediaDesc = "📹 Video Message" + } else if file.isSticker { + mediaDesc = "🎭 Sticker" + } else { + mediaDesc = "📎 \(file.fileName ?? "File")" + } + case is TelegramMediaContact: + mediaDesc = "👤 Contact" + case is TelegramMediaMap: + mediaDesc = "📍 Location" + case let poll as TelegramMediaPoll: + mediaDesc = "📊 Poll: \(poll.text)" + default: + break + } + } + + AntiDeleteManager.shared.archiveMessage( + globalId: globalId, + peerId: messageId.peerId.toInt64(), + messageId: messageId.id, + timestamp: message.timestamp, + authorId: message.author?.id.toInt64(), + text: textContent, + forwardAuthorId: message.forwardInfo?.author?.id.toInt64(), + mediaDescription: mediaDesc + ) + } + } + } + + // ANTI-DELETE: Mark messages as deleted instead of removing them + if AntiDeleteManager.shared.isEnabled { + let messageIds = transaction.messageIdsForGlobalIds(ids) + for messageId in messageIds { + // Mark as deleted for icon display + AntiDeleteManager.shared.markAsDeleted(peerId: messageId.peerId.toInt64(), messageId: messageId.id) + + transaction.updateMessage(messageId, update: { currentMessage in + var attributes = currentMessage.attributes + // Don't add duplicate DeletedMessageAttribute + if !attributes.contains(where: { $0 is DeletedMessageAttribute }) { + attributes.append(DeletedMessageAttribute(deletedAt: Int32(Date().timeIntervalSince1970))) + } + let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init) + // Keep original text, no prefix needed - icon will show deleted status + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + }) + } + } else { + var resourceIds: [MediaResourceId] = [] + transaction.deleteMessagesWithGlobalIds(ids, forEachMedia: { media in + addMessageMediaResourceIdsToRemove(media: media, resourceIds: &resourceIds) + }) + if !resourceIds.isEmpty { + let _ = mediaBox.removeCachedResources(Array(Set(resourceIds)), force: true).start() + } } deletedMessageIds.append(contentsOf: ids.map { .global($0) }) case let .DeleteMessages(ids): - _internal_deleteMessages(transaction: transaction, mediaBox: mediaBox, ids: ids, manualAddMessageThreadStatsDifference: { id, add, remove in - addMessageThreadStatsDifference(threadKey: id, remove: remove, addedMessagePeer: nil, addedMessageId: nil, isOutgoing: false) - }) + // ANTI-DELETE: Archive channel messages with full content before deletion + if AntiDeleteManager.shared.isEnabled { + for messageId in ids { + if let message = transaction.getMessage(messageId) { + // Extract text content + let textContent = message.text + + // Extract media description + var mediaDesc: String? = nil + for media in message.media { + switch media { + case let image as TelegramMediaImage: + mediaDesc = "📷 Photo" + if let largest = image.representations.last { + mediaDesc = "📷 Photo \(largest.dimensions.width)x\(largest.dimensions.height)" + } + case let file as TelegramMediaFile: + if file.isVideo { + mediaDesc = "🎬 Video" + } else if file.isVoice { + mediaDesc = "🎤 Voice Message" + } else if file.isInstantVideo { + mediaDesc = "📹 Video Message" + } else if file.isSticker { + mediaDesc = "🎭 Sticker" + } else { + mediaDesc = "📎 \(file.fileName ?? "File")" + } + case is TelegramMediaContact: + mediaDesc = "👤 Contact" + case is TelegramMediaMap: + mediaDesc = "📍 Location" + case let poll as TelegramMediaPoll: + mediaDesc = "📊 Poll: \(poll.text)" + default: + break + } + } + + AntiDeleteManager.shared.archiveMessage( + globalId: messageId.id, // Use message id as globalId for channel messages + peerId: messageId.peerId.toInt64(), + messageId: messageId.id, + timestamp: message.timestamp, + authorId: message.author?.id.toInt64(), + text: textContent, + forwardAuthorId: message.forwardInfo?.author?.id.toInt64(), + mediaDescription: mediaDesc + ) + } + } + } + + // ANTI-DELETE: Mark messages as deleted instead of removing them + if AntiDeleteManager.shared.isEnabled { + for messageId in ids { + // Mark as deleted for icon display + AntiDeleteManager.shared.markAsDeleted(peerId: messageId.peerId.toInt64(), messageId: messageId.id) + + transaction.updateMessage(messageId, update: { currentMessage in + var attributes = currentMessage.attributes + // Don't add duplicate DeletedMessageAttribute + if !attributes.contains(where: { $0 is DeletedMessageAttribute }) { + attributes.append(DeletedMessageAttribute(deletedAt: Int32(Date().timeIntervalSince1970))) + } + let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init) + // Keep original text, no prefix needed - icon will show deleted status + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + }) + } + } else { + _internal_deleteMessages(transaction: transaction, mediaBox: mediaBox, ids: ids, manualAddMessageThreadStatsDifference: { id, add, remove in + addMessageThreadStatsDifference(threadKey: id, remove: remove, addedMessagePeer: nil, addedMessageId: nil, isOutgoing: false) + }) + } deletedMessageIds.append(contentsOf: ids.map { .messageId($0) }) case let .UpdateMinAvailableMessage(id): if let message = transaction.getMessage(id) { @@ -4294,6 +4447,14 @@ func replayFinalState( updatedAttributes.append(translation) } } + } else { + // GHOSTGRAM: Save original text before edit + EditHistoryManager.shared.saveOriginalText( + peerId: id.peerId.toInt64(), + messageId: id.id, + originalText: previousMessage.text, + editDate: Int32(Date().timeIntervalSince1970) + ) } if let previousFactCheckAttribute = previousMessage.attributes.first(where: { $0 is FactCheckMessageAttribute }) as? FactCheckMessageAttribute, let updatedFactCheckAttribute = message.attributes.first(where: { $0 is FactCheckMessageAttribute }) as? FactCheckMessageAttribute { @@ -5339,6 +5500,8 @@ func replayFinalState( updatedStarGiftAuctionState[giftId] = state case let .UpdateStarGiftAuctionMyState(giftId, state): updatedStarGiftAuctionMyState[giftId] = state + case let .UpdateEmojiGameInfo(info): + updatedEmojiGameInfo = info } } @@ -5889,6 +6052,7 @@ func replayFinalState( reportMessageDelivery: reportMessageDelivery, addedConferenceInvitationMessagesIds: addedConferenceInvitationMessagesIds, updatedStarGiftAuctionState: updatedStarGiftAuctionState, - updatedStarGiftAuctionMyState: updatedStarGiftAuctionMyState + updatedStarGiftAuctionMyState: updatedStarGiftAuctionMyState, + updatedEmojiGameInfo: updatedEmojiGameInfo ) } diff --git a/submodules/TelegramCore/Sources/State/AccountStateManager.swift b/submodules/TelegramCore/Sources/State/AccountStateManager.swift index e9cce703..75c8b017 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManager.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManager.swift @@ -374,6 +374,7 @@ public final class AccountStateManager { private let appliedQtsPromise = Promise(nil) private let appliedQtsDisposable = MetaDisposable() private let reportMessageDeliveryDisposable = DisposableSet() + private let updateEmojiGameInfoDisposable = MetaDisposable() let updateConfigRequested: (() -> Void)? let isPremiumUpdated: (() -> Void)? @@ -414,6 +415,7 @@ public final class AccountStateManager { self.appliedMaxMessageIdDisposable.dispose() self.appliedQtsDisposable.dispose() self.reportMessageDeliveryDisposable.dispose() + self.updateEmojiGameInfoDisposable.dispose() } public func reset() { @@ -1137,6 +1139,11 @@ public final class AccountStateManager { if !events.updatedStarGiftAuctionMyState.isEmpty { strongSelf.notifyUpdatedStarGiftAuctionMyState(events.updatedStarGiftAuctionMyState) } + if let updatedEmojiGameInfo = events.updatedEmojiGameInfo { + strongSelf.updateEmojiGameInfoDisposable.set(strongSelf.postbox.transaction({ transaction in + updateEmojiGameInfo(transaction: transaction, { _ in return updatedEmojiGameInfo }) + }).start()) + } if !events.updatedCalls.isEmpty { for call in events.updatedCalls { strongSelf.callSessionManager?.updateSession(call, completion: { _ in }) diff --git a/submodules/TelegramCore/Sources/State/AccountTaskManager.swift b/submodules/TelegramCore/Sources/State/AccountTaskManager.swift index d470168d..1b644084 100644 --- a/submodules/TelegramCore/Sources/State/AccountTaskManager.swift +++ b/submodules/TelegramCore/Sources/State/AccountTaskManager.swift @@ -125,6 +125,7 @@ final class AccountTaskManager { tasks.add(managedPeerColorUpdates(postbox: self.stateManager.postbox, network: self.stateManager.network).start()) tasks.add(managedStarGiftsUpdates(postbox: self.stateManager.postbox, network: self.stateManager.network, accountPeerId: self.stateManager.accountPeerId).start()) tasks.add(managedSavedMusicIdsUpdates(postbox: self.stateManager.postbox, network: self.stateManager.network, accountPeerId: self.stateManager.accountPeerId).start()) + tasks.add(managedEmojiGameUpdates(postbox: self.stateManager.postbox, network: self.stateManager.network).start()) self.managedTopReactionsDisposable.set(managedTopReactions(postbox: self.stateManager.postbox, network: self.stateManager.network).start()) diff --git a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift index e775e854..ae88fa3c 100644 --- a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift +++ b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift @@ -2173,7 +2173,7 @@ public final class AccountViewTracker { fixedCombinedReadStates: .peer([peerId: CombinedPeerReadState(states: [ (Namespaces.Message.Cloud, PeerReadState.idBased(maxIncomingReadId: Int32.max - 1, maxOutgoingReadId: Int32.max - 1, maxKnownId: Int32.max - 1, count: 0, markedUnread: false)) ])]), - topTaggedMessageIdNamespaces: [], + topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tag: tag, appendMessagesFromTheSameGroup: false, namespaces: .not(Namespaces.Message.allNonRegular), @@ -2201,7 +2201,7 @@ public final class AccountViewTracker { count: count, trackHoles: trackHoles, fixedCombinedReadStates: nil, - topTaggedMessageIdNamespaces: [], + topTaggedMessageIdNamespaces: [Namespaces.Message.Cloud], tag: tag, appendMessagesFromTheSameGroup: false, namespaces: .not(Namespaces.Message.allNonRegular), diff --git a/submodules/TelegramCore/Sources/State/ApplyUpdateMessage.swift b/submodules/TelegramCore/Sources/State/ApplyUpdateMessage.swift index 185ed044..56c75694 100644 --- a/submodules/TelegramCore/Sources/State/ApplyUpdateMessage.swift +++ b/submodules/TelegramCore/Sources/State/ApplyUpdateMessage.swift @@ -104,7 +104,7 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes var updatedTimestamp: Int32? if let apiMessage = apiMessage { switch apiMessage { - case let .message(_, _, _, _, _, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, _, _, _, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): updatedTimestamp = date case .messageEmpty: break @@ -400,7 +400,7 @@ func applyUpdateGroupMessages(postbox: Postbox, stateManager: AccountStateManage } else if let message = messages.first, let apiMessage = result.messages.first { if message.scheduleTime != nil && message.scheduleTime == apiMessage.timestamp { namespace = Namespaces.Message.ScheduledCloud - } else if let apiMessage = result.messages.first, case let .message(_, flags2, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = apiMessage, (flags2 & (1 << 4)) != 0 { + } else if let apiMessage = result.messages.first, case let .message(_, flags2, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = apiMessage, (flags2 & (1 << 4)) != 0 { namespace = Namespaces.Message.ScheduledCloud } } diff --git a/submodules/TelegramCore/Sources/State/ManagedAccountPresence.swift b/submodules/TelegramCore/Sources/State/ManagedAccountPresence.swift index 8c8a4fa3..620f1c31 100644 --- a/submodules/TelegramCore/Sources/State/ManagedAccountPresence.swift +++ b/submodules/TelegramCore/Sources/State/ManagedAccountPresence.swift @@ -43,8 +43,18 @@ private final class AccountPresenceManagerImpl { } private func updatePresence(_ isOnline: Bool) { + // GHOST MODE: Completely block status updates to freeze "last seen" time + if GhostModeManager.shared.shouldHideOnlineStatus { + self.onlineTimer?.invalidate() + self.onlineTimer = nil + return + } + + // ALWAYS ONLINE: Force online status when enabled + let effectiveOnline = MiscSettingsManager.shared.shouldAlwaysBeOnline ? true : isOnline + let request: Signal - if isOnline { + if effectiveOnline { let timer = SignalKitTimer(timeout: 30.0, repeat: false, completion: { [weak self] in guard let strongSelf = self else { return diff --git a/submodules/TelegramCore/Sources/State/ManagedCloudChatRemoveMessagesOperations.swift b/submodules/TelegramCore/Sources/State/ManagedCloudChatRemoveMessagesOperations.swift index 70157349..ddca0d3f 100644 --- a/submodules/TelegramCore/Sources/State/ManagedCloudChatRemoveMessagesOperations.swift +++ b/submodules/TelegramCore/Sources/State/ManagedCloudChatRemoveMessagesOperations.swift @@ -479,6 +479,26 @@ private func _internal_clearHistory(transaction: Transaction, postbox: Postbox, |> `catch` { _ -> Signal in return .complete() } + } else if let threadId = operation.threadId { + guard let inputPeer = apiInputPeer(peer) else { + return .complete() + } + return network.request(Api.functions.messages.deleteTopicHistory(peer: inputPeer, topMsgId: Int32(clamping: threadId))) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { result -> Signal in + if let result = result { + switch result { + case let .affectedHistory(pts, ptsCount, _): + stateManager.addUpdateGroups([.updatePts(pts: pts, ptsCount: ptsCount)]) + return .complete() + } + } else { + return .complete() + } + } } else { return requestClearHistory(postbox: postbox, network: network, stateManager: stateManager, inputPeer: inputPeer, maxId: operation.topMessageId.id, justClear: true, minTimestamp: operation.minTimestamp, maxTimestamp: operation.maxTimestamp, type: operation.type) } diff --git a/submodules/TelegramCore/Sources/State/ManagedEmojiGameUpdates.swift b/submodules/TelegramCore/Sources/State/ManagedEmojiGameUpdates.swift new file mode 100644 index 00000000..710f7890 --- /dev/null +++ b/submodules/TelegramCore/Sources/State/ManagedEmojiGameUpdates.swift @@ -0,0 +1,104 @@ +import Foundation +import Postbox +import SwiftSignalKit +import TelegramApi +import MtProtoKit + +public enum EmojiGameInfo: Codable, Equatable { + private enum CodingKeys: String, CodingKey { + case type + case info + } + + public struct Info: Codable, Equatable { + public let gameHash: String + public let previousStake: Int64 + public let currentStreak: Int32 + public let parameters: [Int32] + public let playsLeft: Int32? + } + + case available(Info) + case unavailable + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + + let type = try container.decode(Int32.self, forKey: .type) + switch type { + case 1: + self = .available(try container.decode(Info.self, forKey: .info)) + default: + self = .unavailable + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + switch self { + case let .available(info): + try container.encode(Int32(1), forKey: .type) + try container.encode(info, forKey: .info) + case .unavailable: + try container.encode(Int32(0), forKey: .type) + } + } +} + +extension EmojiGameInfo { + init(apiEmojiGameInfo: Api.messages.EmojiGameInfo) { + switch apiEmojiGameInfo { + case let .emojiGameDiceInfo(_, gameHash, prevStake, currentStreak, params, playsLeft): + self = .available(Info(gameHash: gameHash, previousStake: prevStake, currentStreak: currentStreak, parameters: params, playsLeft: playsLeft)) + case .emojiGameUnavailable: + self = .unavailable + } + } +} + + +public func currentEmojiGameInfo(transaction: Transaction) -> EmojiGameInfo { + if let entry = transaction.getPreferencesEntry(key: PreferencesKeys.emojiGameInfo())?.get(EmojiGameInfo.self) { + return entry + } else { + return .unavailable + } +} + +func updateEmojiGameInfo(transaction: Transaction, _ f: (EmojiGameInfo) -> EmojiGameInfo) { + let current = currentEmojiGameInfo(transaction: transaction) + let updated = f(current) + if updated != current { + transaction.setPreferencesEntry(key: PreferencesKeys.emojiGameInfo(), value: PreferencesEntry(updated)) + } +} + +func updateEmojiGameInfoOnce(postbox: Postbox, network: Network) -> Signal { + return network.request(Api.functions.messages.getEmojiGameInfo()) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { result -> Signal in + guard let result else { + return .complete() + } + return postbox.transaction { transaction -> Void in + let info = EmojiGameInfo(apiEmojiGameInfo: result) + updateEmojiGameInfo(transaction: transaction) { _ in + return info + } + } + } +} + +func managedEmojiGameUpdates(postbox: Postbox, network: Network) -> Signal { + let poll = Signal { subscriber in + return updateEmojiGameInfoOnce(postbox: postbox, network: network).start(completed: { + subscriber.putCompletion() + }) + } + return (poll |> then(.complete() |> suspendAwareDelay(1.0 * 60.0 * 60.0, queue: Queue.concurrentDefaultQueue()))) |> restart +} diff --git a/submodules/TelegramCore/Sources/State/ManagedLocalInputActivities.swift b/submodules/TelegramCore/Sources/State/ManagedLocalInputActivities.swift index c2fa13e3..099d3c85 100644 --- a/submodules/TelegramCore/Sources/State/ManagedLocalInputActivities.swift +++ b/submodules/TelegramCore/Sources/State/ManagedLocalInputActivities.swift @@ -142,6 +142,11 @@ private func actionFromActivity(_ activity: PeerInputActivity?) -> Api.SendMessa } private func requestActivity(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId, threadId: Int64?, activity: PeerInputActivity?) -> Signal { + // GHOST MODE: Block typing indicator + if GhostModeManager.shared.shouldHideTypingIndicator { + return .complete() + } + return postbox.transaction { transaction -> Signal in if let peer = transaction.getPeer(peerId) { if peerId == accountPeerId { diff --git a/submodules/TelegramCore/Sources/State/ManagedSynchronizeViewStoriesOperations.swift b/submodules/TelegramCore/Sources/State/ManagedSynchronizeViewStoriesOperations.swift index bebd6915..5f846fe6 100644 --- a/submodules/TelegramCore/Sources/State/ManagedSynchronizeViewStoriesOperations.swift +++ b/submodules/TelegramCore/Sources/State/ManagedSynchronizeViewStoriesOperations.swift @@ -119,6 +119,11 @@ func managedSynchronizeViewStoriesOperations(postbox: Postbox, network: Network, } private func pushStoriesAreSeen(postbox: Postbox, network: Network, stateManager: AccountStateManager, peer: Peer, operation: SynchronizeViewStoriesOperation) -> Signal { + // GHOST MODE: Don't send story view notifications + if GhostModeManager.shared.shouldHideStoryViews { + return .complete() + } + guard let inputPeer = apiInputPeer(peer) else { return .complete() } diff --git a/submodules/TelegramCore/Sources/State/PendingMessageManager.swift b/submodules/TelegramCore/Sources/State/PendingMessageManager.swift index cbdf0a3a..869b2f9d 100644 --- a/submodules/TelegramCore/Sources/State/PendingMessageManager.swift +++ b/submodules/TelegramCore/Sources/State/PendingMessageManager.swift @@ -2076,7 +2076,7 @@ public final class PendingMessageManager { if message.scheduleTime != nil && message.scheduleTime == apiMessage.timestamp { isScheduled = true } - if case let .message(_, flags2, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = apiMessage { + if case let .message(_, flags2, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = apiMessage { if (flags2 & (1 << 4)) != 0 { isScheduled = true } @@ -2120,7 +2120,7 @@ public final class PendingMessageManager { namespace = Namespaces.Message.QuickReplyCloud } else if let apiMessage = result.messages.first, message.scheduleTime != nil && message.scheduleTime == apiMessage.timestamp { namespace = Namespaces.Message.ScheduledCloud - } else if let apiMessage = result.messages.first, case let .message(_, flags2, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = apiMessage, (flags2 & (1 << 4)) != 0 { + } else if let apiMessage = result.messages.first, case let .message(_, flags2, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = apiMessage, (flags2 & (1 << 4)) != 0 { namespace = Namespaces.Message.ScheduledCloud } } diff --git a/submodules/TelegramCore/Sources/State/Serialization.swift b/submodules/TelegramCore/Sources/State/Serialization.swift index 0a868049..72cea0ce 100644 --- a/submodules/TelegramCore/Sources/State/Serialization.swift +++ b/submodules/TelegramCore/Sources/State/Serialization.swift @@ -210,7 +210,7 @@ public class BoxedMessage: NSObject { public class Serialization: NSObject, MTSerialization { public func currentLayer() -> UInt { - return 220 + return 221 } public func parseMessage(_ data: Data!) -> Any! { diff --git a/submodules/TelegramCore/Sources/State/SynchronizePeerReadState.swift b/submodules/TelegramCore/Sources/State/SynchronizePeerReadState.swift index 4bc06ce1..4e169d54 100644 --- a/submodules/TelegramCore/Sources/State/SynchronizePeerReadState.swift +++ b/submodules/TelegramCore/Sources/State/SynchronizePeerReadState.swift @@ -219,6 +219,11 @@ private func validatePeerReadState(network: Network, postbox: Postbox, stateMana } private func pushPeerReadState(network: Network, postbox: Postbox, stateManager: AccountStateManager, peerId: PeerId, readState: PeerReadState) -> Signal { + // GHOST MODE: Block read receipts (blue checkmarks) for non-secret chats + if peerId.namespace != Namespaces.Peer.SecretChat && GhostModeManager.shared.shouldHideReadReceipts { + return .single(readState) + } + if peerId.namespace == Namespaces.Peer.SecretChat { return inputSecretChat(postbox: postbox, peerId: peerId) |> mapToSignal { inputPeer -> Signal in diff --git a/submodules/TelegramCore/Sources/State/UpdateMessageService.swift b/submodules/TelegramCore/Sources/State/UpdateMessageService.swift index 716fffa5..655b2380 100644 --- a/submodules/TelegramCore/Sources/State/UpdateMessageService.swift +++ b/submodules/TelegramCore/Sources/State/UpdateMessageService.swift @@ -58,7 +58,7 @@ class UpdateMessageService: NSObject, MTMessageService { self.putNext(groups) } case let .updateShortChatMessage(flags, id, fromId, chatId, message, pts, ptsCount, date, fwdFrom, viaBotId, replyHeader, entities, ttlPeriod): - let generatedMessage = Api.Message.message(flags: flags, flags2: 0, id: id, fromId: .peerUser(userId: fromId), fromBoostsApplied: nil, peerId: Api.Peer.peerChat(chatId: chatId), savedPeerId: nil, fwdFrom: fwdFrom, viaBotId: viaBotId, viaBusinessBotId: nil, replyTo: replyHeader, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, reactions: nil, restrictionReason: nil, ttlPeriod: ttlPeriod, quickReplyShortcutId: nil, effect: nil, factcheck: nil, reportDeliveryUntilDate: nil, paidMessageStars: nil, suggestedPost: nil, scheduleRepeatPeriod: nil) + let generatedMessage = Api.Message.message(flags: flags, flags2: 0, id: id, fromId: .peerUser(userId: fromId), fromBoostsApplied: nil, peerId: Api.Peer.peerChat(chatId: chatId), savedPeerId: nil, fwdFrom: fwdFrom, viaBotId: viaBotId, viaBusinessBotId: nil, replyTo: replyHeader, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, reactions: nil, restrictionReason: nil, ttlPeriod: ttlPeriod, quickReplyShortcutId: nil, effect: nil, factcheck: nil, reportDeliveryUntilDate: nil, paidMessageStars: nil, suggestedPost: nil, scheduleRepeatPeriod: nil, summaryFromLanguage: nil) let update = Api.Update.updateNewMessage(message: generatedMessage, pts: pts, ptsCount: ptsCount) let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil) if groups.count != 0 { @@ -74,7 +74,7 @@ class UpdateMessageService: NSObject, MTMessageService { let generatedPeerId = Api.Peer.peerUser(userId: userId) - let generatedMessage = Api.Message.message(flags: flags, flags2: 0, id: id, fromId: generatedFromId, fromBoostsApplied: nil, peerId: generatedPeerId, savedPeerId: nil, fwdFrom: fwdFrom, viaBotId: viaBotId, viaBusinessBotId: nil, replyTo: replyHeader, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, reactions: nil, restrictionReason: nil, ttlPeriod: ttlPeriod, quickReplyShortcutId: nil, effect: nil, factcheck: nil, reportDeliveryUntilDate: nil, paidMessageStars: nil, suggestedPost: nil, scheduleRepeatPeriod: nil) + let generatedMessage = Api.Message.message(flags: flags, flags2: 0, id: id, fromId: generatedFromId, fromBoostsApplied: nil, peerId: generatedPeerId, savedPeerId: nil, fwdFrom: fwdFrom, viaBotId: viaBotId, viaBusinessBotId: nil, replyTo: replyHeader, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, replies: nil, editDate: nil, postAuthor: nil, groupedId: nil, reactions: nil, restrictionReason: nil, ttlPeriod: ttlPeriod, quickReplyShortcutId: nil, effect: nil, factcheck: nil, reportDeliveryUntilDate: nil, paidMessageStars: nil, suggestedPost: nil, scheduleRepeatPeriod: nil, summaryFromLanguage: nil) let update = Api.Update.updateNewMessage(message: generatedMessage, pts: pts, ptsCount: ptsCount) let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil) if groups.count != 0 { diff --git a/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift b/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift index 336b9c43..77f07157 100644 --- a/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift +++ b/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift @@ -104,7 +104,7 @@ extension Api.MessageMedia { extension Api.Message { var rawId: Int32 { switch self { - case let .message(_, _, id, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, id, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): return id case let .messageEmpty(_, id, _): return id @@ -115,7 +115,7 @@ extension Api.Message { func id(namespace: MessageId.Namespace = Namespaces.Message.Cloud) -> MessageId? { switch self { - case let .message(_, flags2, id, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, flags2, id, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): var namespace = namespace if (flags2 & (1 << 4)) != 0 { namespace = Namespaces.Message.ScheduledCloud @@ -136,7 +136,7 @@ extension Api.Message { var peerId: PeerId? { switch self { - case let .message(_, _, _, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, _, _, _, messagePeerId, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): let peerId: PeerId = messagePeerId.peerId return peerId case let .messageEmpty(_, _, peerId): @@ -149,7 +149,7 @@ extension Api.Message { var timestamp: Int32? { switch self { - case let .message(_, _, _, _, _, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, _, _, _, _, _, _, _, _, _, date, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): return date case let .messageService(_, _, _, _, _, _, date, _, _, _): return date @@ -160,7 +160,7 @@ extension Api.Message { var preCachedResources: [(MediaResource, Data)]? { switch self { - case let .message(_, _, _, _, _, _, _, _, _, _, _, _, _, media, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, _, _, _, _, _, _, _, _, _, _, _, media, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): return media?.preCachedResources default: return nil @@ -169,7 +169,7 @@ extension Api.Message { var preCachedStories: [StoryId: Api.StoryItem]? { switch self { - case let .message(_, _, _, _, _, _, _, _, _, _, _, _, _, media, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): + case let .message(_, _, _, _, _, _, _, _, _, _, _, _, _, media, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _): return media?.preCachedStories default: return nil diff --git a/submodules/TelegramCore/Sources/State/UserLimitsConfiguration.swift b/submodules/TelegramCore/Sources/State/UserLimitsConfiguration.swift index 7c1fc585..29255b7e 100644 --- a/submodules/TelegramCore/Sources/State/UserLimitsConfiguration.swift +++ b/submodules/TelegramCore/Sources/State/UserLimitsConfiguration.swift @@ -32,9 +32,10 @@ public struct UserLimitsConfiguration: Equatable { public static var defaultValue: UserLimitsConfiguration { return UserLimitsConfiguration( - maxPinnedChatCount: 5, - maxPinnedSavedChatCount: 5, - maxArchivedPinnedChatCount: 100, + // GHOSTGRAM: Unlimited pinned chats bypass + maxPinnedChatCount: 99, + maxPinnedSavedChatCount: 99, + maxArchivedPinnedChatCount: 99, maxChannelsCount: 500, maxPublicLinksCount: 10, maxSavedGifCount: 200, @@ -145,9 +146,10 @@ extension UserLimitsConfiguration { } } - self.maxPinnedChatCount = getValue("dialogs_pinned_limit", orElse: defaultValue.maxPinnedChatCount) - self.maxPinnedSavedChatCount = getValue("saved_dialogs_pinned_limit", orElse: defaultValue.maxPinnedSavedChatCount) - self.maxArchivedPinnedChatCount = getValue("dialogs_folder_pinned_limit", orElse: defaultValue.maxArchivedPinnedChatCount) + // GHOSTGRAM: Force unlimited pinned chats (ignore server limits) + self.maxPinnedChatCount = 99 + self.maxPinnedSavedChatCount = 99 + self.maxArchivedPinnedChatCount = 99 self.maxChannelsCount = getValue("channels_limit", orElse: defaultValue.maxChannelsCount) self.maxPublicLinksCount = getValue("channels_public_limit", orElse: defaultValue.maxPublicLinksCount) self.maxSavedGifCount = getValue("saved_gifs_limit", orElse: defaultValue.maxSavedGifCount) diff --git a/submodules/TelegramCore/Sources/SyncCore/SummarizationMessageAttribute.swift b/submodules/TelegramCore/Sources/SyncCore/SummarizationMessageAttribute.swift new file mode 100644 index 00000000..21fa9647 --- /dev/null +++ b/submodules/TelegramCore/Sources/SyncCore/SummarizationMessageAttribute.swift @@ -0,0 +1,87 @@ +import Foundation +import Postbox +import TelegramApi + +public final class SummarizationMessageAttribute: Equatable, MessageAttribute { + public struct Summary: Equatable, Codable, PostboxCoding { + public let text: String + public let entities: [MessageTextEntity] + + public init( + text: String, + entities: [MessageTextEntity] + ) { + self.text = text + self.entities = entities + } + + public init(decoder: PostboxDecoder) { + self.text = decoder.decodeStringForKey("text", orElse: "") + self.entities = decoder.decodeObjectArrayWithDecoderForKey("entities") + } + + public func encode(_ encoder: PostboxEncoder) { + encoder.encodeString(self.text, forKey: "text") + encoder.encodeObjectArray(self.entities, forKey: "entities") + } + } + + public let fromLang: String + public let summary: Summary? + public let translated: [String: Summary] + + public init( + fromLang: String, + summary: Summary? = nil, + translated: [String: Summary] = [:] + ) { + self.fromLang = fromLang + self.summary = summary + self.translated = translated + } + + required public init(decoder: PostboxDecoder) { + self.fromLang = decoder.decodeStringForKey("fl", orElse: "") + self.summary = decoder.decodeObjectForKey("s", decoder: { Summary(decoder: $0) }) as? Summary + self.translated = decoder.decodeObjectDictionaryForKey("t", keyDecoder: { decoder in + return decoder.decodeStringForKey("k", orElse: "") + }, valueDecoder: { decoder in + return Summary(decoder: decoder) + }) + } + + public func encode(_ encoder: PostboxEncoder) { + encoder.encodeString(self.fromLang, forKey: "fl") + if let summary = self.summary { + encoder.encodeObject(summary, forKey: "s") + } else { + encoder.encodeNil(forKey: "s") + } + encoder.encodeObjectDictionary(self.translated, forKey: "t", keyEncoder: { k, e in + e.encodeString(k, forKey: "k") + }) + } + + public static func ==(lhs: SummarizationMessageAttribute, rhs: SummarizationMessageAttribute) -> Bool { + if lhs.fromLang != rhs.fromLang { + return false + } + if lhs.summary != rhs.summary { + return false + } + if lhs.translated != rhs.translated { + return false + } + return true + } +} + +public extension SummarizationMessageAttribute { + func summaryForLang(_ lang: String?) -> Summary? { + if let lang { + return self.translated[lang] + } else { + return self.summary + } + } +} diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_AutoremoveTimeoutMessageAttribute.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_AutoremoveTimeoutMessageAttribute.swift index 1eab2f65..b844a933 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_AutoremoveTimeoutMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_AutoremoveTimeoutMessageAttribute.swift @@ -111,6 +111,11 @@ public extension Message { } var minAutoremoveOrClearTimeout: Int32? { + // MISC: Bypass if view-once setting is enabled + if MiscSettingsManager.shared.shouldDisableViewOnceAutoDelete { + return nil + } + var timeout: Int32? for attribute in self.attributes { if let attribute = attribute as? AutoremoveTimeoutMessageAttribute { @@ -137,6 +142,11 @@ public extension Message { } var containsSecretMedia: Bool { + // MISC: Bypass if view-once setting is enabled + if MiscSettingsManager.shared.shouldDisableViewOnceAutoDelete { + return false + } + guard let timeout = self.minAutoremoveOrClearTimeout else { return false } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift index b6a82d2f..a6898bfb 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift @@ -323,6 +323,7 @@ private enum PreferencesKeyValues: Int32 { case persistentChatInterfaceData = 45 case globalPostSearchState = 46 case savedMusicIds = 47 + case emojiGameInfo = 48 } public func applicationSpecificPreferencesKey(_ value: Int32) -> ValueBoxKey { @@ -591,6 +592,12 @@ public struct PreferencesKeys { key.setInt32(0, value: PreferencesKeyValues.savedMusicIds.rawValue) return key } + + public static func emojiGameInfo() -> ValueBoxKey { + let key = ValueBoxKey(length: 4) + key.setInt32(0, value: PreferencesKeyValues.emojiGameInfo.rawValue) + return key + } } private enum SharedDataKeyValues: Int32 { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatAdminRights.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatAdminRights.swift index 733b5f92..5d270bf5 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatAdminRights.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramChatAdminRights.swift @@ -62,7 +62,8 @@ public struct TelegramChatAdminRightsFlags: OptionSet, Hashable { .canPostStories, .canEditStories, .canDeleteStories, - .canManageDirect + .canManageDirect, + .canBanUsers ] public static func peerSpecific(peer: EnginePeer) -> TelegramChatAdminRightsFlags { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaDice.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaDice.swift index d142f4d8..c6bdf9d4 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaDice.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaDice.swift @@ -1,29 +1,57 @@ +import Foundation import Postbox public final class TelegramMediaDice: Media, Equatable { + public struct GameOutcome: Equatable { + let seed: Data + public let tonAmount: Int64 + } + public let emoji: String + public let tonAmount: Int64? public let value: Int32? + public let gameOutcome: GameOutcome? public let id: MediaId? = nil public let peerIds: [PeerId] = [] - public init(emoji: String, value: Int32? = nil) { + public init(emoji: String, tonAmount: Int64? = nil, value: Int32? = nil, gameOutcome: GameOutcome? = nil) { self.emoji = emoji + self.tonAmount = tonAmount self.value = value + self.gameOutcome = gameOutcome } public init(decoder: PostboxDecoder) { self.emoji = decoder.decodeStringForKey("e", orElse: "🎲") + self.tonAmount = decoder.decodeOptionalInt64ForKey("ta") self.value = decoder.decodeOptionalInt32ForKey("v") + if let seed = decoder.decodeDataForKey("gos"), let tonAmount = decoder.decodeOptionalInt64ForKey("goa") { + self.gameOutcome = GameOutcome(seed: seed, tonAmount: tonAmount) + } else { + self.gameOutcome = nil + } } public func encode(_ encoder: PostboxEncoder) { encoder.encodeString(self.emoji, forKey: "e") + if let tonAmount = self.tonAmount { + encoder.encodeInt64(tonAmount, forKey: "ta") + } else { + encoder.encodeNil(forKey: "ta") + } if let value = self.value { encoder.encodeInt32(value, forKey: "v") } else { encoder.encodeNil(forKey: "v") } + if let gameOutcome = self.gameOutcome { + encoder.encodeData(gameOutcome.seed, forKey: "gos") + encoder.encodeInt64(gameOutcome.tonAmount, forKey: "goa") + } else { + encoder.encodeNil(forKey: "gos") + encoder.encodeNil(forKey: "goa") + } } public static func ==(lhs: TelegramMediaDice, rhs: TelegramMediaDice) -> Bool { @@ -35,9 +63,15 @@ public final class TelegramMediaDice: Media, Equatable { if self.emoji != other.emoji { return false } + if self.tonAmount != other.tonAmount { + return false + } if self.value != other.value { return false } + if self.gameOutcome != other.gameOutcome { + return false + } return true } return false diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Data/ConfigurationData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Data/ConfigurationData.swift index e9d24a6c..3724ff8b 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Data/ConfigurationData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Data/ConfigurationData.swift @@ -571,5 +571,26 @@ public extension TelegramEngine.EngineData.Item { return value } } + + public struct EmojiGame: TelegramEngineDataItem, PostboxViewDataItem { + public typealias Result = EmojiGameInfo + + public init() { + } + + var key: PostboxViewKey { + return .preferences(keys: Set([PreferencesKeys.emojiGameInfo()])) + } + + func extract(view: PostboxView) -> Result { + guard let view = view as? PreferencesView else { + preconditionFailure() + } + guard let emojiGameInfo = view.values[PreferencesKeys.emojiGameInfo()]?.get(EmojiGameInfo.self) else { + return .unavailable + } + return emojiGameInfo + } + } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift index a60886ac..69d51898 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift @@ -482,6 +482,12 @@ private class AdMessagesHistoryContextImpl { } self.isActivated = true + // MISC: Block ads if setting enabled + if MiscSettingsManager.shared.shouldBlockAds { + self.stateValue = State(interPostInterval: nil, startDelay: nil, betweenDelay: nil, messages: []) + return + } + let peerId = self.peerId let accountPeerId = self.account.peerId let account = self.account diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift index 7b6a8bac..1ade75ef 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ApplyMaxReadIndexInteractively.swift @@ -64,7 +64,10 @@ func _internal_applyMaxReadIndexInteractively(transaction: Transaction, stateMan } } } else if index.id.peerId.namespace == Namespaces.Peer.CloudUser || index.id.peerId.namespace == Namespaces.Peer.CloudGroup || index.id.peerId.namespace == Namespaces.Peer.CloudChannel { - stateManager.notifyAppliedIncomingReadMessages([index.id]) + // GHOST MODE: Don't send read receipts (blue checkmarks) + if !GhostModeManager.shared.shouldHideReadReceipts { + stateManager.notifyAppliedIncomingReadMessages([index.id]) + } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/MarkMessageContentAsConsumedInteractively.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/MarkMessageContentAsConsumedInteractively.swift index 57d89f8f..be90d28f 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/MarkMessageContentAsConsumedInteractively.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/MarkMessageContentAsConsumedInteractively.swift @@ -51,6 +51,11 @@ func _internal_markMessageContentAsConsumedInteractively(postbox: Postbox, messa for i in 0 ..< updatedAttributes.count { if let attribute = updatedAttributes[i] as? AutoremoveTimeoutMessageAttribute { if attribute.countdownBeginTime == nil || attribute.countdownBeginTime == 0 { + // MISC: Don't start countdown for view-once if bypass enabled + if attribute.timeout == viewOnceTimeout && MiscSettingsManager.shared.shouldDisableViewOnceAutoDelete { + continue + } + var timeout = attribute.timeout if let duration = message.secretMediaDuration { timeout = max(timeout, Int32(duration)) @@ -194,7 +199,9 @@ func markMessageContentAsConsumedRemotely(transaction: Transaction, messageId: M if message.id.peerId.namespace == Namespaces.Peer.SecretChat { } else { - if attribute.timeout == viewOnceTimeout || timestamp >= countdownBeginTime + attribute.timeout { + // MISC: Don't expire view-once media if bypass enabled + let shouldExpire = !(attribute.timeout == viewOnceTimeout && MiscSettingsManager.shared.shouldDisableViewOnceAutoDelete) + if shouldExpire && (attribute.timeout == viewOnceTimeout || timestamp >= countdownBeginTime + attribute.timeout) { for i in 0 ..< updatedMedia.count { if let _ = updatedMedia[i] as? TelegramMediaImage { updatedMedia[i] = TelegramMediaExpiredContent(data: .image) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Summarize.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Summarize.swift new file mode 100644 index 00000000..ab55533e --- /dev/null +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Summarize.swift @@ -0,0 +1,83 @@ +import Foundation +import Postbox +import SwiftSignalKit +import TelegramApi +import MtProtoKit + +public enum SummarizeError { + case generic + case invalidMessageId + case limitExceeded + case invalidLanguage + case limitExceededPremium +} + +func _internal_summarizeMessage(account: Account, messageId: EngineMessage.Id, translateToLang: String?) -> Signal { + return account.postbox.transaction { transaction -> Api.InputPeer? in + return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer) + } + |> castError(SummarizeError.self) + |> mapToSignal { inputPeer -> Signal in + guard let inputPeer else { + return .never() + } + + var flags: Int32 = 0 + if let _ = translateToLang { + flags |= (1 << 0) + } + + return account.network.request(Api.functions.messages.summarizeText(flags: flags, peer: inputPeer, id: messageId.id, toLang: translateToLang)) + |> map(Optional.init) + |> mapError { error -> SummarizeError in + if error.errorDescription.hasPrefix("FLOOD_WAIT") { + return .limitExceeded + } else if error.errorDescription == "MSG_ID_INVALID" { + return .invalidMessageId + } else if error.errorDescription == "TO_LANG_INVALID" { + return .invalidLanguage + } else if error.errorDescription == "SUMMARY_FLOOD_PREMIUM" { + return .limitExceededPremium + } else { + return .generic + } + } + |> mapToSignal { result -> Signal in + return account.postbox.transaction { transaction in + switch result { + case let .textWithEntities(text, entities): + transaction.updateMessage(messageId, update: { currentMessage in + let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init) + var attributes = currentMessage.attributes + + let currentAttribute = attributes.first(where: { $0 is SummarizationMessageAttribute }) as? SummarizationMessageAttribute + let updatedAttribute: SummarizationMessageAttribute + if let translateToLang { + var translated = currentAttribute?.translated ?? [:] + translated[translateToLang] = SummarizationMessageAttribute.Summary(text: text, entities: messageTextEntitiesFromApiEntities(entities)) + updatedAttribute = SummarizationMessageAttribute( + fromLang: currentAttribute?.fromLang ?? "", + summary: currentAttribute?.summary, + translated: translated + ) + } else { + updatedAttribute = SummarizationMessageAttribute( + fromLang: currentAttribute?.fromLang ?? "", + summary: .init(text: text, entities: messageTextEntitiesFromApiEntities(entities)), + translated: currentAttribute?.translated ?? [:] + ) + } + attributes = attributes.filter { !($0 is SummarizationMessageAttribute) } + attributes.append(updatedAttribute) + + return .update(StoreMessage(id: currentMessage.id, customStableId: nil, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + }) + default: + break + } + } + |> castError(SummarizeError.self) + } + |> ignoreValues + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index 9133c0a5..143106a1 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -604,6 +604,10 @@ public extension TelegramEngine { return _internal_togglePeerMessagesTranslationHidden(account: self.account, peerId: peerId, hidden: hidden) } + public func summarizeMessage(messageId: EngineMessage.Id, translateToLang: String?) -> Signal { + return _internal_summarizeMessage(account: self.account, messageId: messageId, translateToLang: translateToLang) + } + public func transcribeAudio(messageId: MessageId) -> Signal { return _internal_transcribeAudio(postbox: self.account.postbox, network: self.account.network, messageId: messageId) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift index d3dd832a..95e29856 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift @@ -1578,7 +1578,8 @@ func _internal_upgradeStarGift(account: Account, formId: Int64?, reference: Star prepaidUpgradeHash: nil, upgradeSeparate: false, dropOriginalDetailsStars: dropOriginalDetailsStars, - number: nil + number: nil, + isRefunded: false )) } } @@ -2533,6 +2534,7 @@ public final class ProfileGiftsContext { case upgradeSeparate case dropOriginalDetailsStars case number + case isRefunded } public let gift: TelegramCore.StarGift @@ -2556,7 +2558,8 @@ public final class ProfileGiftsContext { public let upgradeSeparate: Bool public let dropOriginalDetailsStars: Int64? public let number: Int32? - + public let isRefunded: Bool + fileprivate let _fromPeerId: EnginePeer.Id? public enum DecodingError: Error { @@ -2584,7 +2587,8 @@ public final class ProfileGiftsContext { prepaidUpgradeHash: String?, upgradeSeparate: Bool, dropOriginalDetailsStars: Int64?, - number: Int32? + number: Int32?, + isRefunded: Bool ) { self.gift = gift self.reference = reference @@ -2608,6 +2612,7 @@ public final class ProfileGiftsContext { self.upgradeSeparate = upgradeSeparate self.dropOriginalDetailsStars = dropOriginalDetailsStars self.number = number + self.isRefunded = isRefunded } public init(from decoder: Decoder) throws { @@ -2641,6 +2646,7 @@ public final class ProfileGiftsContext { self.upgradeSeparate = try container.decodeIfPresent(Bool.self, forKey: .upgradeSeparate) ?? false self.dropOriginalDetailsStars = try container.decodeIfPresent(Int64.self, forKey: .dropOriginalDetailsStars) self.number = try container.decodeIfPresent(Int32.self, forKey: .number) + self.isRefunded = try container.decodeIfPresent(Bool.self, forKey: .isRefunded) ?? false } public func encode(to encoder: Encoder) throws { @@ -2667,6 +2673,8 @@ public final class ProfileGiftsContext { try container.encode(self.upgradeSeparate, forKey: .upgradeSeparate) try container.encodeIfPresent(self.dropOriginalDetailsStars, forKey: .dropOriginalDetailsStars) try container.encodeIfPresent(self.number, forKey: .number) + try container.encode(self.isRefunded, forKey: .isRefunded) + } public func withGift(_ gift: TelegramCore.StarGift) -> StarGift { @@ -2691,7 +2699,8 @@ public final class ProfileGiftsContext { prepaidUpgradeHash: self.prepaidUpgradeHash, upgradeSeparate: self.upgradeSeparate, dropOriginalDetailsStars: self.dropOriginalDetailsStars, - number: self.number + number: self.number, + isRefunded: self.isRefunded ) } @@ -2717,7 +2726,8 @@ public final class ProfileGiftsContext { prepaidUpgradeHash: self.prepaidUpgradeHash, upgradeSeparate: self.upgradeSeparate, dropOriginalDetailsStars: self.dropOriginalDetailsStars, - number: self.number + number: self.number, + isRefunded: self.isRefunded ) } @@ -2743,7 +2753,8 @@ public final class ProfileGiftsContext { prepaidUpgradeHash: self.prepaidUpgradeHash, upgradeSeparate: self.upgradeSeparate, dropOriginalDetailsStars: self.dropOriginalDetailsStars, - number: self.number + number: self.number, + isRefunded: self.isRefunded ) } fileprivate func withFromPeer(_ fromPeer: EnginePeer?) -> StarGift { @@ -2768,7 +2779,8 @@ public final class ProfileGiftsContext { prepaidUpgradeHash: self.prepaidUpgradeHash, upgradeSeparate: self.upgradeSeparate, dropOriginalDetailsStars: self.dropOriginalDetailsStars, - number: self.number + number: self.number, + isRefunded: self.isRefunded ) } @@ -2794,7 +2806,8 @@ public final class ProfileGiftsContext { prepaidUpgradeHash: self.prepaidUpgradeHash, upgradeSeparate: self.upgradeSeparate, dropOriginalDetailsStars: self.dropOriginalDetailsStars, - number: self.number + number: self.number, + isRefunded: self.isRefunded ) } } @@ -3063,6 +3076,7 @@ extension ProfileGiftsContext.State.StarGift { self.upgradeSeparate = (flags & (1 << 17)) != 0 self.dropOriginalDetailsStars = dropOriginalDetailsStars self.number = number + self.isRefunded = (flags & (1 << 9)) != 0 } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGiftsAuctions.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGiftsAuctions.swift index 10ed47ea..a3652a45 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGiftsAuctions.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGiftsAuctions.swift @@ -580,7 +580,7 @@ public class GiftAuctionsManager { } public extension GiftAuctionContext.State { - func getPlace(myBid: Int64?, myBidDate: Int32?) -> Int32? { + func getPlace(myBid: Int64?, myBidDate: Int32?) -> (place: Int32, isApproximate: Bool)? { guard case let .ongoing(_, _, _, _, bidLevels, _, _, _, _, _, _, _) = self.auctionState else { return nil } @@ -592,7 +592,7 @@ public extension GiftAuctionContext.State { let levels = bidLevels guard !levels.isEmpty else { - return 1 + return (1, false) } func isWorse(than level: GiftAuctionContext.State.BidLevel) -> Bool { @@ -614,7 +614,7 @@ public extension GiftAuctionContext.State { } } if lowerIndex == -1 { - return 1 + return (1, false) } let lowerPosition = levels[lowerIndex].position @@ -626,14 +626,14 @@ public extension GiftAuctionContext.State { nextPosition = lowerPosition } if nextPosition == lowerPosition + 1 { - return lowerPosition + 1 + return (lowerPosition + 1, false) } else { - return nextPosition + return (lowerPosition, true) } } var place: Int32? { - return self.getPlace(myBid: nil, myBidDate: nil) + return self.getPlace(myBid: nil, myBidDate: nil)?.place } var startDate: Int32 { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift index 10146634..b51aebda 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/Stars.swift @@ -1644,7 +1644,7 @@ func _internal_sendStarsPaymentForm(account: Account, formId: Int64, source: Bot case .giftCode, .stars, .starsGift, .starsChatSubscription, .starGift, .starGiftUpgrade, .starGiftTransfer, .premiumGift, .starGiftResale, .starGiftPrepaidUpgrade, .starGiftDropOriginalDetails, .starGiftAuctionBid: receiptMessageId = nil } - } else if case let .starGiftUnique(gift, _, _, savedToProfile, canExportDate, transferStars, _, _, peerId, _, savedId, _, canTransferDate, canResaleDate, dropOriginalDetailsStars, _, _) = action.action, case let .Id(messageId) = message.id { + } else if case let .starGiftUnique(gift, _, _, savedToProfile, canExportDate, transferStars, isRefunded, _, peerId, _, savedId, _, canTransferDate, canResaleDate, dropOriginalDetailsStars, _, _) = action.action, case let .Id(messageId) = message.id { let reference: StarGiftReference if let peerId, let savedId { reference = .peer(peerId: peerId, id: savedId) @@ -1672,7 +1672,8 @@ func _internal_sendStarsPaymentForm(account: Account, formId: Int64, source: Bot prepaidUpgradeHash: nil, upgradeSeparate: false, dropOriginalDetailsStars: dropOriginalDetailsStars, - number: nil + number: nil, + isRefunded: isRefunded ) } } diff --git a/submodules/TelegramCore/Sources/Utils/MessageUtils.swift b/submodules/TelegramCore/Sources/Utils/MessageUtils.swift index 393234c0..8254b733 100644 --- a/submodules/TelegramCore/Sources/Utils/MessageUtils.swift +++ b/submodules/TelegramCore/Sources/Utils/MessageUtils.swift @@ -380,6 +380,11 @@ public extension Message { } func isCopyProtected() -> Bool { + // MISC: Bypass copy protection if enabled + if MiscSettingsManager.shared.shouldBypassCopyProtection { + return false + } + if self.flags.contains(.CopyProtected) { return true } else if let group = self.peers[self.id.peerId] as? TelegramGroup, group.flags.contains(.copyProtectionEnabled) { diff --git a/submodules/TelegramNotices/Sources/Notices.swift b/submodules/TelegramNotices/Sources/Notices.swift index b1e4e9ac..07340336 100644 --- a/submodules/TelegramNotices/Sources/Notices.swift +++ b/submodules/TelegramNotices/Sources/Notices.swift @@ -193,12 +193,8 @@ private enum ApplicationSpecificGlobalNotice: Int32 { case incomingVideoMessagePlayOnceTip = 62 case outgoingVideoMessagePlayOnceTip = 63 case savedMessageTagLabelSuggestion = 65 - case dismissedBusinessBadge = 68 case monetizationIntroDismissed = 70 case businessBotMessageTooltip = 71 - case dismissedBusinessIntroBadge = 72 - case dismissedBusinessLinksBadge = 73 - case dismissedBusinessChatbotsBadge = 74 case captionAboveMediaTooltip = 75 case channelSendGiftTooltip = 76 case starGiftWearTips = 77 @@ -519,10 +515,6 @@ private struct ApplicationSpecificNoticeKeys { return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.savedMessageTagLabelSuggestion.key) } - static func dismissedBusinessBadge() -> NoticeEntryKey { - return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.dismissedBusinessBadge.key) - } - static func dismissedBirthdayPremiumGiftTip(peerId: PeerId) -> NoticeEntryKey { return NoticeEntryKey(namespace: noticeNamespace(namespace: dismissedBirthdayPremiumGiftTipNamespace), key: noticeKey(peerId: peerId, key: 0)) } @@ -543,18 +535,6 @@ private struct ApplicationSpecificNoticeKeys { return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.businessBotMessageTooltip.key) } - static func dismissedBusinessIntroBadge() -> NoticeEntryKey { - return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.dismissedBusinessIntroBadge.key) - } - - static func dismissedBusinessLinksBadge() -> NoticeEntryKey { - return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.dismissedBusinessLinksBadge.key) - } - - static func dismissedBusinessChatbotsBadge() -> NoticeEntryKey { - return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.dismissedBusinessChatbotsBadge.key) - } - static func captionAboveMediaTooltip() -> NoticeEntryKey { return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.captionAboveMediaTooltip.key) } @@ -2154,27 +2134,6 @@ public struct ApplicationSpecificNotice { return Int(previousValue) } } - - public static func setDismissedBusinessBadge(accountManager: AccountManager) -> Signal { - return accountManager.transaction { transaction -> Void in - if let entry = CodableEntry(ApplicationSpecificBoolNotice()) { - transaction.setNotice(ApplicationSpecificNoticeKeys.dismissedBusinessBadge(), entry) - } - } - |> ignoreValues - } - - public static func dismissedBusinessBadge(accountManager: AccountManager) -> Signal { - return accountManager.noticeEntry(key: ApplicationSpecificNoticeKeys.dismissedBusinessBadge()) - |> map { view -> Bool in - if let _ = view.value?.get(ApplicationSpecificBoolNotice.self) { - return true - } else { - return false - } - } - |> take(1) - } public static func dismissedBirthdayPremiumGiftTip(accountManager: AccountManager, peerId: PeerId) -> Signal { return accountManager.noticeEntry(key: ApplicationSpecificNoticeKeys.dismissedBirthdayPremiumGiftTip(peerId: peerId)) @@ -2286,69 +2245,6 @@ public struct ApplicationSpecificNotice { } } - public static func setDismissedBusinessLinksBadge(accountManager: AccountManager) -> Signal { - return accountManager.transaction { transaction -> Void in - if let entry = CodableEntry(ApplicationSpecificBoolNotice()) { - transaction.setNotice(ApplicationSpecificNoticeKeys.dismissedBusinessLinksBadge(), entry) - } - } - |> ignoreValues - } - - public static func dismissedBusinessLinksBadge(accountManager: AccountManager) -> Signal { - return accountManager.noticeEntry(key: ApplicationSpecificNoticeKeys.dismissedBusinessLinksBadge()) - |> map { view -> Bool in - if let _ = view.value?.get(ApplicationSpecificBoolNotice.self) { - return true - } else { - return false - } - } - |> take(1) - } - - public static func setDismissedBusinessIntroBadge(accountManager: AccountManager) -> Signal { - return accountManager.transaction { transaction -> Void in - if let entry = CodableEntry(ApplicationSpecificBoolNotice()) { - transaction.setNotice(ApplicationSpecificNoticeKeys.dismissedBusinessIntroBadge(), entry) - } - } - |> ignoreValues - } - - public static func dismissedBusinessIntroBadge(accountManager: AccountManager) -> Signal { - return accountManager.noticeEntry(key: ApplicationSpecificNoticeKeys.dismissedBusinessIntroBadge()) - |> map { view -> Bool in - if let _ = view.value?.get(ApplicationSpecificBoolNotice.self) { - return true - } else { - return false - } - } - |> take(1) - } - - public static func setDismissedBusinessChatbotsBadge(accountManager: AccountManager) -> Signal { - return accountManager.transaction { transaction -> Void in - if let entry = CodableEntry(ApplicationSpecificBoolNotice()) { - transaction.setNotice(ApplicationSpecificNoticeKeys.dismissedBusinessChatbotsBadge(), entry) - } - } - |> ignoreValues - } - - public static func dismissedBusinessChatbotsBadge(accountManager: AccountManager) -> Signal { - return accountManager.noticeEntry(key: ApplicationSpecificNoticeKeys.dismissedBusinessChatbotsBadge()) - |> map { view -> Bool in - if let _ = view.value?.get(ApplicationSpecificBoolNotice.self) { - return true - } else { - return false - } - } - |> take(1) - } - public static func getCaptionAboveMediaTooltip(accountManager: AccountManager) -> Signal { return accountManager.transaction { transaction -> Int32 in if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.captionAboveMediaTooltip())?.get(ApplicationSpecificCounterNotice.self) { diff --git a/submodules/TelegramPermissionsUI/Sources/PermissionContentNode.swift b/submodules/TelegramPermissionsUI/Sources/PermissionContentNode.swift index 04a7a160..98433348 100644 --- a/submodules/TelegramPermissionsUI/Sources/PermissionContentNode.swift +++ b/submodules/TelegramPermissionsUI/Sources/PermissionContentNode.swift @@ -107,7 +107,7 @@ public final class PermissionContentNode: ASDisplayNode { self.textNode.displaysAsynchronously = false self.textNode.isAccessibilityElement = true - self.actionButton = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(theme: theme), height: 52.0, cornerRadius: 9.0, isShimmering: true) + self.actionButton = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(theme: theme), glass: true, height: 52.0, cornerRadius: 26.0, isShimmering: true) self.footerNode = ImmediateTextNode() self.footerNode.textAlignment = .center diff --git a/submodules/TelegramPermissionsUI/Sources/PermissionController.swift b/submodules/TelegramPermissionsUI/Sources/PermissionController.swift index 9589dcc7..d0c2f053 100644 --- a/submodules/TelegramPermissionsUI/Sources/PermissionController.swift +++ b/submodules/TelegramPermissionsUI/Sources/PermissionController.swift @@ -36,15 +36,17 @@ public final class PermissionController: ViewController { self.presentationData = context.sharedContext.currentPresentationData.with { $0 } self.splashScreen = splashScreen - let navigationBarPresentationData: NavigationBarPresentationData - if splashScreen { - navigationBarPresentationData = NavigationBarPresentationData(theme: NavigationBarTheme(buttonColor: self.presentationData.theme.rootController.navigationBar.accentTextColor, disabledButtonColor: self.presentationData.theme.rootController.navigationBar.disabledButtonColor, primaryTextColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)) - } else { - navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData) - } +// let navigationBarPresentationData: NavigationBarPresentationData +// if splashScreen { +// navigationBarPresentationData = NavigationBarPresentationData(theme: NavigationBarTheme(overallDarkAppearance: self.presentationData.theme.overallDarkAppearance, buttonColor: self.presentationData.theme.rootController.navigationBar.accentTextColor, disabledButtonColor: self.presentationData.theme.rootController.navigationBar.disabledButtonColor, primaryTextColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear, style: .glass), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)) +// } else { + let navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData, style: .glass) +// } super.init(navigationBarPresentationData: navigationBarPresentationData) + self._hasGlassStyle = true + self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) self.updateThemeAndStrings() @@ -86,15 +88,15 @@ public final class PermissionController: ViewController { private func updateThemeAndStrings() { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - let navigationBarPresentationData: NavigationBarPresentationData - if self.splashScreen { - navigationBarPresentationData = NavigationBarPresentationData(theme: NavigationBarTheme(buttonColor: self.presentationData.theme.rootController.navigationBar.accentTextColor, disabledButtonColor: self.presentationData.theme.rootController.navigationBar.disabledButtonColor, primaryTextColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)) - } else { - navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData) - } +// let navigationBarPresentationData: NavigationBarPresentationData +// if self.splashScreen { +// navigationBarPresentationData = NavigationBarPresentationData(theme: NavigationBarTheme(overallDarkAppearance: self.presentationData.theme.overallDarkAppearance, buttonColor: self.presentationData.theme.rootController.navigationBar.accentTextColor, disabledButtonColor: self.presentationData.theme.rootController.navigationBar.disabledButtonColor, primaryTextColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)) +// } else { + let navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData, style: .glass) +// } - self.navigationBar?.updatePresentationData(navigationBarPresentationData) - self.navigationItem.backBarButtonItem = UIBarButtonItem(title: nil, style: .plain, target: nil, action: nil) + self.navigationBar?.updatePresentationData(navigationBarPresentationData, transition: .immediate) + //self.navigationItem.backBarButtonItem = UIBarButtonItem(title: nil, style: .plain, target: nil, action: nil) if self.navigationItem.rightBarButtonItem != nil { self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Permissions_Skip, style: .plain, target: self, action: #selector(PermissionController.nextPressed)) } diff --git a/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift b/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift index 3b76534e..3e60f0ff 100644 --- a/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift +++ b/submodules/TelegramPresentationData/Sources/ComponentsThemes.swift @@ -46,9 +46,30 @@ public extension ToolbarTheme { } public extension NavigationBarTheme { - convenience init(rootControllerTheme: PresentationTheme, enableBackgroundBlur: Bool = true, hideBackground: Bool = false, hideBadge: Bool = false, hideSeparator: Bool = false) { + convenience init(rootControllerTheme: PresentationTheme, enableBackgroundBlur: Bool = true, hideBackground: Bool = false, hideBadge: Bool = false, hideSeparator: Bool = false, edgeEffectColor: UIColor? = nil, style: NavigationBar.Style = .legacy) { let theme = rootControllerTheme.rootController.navigationBar - self.init(buttonColor: theme.buttonColor, disabledButtonColor: theme.disabledButtonColor, primaryTextColor: theme.primaryTextColor, backgroundColor: hideBackground ? .clear : theme.blurredBackgroundColor, opaqueBackgroundColor: hideBackground ? .clear : theme.opaqueBackgroundColor, enableBackgroundBlur: enableBackgroundBlur, separatorColor: hideBackground || hideSeparator ? .clear : theme.separatorColor, badgeBackgroundColor: hideBadge ? .clear : theme.badgeBackgroundColor, badgeStrokeColor: hideBadge ? .clear : theme.badgeStrokeColor, badgeTextColor: hideBadge ? .clear : theme.badgeTextColor) + + let buttonColor: UIColor + let disabledButtonColor: UIColor + let badgeBackgroundColor: UIColor + let badgeTextColor: UIColor + var edgeEffectColor = edgeEffectColor + if case .glass = style { + buttonColor = rootControllerTheme.chat.inputPanel.panelControlColor + disabledButtonColor = buttonColor.withMultipliedAlpha(0.5) + badgeBackgroundColor = rootControllerTheme.chat.inputPanel.panelControlColor + badgeTextColor = rootControllerTheme.overallDarkAppearance ? .black : rootControllerTheme.list.itemCheckColors.foregroundColor + if edgeEffectColor == nil { + edgeEffectColor = rootControllerTheme.list.plainBackgroundColor + } + } else { + buttonColor = theme.buttonColor + disabledButtonColor = theme.disabledButtonColor + badgeBackgroundColor = theme.badgeBackgroundColor + badgeTextColor = theme.badgeTextColor + } + + self.init(overallDarkAppearance: rootControllerTheme.overallDarkAppearance, buttonColor: buttonColor, disabledButtonColor: disabledButtonColor, primaryTextColor: theme.primaryTextColor, backgroundColor: hideBackground ? .clear : theme.blurredBackgroundColor, opaqueBackgroundColor: hideBackground ? .clear : theme.opaqueBackgroundColor, enableBackgroundBlur: enableBackgroundBlur, separatorColor: hideBackground || hideSeparator ? .clear : theme.separatorColor, badgeBackgroundColor: hideBadge ? .clear : badgeBackgroundColor, badgeStrokeColor: .clear, badgeTextColor: hideBadge ? .clear : badgeTextColor, edgeEffectColor: edgeEffectColor, style: style) } } @@ -59,16 +80,16 @@ public extension NavigationBarStrings { } public extension NavigationBarPresentationData { - convenience init(presentationData: PresentationData) { - self.init(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme), strings: NavigationBarStrings(presentationStrings: presentationData.strings)) + convenience init(presentationData: PresentationData, style: NavigationBar.Style = .legacy) { + self.init(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme, style: style), strings: NavigationBarStrings(presentationStrings: presentationData.strings)) } - convenience init(presentationData: PresentationData, hideBackground: Bool, hideBadge: Bool, hideSeparator: Bool = false) { - self.init(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme, hideBackground: hideBackground, hideBadge: hideBadge, hideSeparator: hideSeparator), strings: NavigationBarStrings(presentationStrings: presentationData.strings)) + convenience init(presentationData: PresentationData, hideBackground: Bool, hideBadge: Bool, hideSeparator: Bool = false, style: NavigationBar.Style = .legacy) { + self.init(theme: NavigationBarTheme(rootControllerTheme: presentationData.theme, hideBackground: hideBackground, hideBadge: hideBadge, hideSeparator: hideSeparator, edgeEffectColor: hideBackground ? .clear : nil, style: style), strings: NavigationBarStrings(presentationStrings: presentationData.strings)) } - convenience init(presentationTheme: PresentationTheme, presentationStrings: PresentationStrings) { - self.init(theme: NavigationBarTheme(rootControllerTheme: presentationTheme), strings: NavigationBarStrings(presentationStrings: presentationStrings)) + convenience init(presentationTheme: PresentationTheme, presentationStrings: PresentationStrings, style: NavigationBar.Style = .legacy) { + self.init(theme: NavigationBarTheme(rootControllerTheme: presentationTheme, style: style), strings: NavigationBarStrings(presentationStrings: presentationStrings)) } } diff --git a/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift index 82018273..5ded69e4 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift @@ -371,7 +371,7 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati let navigationSearchBar = PresentationThemeNavigationSearchBar( backgroundColor: UIColor(rgb: 0x1c1c1d), accentColor: UIColor(rgb: 0xffffff), - inputFillColor: UIColor(rgb: 0x0f0f0f), + inputFillColor: UIColor(white: 1.0, alpha: 0.1), inputTextColor: UIColor(rgb: 0xffffff), inputPlaceholderTextColor: UIColor(rgb: 0x8f8f8f), inputIconColor: UIColor(rgb: 0x8f8f8f), @@ -504,8 +504,8 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati pinnedBadgeColor: UIColor(rgb: 0x767677), pinnedSearchBarColor: UIColor(rgb: 0x272728), regularSearchBarColor: UIColor(rgb: 0x272728), - sectionHeaderFillColor: UIColor(rgb: 0x1c1c1d), - sectionHeaderTextColor: UIColor(rgb: 0xffffff), + sectionHeaderFillColor: .black, + sectionHeaderTextColor: UIColor(rgb: 0x8d8e93), verifiedIconFillColor: UIColor(rgb: 0xffffff), verifiedIconForegroundColor: UIColor(rgb: 0x000000), secretIconColor: UIColor(rgb: 0x00b12c), diff --git a/submodules/TelegramPresentationData/Sources/DefaultDarkTintedPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDarkTintedPresentationTheme.swift index 86c3dad6..0fc5c681 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDarkTintedPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDarkTintedPresentationTheme.swift @@ -145,7 +145,7 @@ public func customizeDefaultDarkTintedPresentationTheme(theme: PresentationTheme navigationSearchBar: rootController.navigationSearchBar.withUpdated( backgroundColor: mainBackgroundColor, accentColor: accentColor, - inputFillColor: mainInputColor, + inputFillColor: UIColor(white: 1.0, alpha: 0.1), inputPlaceholderTextColor: mainSecondaryColor, inputIconColor: mainSecondaryColor, inputClearButtonColor: mainSecondaryColor, @@ -429,7 +429,7 @@ public func customizeDefaultDarkTintedPresentationTheme(theme: PresentationTheme panelBackgroundColor: mainBackgroundColor?.withAlphaComponent(0.9), panelSeparatorColor: mainSeparatorColor, panelControlAccentColor: accentColor, - panelControlColor: mainSecondaryTextColor?.withAlphaComponent(0.5), + panelControlColor: UIColor(rgb: 0xffffff), inputBackgroundColor: inputBackgroundColor, inputStrokeColor: accentColor?.withMultiplied(hue: 1.038, saturation: 0.463, brightness: 0.26), inputPlaceholderColor: mainSecondaryTextColor?.withAlphaComponent(0.4), @@ -581,7 +581,7 @@ public func makeDefaultDarkTintedPresentationTheme(extendingThemeReference: Pres let navigationSearchBar = PresentationThemeNavigationSearchBar( backgroundColor: mainBackgroundColor, accentColor: accentColor, - inputFillColor: mainInputColor, + inputFillColor: UIColor(white: 1.0, alpha: 0.1), inputTextColor: UIColor(rgb: 0xffffff), inputPlaceholderTextColor: mainSecondaryColor, inputIconColor: mainSecondaryColor, @@ -714,7 +714,7 @@ public func makeDefaultDarkTintedPresentationTheme(extendingThemeReference: Pres pinnedBadgeColor: mainSecondaryTextColor.withAlphaComponent(0.5), pinnedSearchBarColor: accentColor.withMultiplied(hue: 1.029, saturation: 0.609, brightness: 0.12), regularSearchBarColor: accentColor.withMultiplied(hue: 1.029, saturation: 0.609, brightness: 0.12), - sectionHeaderFillColor: mainBackgroundColor, + sectionHeaderFillColor: .black, sectionHeaderTextColor: mainSecondaryTextColor.withAlphaComponent(0.5), verifiedIconFillColor: accentColor, verifiedIconForegroundColor: .white, @@ -874,7 +874,7 @@ public func makeDefaultDarkTintedPresentationTheme(extendingThemeReference: Pres panelBackgroundColorNoWallpaper: accentColor.withMultiplied(hue: 1.024, saturation: 0.573, brightness: 0.18), panelSeparatorColor: mainSeparatorColor, panelControlAccentColor: accentColor, - panelControlColor: mainSecondaryTextColor.withAlphaComponent(0.5), + panelControlColor: UIColor(rgb: 0xffffff), panelControlDisabledColor: UIColor(rgb: 0x90979F, alpha: 0.5), panelControlDestructiveColor: UIColor(rgb: 0xff6767), inputBackgroundColor: inputBackgroundColor, diff --git a/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift index aacb1f01..4b5d5bd0 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift @@ -565,8 +565,8 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio pinnedBadgeColor: UIColor(rgb: 0xb6b6bb), pinnedSearchBarColor: UIColor(rgb: 0xe5e5e5), regularSearchBarColor: UIColor(rgb: 0xe9e9e9), - sectionHeaderFillColor: UIColor(rgb: 0xf7f7f7), - sectionHeaderTextColor: UIColor(rgb: 0x8e8e93), + sectionHeaderFillColor: .white, + sectionHeaderTextColor: UIColor(rgb: 0x6d6d72), verifiedIconFillColor: defaultDayAccentColor, verifiedIconForegroundColor: UIColor(rgb: 0xffffff), secretIconColor: UIColor(rgb: 0x00b12c), @@ -946,7 +946,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio panelControlColor: UIColor(rgb: 0x000000, alpha: 1.0), panelControlDisabledColor: UIColor(rgb: 0x727b87, alpha: 0.5), panelControlDestructiveColor: UIColor(rgb: 0xff3b30), - inputBackgroundColor: UIColor(rgb: 0xffffff), + inputBackgroundColor: UIColor(white: 1.0, alpha: 0.8), inputStrokeColor: UIColor(rgb: 0x000000, alpha: 0.1), inputPlaceholderColor: UIColor(rgb: 0x000000, alpha: 0.4), inputTextColor: UIColor(rgb: 0x000000), @@ -1032,7 +1032,7 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio primaryTextColor: UIColor(rgb: 0x000000), secondaryTextColor: UIColor(rgb: 0x8e8e93), controlAccentColor: defaultDayAccentColor, - inputBackgroundColor: UIColor(rgb: 0xe9e9e9), + inputBackgroundColor: UIColor(white: 1.0, alpha: 0.6), inputHollowBackgroundColor: UIColor(rgb: 0xffffff), inputBorderColor: UIColor(rgb: 0xe4e4e6), inputPlaceholderColor: UIColor(rgb: 0x8e8d92), diff --git a/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift b/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift index a2c2a1da..4d80c191 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationThemeEssentialGraphics.swift @@ -175,6 +175,11 @@ public final class PrincipalThemeEssentialGraphics { public let outgoingDateAndStatusStarsIcon: UIImage public let mediaStarsIcon: UIImage public let freeStarsIcon: UIImage + + public let incomingDateAndStatusTonIcon: UIImage + public let outgoingDateAndStatusTonIcon: UIImage + public let mediaTonIcon: UIImage + public let freeTonIcon: UIImage public let incomingDateAndStatusPinnedIcon: UIImage public let outgoingDateAndStatusPinnedIcon: UIImage @@ -369,6 +374,12 @@ public final class PrincipalThemeEssentialGraphics { self.outgoingDateAndStatusStarsIcon = generateTintedImage(image: starsImage, color: theme.message.outgoing.secondaryTextColor)! self.mediaStarsIcon = generateTintedImage(image: starsImage, color: .white)! self.freeStarsIcon = generateTintedImage(image: starsImage, color: serviceColor.primaryText)! + + let tonImage = generateScaledImage(image: UIImage(bundleImageName: "Ads/TonMedium"), size: CGSize(width: 12.0, height: 12.0), opaque: false)! + self.incomingDateAndStatusTonIcon = generateTintedImage(image: tonImage, color: theme.message.incoming.secondaryTextColor)! + self.outgoingDateAndStatusTonIcon = generateTintedImage(image: tonImage, color: theme.message.outgoing.secondaryTextColor)! + self.mediaTonIcon = generateTintedImage(image: tonImage, color: .white)! + self.freeTonIcon = generateTintedImage(image: tonImage, color: serviceColor.primaryText)! let pinnedImage = UIImage(bundleImageName: "Chat/Message/Pinned")! self.incomingDateAndStatusPinnedIcon = generateTintedImage(image: pinnedImage, color: theme.message.incoming.secondaryTextColor)! @@ -497,6 +508,12 @@ public final class PrincipalThemeEssentialGraphics { self.mediaStarsIcon = generateTintedImage(image: starsImage, color: .white)! self.freeStarsIcon = generateTintedImage(image: starsImage, color: serviceColor.primaryText)! + let tonImage = generateScaledImage(image: UIImage(bundleImageName: "Ads/TonMedium"), size: CGSize(width: 12.0, height: 12.0), opaque: false)! + self.incomingDateAndStatusTonIcon = generateTintedImage(image: tonImage, color: theme.message.incoming.secondaryTextColor)! + self.outgoingDateAndStatusTonIcon = generateTintedImage(image: tonImage, color: theme.message.outgoing.secondaryTextColor)! + self.mediaTonIcon = generateTintedImage(image: tonImage, color: .white)! + self.freeTonIcon = generateTintedImage(image: tonImage, color: serviceColor.primaryText)! + let pinnedImage = UIImage(bundleImageName: "Chat/Message/Pinned")! self.incomingDateAndStatusPinnedIcon = generateTintedImage(image: pinnedImage, color: theme.message.incoming.secondaryTextColor)! self.outgoingDateAndStatusPinnedIcon = generateTintedImage(image: pinnedImage, color: theme.message.outgoing.secondaryTextColor)! diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift index 504d20f5..bdb4dec8 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift @@ -308,6 +308,8 @@ public enum PresentationResourceKey: Int32 { case chatFreeShareButtonIcon case chatFreeCloseButtonIcon case chatFreeMoreButtonIcon + case chatFreeExpandButtonIcon + case chatFreeCollapseButtonIcon case chatKeyboardActionButtonMessageIcon case chatKeyboardActionButtonLinkIcon diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift index d2e5ce37..ccb452e6 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift @@ -1174,6 +1174,18 @@ public struct PresentationResourcesChat { }) } + public static func chatFreeExpandButtonIcon(_ theme: PresentationTheme, wallpaper: TelegramWallpaper) -> UIImage? { + return theme.image(PresentationResourceKey.chatFreeExpandButtonIcon.rawValue, { _ in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/ExpandIcon"), color: bubbleVariableColor(variableColor: theme.chat.message.shareButtonForegroundColor, wallpaper: wallpaper)) + }) + } + + public static func chatFreeCollapseButtonIcon(_ theme: PresentationTheme, wallpaper: TelegramWallpaper) -> UIImage? { + return theme.image(PresentationResourceKey.chatFreeCollapseButtonIcon.rawValue, { _ in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/CollapseIcon"), color: bubbleVariableColor(variableColor: theme.chat.message.shareButtonForegroundColor, wallpaper: wallpaper)) + }) + } + public static func chatKeyboardActionButtonMessageIconImage(_ theme: PresentationTheme) -> UIImage? { return theme.image(PresentationResourceKey.chatKeyboardActionButtonMessageIcon.rawValue, { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/BotMessage"), color: theme.chat.inputButtonPanel.buttonTextColor) diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift index d486af77..4ae03565 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift @@ -70,7 +70,7 @@ public struct PresentationResourcesRootController { public static func navigationCompactSearchIcon(_ theme: PresentationTheme) -> UIImage? { return theme.image(PresentationResourceKey.navigationCompactSearchIcon.rawValue, { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat List/SearchIcon"), color: theme.rootController.navigationBar.accentTextColor) + return generateTintedImage(image: UIImage(bundleImageName: "Chat List/SearchIcon"), color: theme.chat.inputPanel.panelControlColor) }) } @@ -82,7 +82,7 @@ public struct PresentationResourcesRootController { public static func navigationCompactTagsSearchIcon(_ theme: PresentationTheme) -> UIImage? { return theme.image(PresentationResourceKey.navigationCompactTagsSearchIcon.rawValue, { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/NavigationSearchTagsIcon"), color: theme.rootController.navigationBar.accentTextColor) + return generateTintedImage(image: UIImage(bundleImageName: "Chat/NavigationSearchTagsIcon"), color: theme.chat.inputPanel.panelControlColor) }) } diff --git a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift index 01c037b5..f6d67402 100644 --- a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift +++ b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift @@ -1663,7 +1663,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, case .stars: priceString = strings.Notification_StarsGiftOffer_OfferYou_Stars(Int32(clamping: amount.amount.value)) case .ton: - priceString = "\(amount.amount) TON" + priceString = formatTonAmountText(amount.amount.value, dateTimeFormat: dateTimeFormat) + " TON" } attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGiftOffer_OfferYou(peerName, priceString, giftTitle)._tuple, body: bodyAttributes, argumentAttributes: attributes) @@ -1673,7 +1673,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, case .stars: priceString = strings.Notification_StarsGiftOffer_Offer_Stars(Int32(clamping: amount.amount.value)) case .ton: - priceString = "\(amount.amount) TON" + priceString = formatTonAmountText(amount.amount.value, dateTimeFormat: dateTimeFormat) + " TON" } attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGiftOffer_Offer(peerName, priceString, giftTitle)._tuple, body: bodyAttributes, argumentAttributes: attributes) @@ -1696,7 +1696,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, case .stars: priceString = strings.Notification_StarsGiftOffer_ExpiredYou_Stars(Int32(clamping: amount.amount.value)) case .ton: - priceString = "\(amount.amount) TON" + priceString = formatTonAmountText(amount.amount.value, dateTimeFormat: dateTimeFormat) + " TON" } var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds) @@ -1709,7 +1709,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, case .stars: priceString = strings.Notification_StarsGiftOffer_Expired_Stars(Int32(clamping: amount.amount.value)) case .ton: - priceString = "\(amount.amount) TON" + priceString = formatTonAmountText(amount.amount.value, dateTimeFormat: dateTimeFormat) + " TON" } let timeString = "[TODO]" @@ -1730,7 +1730,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, case .stars: priceString = strings.Notification_StarsGiftOffer_Rejected_Stars(Int32(clamping: amount.amount.value)) case .ton: - priceString = "\(amount.amount) TON" + priceString = formatTonAmountText(amount.amount.value, dateTimeFormat: dateTimeFormat) + " TON" } var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds) @@ -1764,6 +1764,35 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, resultTitleString = strings.Conversation_StoryExpiredMentionTextOutgoing(compactPeerName) } attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes]) + } else if let dice = media as? TelegramMediaDice, let gameOutcome = dice.gameOutcome { + if let value = dice.value { + let rawString: String + if message.author?.id == accountPeerId { + if value == 1, let tonAmount = dice.tonAmount { + let value = formatTonAmountText(tonAmount, dateTimeFormat: dateTimeFormat) + rawString = strings.Conversation_EmojiStake_LostYou(value).string + } else { + let value = formatTonAmountText(gameOutcome.tonAmount, dateTimeFormat: dateTimeFormat) + rawString = strings.Conversation_EmojiStake_WonYou(value).string + } + } else { + let compactPeerName = message.peers[message.id.peerId].flatMap(EnginePeer.init)?.compactDisplayTitle ?? "" + if value == 1, let tonAmount = dice.tonAmount { + let value = formatTonAmountText(tonAmount, dateTimeFormat: dateTimeFormat) + rawString = strings.Conversation_EmojiStake_Lost(compactPeerName, value).string + } else { + let value = formatTonAmountText(gameOutcome.tonAmount, dateTimeFormat: dateTimeFormat) + rawString = strings.Conversation_EmojiStake_Won(compactPeerName, value).string + } + } + + let attributedText = NSMutableAttributedString(string: rawString, font: titleFont, textColor: primaryTextColor) + if let range = attributedText.string.range(of: "$") { + attributedText.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .ton(tinted: true)), range: NSRange(range, in: attributedText.string)) + attributedText.addAttribute(.baselineOffset, value: 1.5, range: NSRange(range, in: attributedText.string)) + } + attributedString = attributedText + } } } diff --git a/submodules/TelegramUI/BUILD b/submodules/TelegramUI/BUILD index 8d395730..8d05f333 100644 --- a/submodules/TelegramUI/BUILD +++ b/submodules/TelegramUI/BUILD @@ -49,7 +49,7 @@ swift_library( "-warnings-as-errors", ], deps = [ - "//third-party/recaptcha:RecaptchaEnterprise", + "//third-party/recaptcha:RecaptchaEnterpriseSDK", "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/SSignalKit/SSignalKit:SSignalKit", "//submodules/AsyncDisplayKit:AsyncDisplayKit", @@ -153,7 +153,6 @@ swift_library( "//submodules/ChatListSearchItemHeader:ChatListSearchItemHeader", "//submodules/ItemListPeerItem:ItemListPeerItem", "//submodules/ContactsPeerItem:ContactsPeerItem", - "//submodules/ChatListSearchItemNode:ChatListSearchItemNode", "//submodules/TelegramPermissionsUI:TelegramPermissionsUI", "//submodules/PeersNearbyIconNode:PeersNearbyIconNode", "//submodules/SolidRoundedButtonNode:SolidRoundedButtonNode", @@ -498,12 +497,26 @@ swift_library( "//submodules/TelegramUI/Components/AttachmentFileController", "//submodules/TelegramUI/Components/Contacts/NewContactScreen", "//submodules/TelegramUI/Components/Chat/ChatSendAsContextMenu", + "//submodules/TelegramUI/Components/NavigationBarImpl", + "//submodules/TelegramUI/Components/GlobalControlPanelsContext", + "//submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent", + "//submodules/TelegramUI/Components/LiveLocationHeaderPanelComponent", + "//submodules/TelegramUI/Components/TranslateHeaderPanelComponent", + "//submodules/TelegramUI/Components/AdPanelHeaderPanelComponent", + "//submodules/TelegramUI/Components/MessageFeeHeaderPanelComponent", + "//submodules/TelegramUI/Components/LegacyChatHeaderPanelComponent", + "//submodules/TelegramUI/Components/GroupCallHeaderPanelComponent", + "//submodules/TelegramUI/Components/Chat/ChatSearchNavigationContentNode", "//submodules/TelegramUI/Components/Settings/PasskeysScreen", "//submodules/TelegramUI/Components/Gifts/GiftDemoScreen", + "//submodules/TelegramUI/Components/EmojiGameStakeScreen", + "//submodules/TelegramUI/Components/AlertComponent", + "//submodules/TelegramUI/Components/Chat/ChatAgeRestrictionAlertController", + "//submodules/TelegramUI/Components/CocoonInfoScreen", ] + select({ "@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets, "//build-system:ios_sim_arm64": [], - "@build_bazel_rules_apple//apple:ios_x86_64": [], + "//conditions:default": [], }), visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/AdPanelHeaderPanelComponent/BUILD b/submodules/TelegramUI/Components/AdPanelHeaderPanelComponent/BUILD new file mode 100644 index 00000000..cf80f8d1 --- /dev/null +++ b/submodules/TelegramUI/Components/AdPanelHeaderPanelComponent/BUILD @@ -0,0 +1,36 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "AdPanelHeaderPanelComponent", + module_name = "AdPanelHeaderPanelComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Display", + "//submodules/AsyncDisplayKit", + "//submodules/TelegramPresentationData", + "//submodules/AccountContext", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/Postbox", + "//submodules/TelegramCore", + "//submodules/TelegramUIPreferences", + "//submodules/StickerResources", + "//submodules/PhotoResources", + "//submodules/TelegramStringFormatting", + "//submodules/AnimatedCountLabelNode", + "//submodules/AnimatedNavigationStripeNode", + "//submodules/ContextUI", + "//submodules/RadialStatusNode", + "//submodules/TextFormat", + "//submodules/TelegramUI/Components/TextNodeWithEntities", + "//submodules/TranslateUI", + + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/AdPanelHeaderPanelComponent/Sources/AdPanelHeaderPanelComponent.swift b/submodules/TelegramUI/Components/AdPanelHeaderPanelComponent/Sources/AdPanelHeaderPanelComponent.swift new file mode 100644 index 00000000..69991d5d --- /dev/null +++ b/submodules/TelegramUI/Components/AdPanelHeaderPanelComponent/Sources/AdPanelHeaderPanelComponent.swift @@ -0,0 +1,127 @@ +import Foundation +import UIKit +import Display +import TelegramPresentationData +import ComponentFlow +import ComponentDisplayAdapters +import AccountContext +import TelegramCore +import SwiftSignalKit +import Postbox +import PresentationDataUtils +import ContextUI +import AsyncDisplayKit + +public final class AdPanelHeaderPanelComponent: Component { + public struct Info: Equatable { + public let message: EngineMessage + + public init(message: EngineMessage) { + self.message = message + } + } + + public let context: AccountContext + public let theme: PresentationTheme + public let strings: PresentationStrings + public let info: Info + public let action: (EngineMessage) -> Void + public let contextAction: (EngineMessage, ASDisplayNode, ContextGesture?) -> Void + public let close: () -> Void + + public init( + context: AccountContext, + theme: PresentationTheme, + strings: PresentationStrings, + info: Info, + action: @escaping (EngineMessage) -> Void, + contextAction: @escaping (EngineMessage, ASDisplayNode, ContextGesture?) -> Void, + close: @escaping () -> Void + ) { + self.context = context + self.theme = theme + self.strings = strings + self.info = info + self.action = action + self.contextAction = contextAction + self.close = close + } + + public static func ==(lhs: AdPanelHeaderPanelComponent, rhs: AdPanelHeaderPanelComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.theme !== rhs.theme { + return false + } + if lhs.strings !== rhs.strings { + return false + } + if lhs.info != rhs.info { + return false + } + return true + } + + public final class View: UIView { + private var panel: ChatAdPanelNode? + + private var component: AdPanelHeaderPanelComponent? + private weak var state: EmptyComponentState? + + public var message: EngineMessage? { + return self.component?.info.message + } + + public override init(frame: CGRect) { + super.init(frame: frame) + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + } + + func update(component: AdPanelHeaderPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + self.state = state + + let panel: ChatAdPanelNode + if let current = self.panel { + panel = current + } else { + panel = ChatAdPanelNode( + context: component.context, + action: component.action, + contextAction: component.contextAction, + close: component.close + ) + self.panel = panel + self.addSubview(panel.view) + } + + let height = panel.updateLayout( + width: availableSize.width, + theme: component.theme, + strings: component.strings, + info: component.info, + transition: transition.containedViewLayoutTransition + ) + let size = CGSize(width: availableSize.width, height: height) + let panelFrame = CGRect(origin: CGPoint(), size: size) + transition.setFrame(view: panel.view, frame: panelFrame) + + return size + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/AdPanelHeaderPanelComponent/Sources/ChatAdPanelNode.swift b/submodules/TelegramUI/Components/AdPanelHeaderPanelComponent/Sources/ChatAdPanelNode.swift new file mode 100644 index 00000000..b6e7d92a --- /dev/null +++ b/submodules/TelegramUI/Components/AdPanelHeaderPanelComponent/Sources/ChatAdPanelNode.swift @@ -0,0 +1,523 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import Postbox +import TelegramCore +import SwiftSignalKit +import TelegramPresentationData +import TelegramUIPreferences +import AccountContext +import StickerResources +import PhotoResources +import TelegramStringFormatting +import AnimatedCountLabelNode +import AnimatedNavigationStripeNode +import ContextUI +import RadialStatusNode +import TextFormat +import TextNodeWithEntities +import TranslateUI + +private enum PinnedMessageAnimation { + case slideToTop + case slideToBottom +} + +final class ChatAdPanelNode: ASDisplayNode { + private let context: AccountContext + private let action: (EngineMessage) -> Void + private let contextAction: (EngineMessage, ASDisplayNode, ContextGesture?) -> Void + private let close: () -> Void + + private(set) var message: EngineMessage? + + private let tapButton: HighlightTrackingButtonNode + + private let contextContainer: ContextControllerSourceNode + private let clippingContainer: ASDisplayNode + private let contentContainer: ASDisplayNode + private let contentTextContainer: ASDisplayNode + private let adNode: TextNode + private let titleNode: TextNode + private let textNode: TextNodeWithEntities + + private let removeButtonNode: HighlightTrackingButtonNode + private let removeBackgroundNode: ASImageNode + private let removeTextNode: ImmediateTextNode + + private let closeButton: HighlightableButtonNode + + private let imageNode: TransformImageNode + private let imageNodeContainer: ASDisplayNode + + private var currentLayout: (CGFloat, CGFloat, CGFloat)? + private var previousMediaReference: AnyMediaReference? + + private let fetchDisposable = MetaDisposable() + + init( + context: AccountContext, + action: @escaping (EngineMessage) -> Void, + contextAction: @escaping (EngineMessage, ASDisplayNode, ContextGesture?) -> Void, + close: @escaping () -> Void + ) { + self.context = context + self.action = action + self.contextAction = contextAction + self.close = close + + self.tapButton = HighlightTrackingButtonNode() + + self.contextContainer = ContextControllerSourceNode() + + self.clippingContainer = ASDisplayNode() + self.clippingContainer.clipsToBounds = true + + self.contentContainer = ASDisplayNode() + self.contentTextContainer = ASDisplayNode() + + self.adNode = TextNode() + self.adNode.displaysAsynchronously = false + self.adNode.isUserInteractionEnabled = false + + self.removeButtonNode = HighlightTrackingButtonNode() + self.removeBackgroundNode = ASImageNode() + + self.removeTextNode = ImmediateTextNode() + self.removeTextNode.displaysAsynchronously = false + self.removeTextNode.isUserInteractionEnabled = false + + self.titleNode = TextNode() + self.titleNode.displaysAsynchronously = false + self.titleNode.isUserInteractionEnabled = false + + self.textNode = TextNodeWithEntities() + self.textNode.textNode.displaysAsynchronously = false + self.textNode.textNode.isUserInteractionEnabled = false + + self.imageNode = TransformImageNode() + self.imageNode.contentAnimations = [.subsequentUpdates] + + self.imageNodeContainer = ASDisplayNode() + + self.closeButton = HighlightableButtonNode() + self.closeButton.hitTestSlop = UIEdgeInsets(top: -8.0, left: -8.0, bottom: -8.0, right: -8.0) + self.closeButton.displaysAsynchronously = false + + super.init() + + self.addSubnode(self.contextContainer) + + self.contextContainer.addSubnode(self.clippingContainer) + self.clippingContainer.addSubnode(self.contentContainer) + self.contentTextContainer.addSubnode(self.titleNode) + + self.contentTextContainer.addSubnode(self.adNode) + + self.contentTextContainer.addSubnode(self.textNode.textNode) + self.contentContainer.addSubnode(self.contentTextContainer) + + self.imageNodeContainer.addSubnode(self.imageNode) + self.contentContainer.addSubnode(self.imageNodeContainer) + + self.tapButton.addTarget(self, action: #selector(self.tapped), forControlEvents: [.touchUpInside]) + self.tapButton.highligthedChanged = { [weak self] highlighted in + if let strongSelf = self { + if highlighted { + strongSelf.adNode.layer.removeAnimation(forKey: "opacity") + strongSelf.adNode.alpha = 0.4 + strongSelf.titleNode.layer.removeAnimation(forKey: "opacity") + strongSelf.titleNode.alpha = 0.4 + strongSelf.textNode.textNode.layer.removeAnimation(forKey: "opacity") + strongSelf.textNode.textNode.alpha = 0.4 + strongSelf.imageNode.layer.removeAnimation(forKey: "opacity") + strongSelf.imageNode.alpha = 0.4 + strongSelf.removeTextNode.layer.removeAnimation(forKey: "opacity") + strongSelf.removeTextNode.alpha = 0.4 + strongSelf.removeBackgroundNode.layer.removeAnimation(forKey: "opacity") + strongSelf.removeBackgroundNode.alpha = 0.4 + } else { + strongSelf.adNode.alpha = 1.0 + strongSelf.adNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + strongSelf.titleNode.alpha = 1.0 + strongSelf.titleNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + strongSelf.textNode.textNode.alpha = 1.0 + strongSelf.textNode.textNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + strongSelf.imageNode.alpha = 1.0 + strongSelf.imageNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + strongSelf.removeTextNode.alpha = 1.0 + strongSelf.removeTextNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + strongSelf.removeBackgroundNode.alpha = 1.0 + strongSelf.removeBackgroundNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + } + } + } + self.contextContainer.addSubnode(self.tapButton) + + self.contextContainer.addSubnode(self.removeBackgroundNode) + self.contextContainer.addSubnode(self.removeTextNode) + self.contextContainer.addSubnode(self.removeButtonNode) + + self.removeButtonNode.addTarget(self, action: #selector(self.removePressed), forControlEvents: [.touchUpInside]) + self.removeButtonNode.highligthedChanged = { [weak self] highlighted in + if let strongSelf = self { + if highlighted { + strongSelf.removeTextNode.layer.removeAnimation(forKey: "opacity") + strongSelf.removeTextNode.alpha = 0.4 + strongSelf.removeBackgroundNode.layer.removeAnimation(forKey: "opacity") + strongSelf.removeBackgroundNode.alpha = 0.4 + } else { + strongSelf.removeTextNode.alpha = 1.0 + strongSelf.removeTextNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + strongSelf.removeBackgroundNode.alpha = 1.0 + strongSelf.removeBackgroundNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + } + } + } + + self.contextContainer.activated = { [weak self] gesture, _ in + guard let self, let message = self.message else { + return + } + self.contextAction(message, self.contextContainer, gesture) + } + + self.closeButton.addTarget(self, action: #selector(self.closePressed), forControlEvents: [.touchUpInside]) + self.addSubnode(self.closeButton) + } + + deinit { + self.fetchDisposable.dispose() + } + + private var theme: PresentationTheme? + + @objc private func closePressed() { + /*if self.context.isPremium, let adAttribute = self.message?.adAttribute { + self.controllerInteraction?.removeAd(adAttribute.opaqueId) + } else { + self.controllerInteraction?.openNoAdsDemo() + }*/ + self.close() + } + + func updateLayout(width: CGFloat, theme: PresentationTheme, strings: PresentationStrings, info: AdPanelHeaderPanelComponent.Info, transition: ContainedViewLayoutTransition) -> CGFloat { + let leftInset: CGFloat = 0.0 + let rightInset: CGFloat = 0.0 + + self.message = info.message + + if self.theme !== theme { + self.theme = theme + self.removeBackgroundNode.image = generateStretchableFilledCircleImage(diameter: 15.0, color: theme.chat.inputPanel.panelControlColor.withMultipliedAlpha(0.1)) + self.removeTextNode.attributedText = NSAttributedString(string: strings.Chat_BotAd_WhatIsThis, font: Font.regular(11.0), textColor: theme.chat.inputPanel.panelControlColor) + self.closeButton.setImage(generateImage(CGSize(width: 12.0, height: 12.0), contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setStrokeColor(theme.chat.inputPanel.panelControlColor.cgColor) + context.setLineWidth(1.33) + context.setLineCap(.round) + context.move(to: CGPoint(x: 1.0, y: 1.0)) + context.addLine(to: CGPoint(x: size.width - 1.0, y: size.height - 1.0)) + context.strokePath() + context.move(to: CGPoint(x: size.width - 1.0, y: 1.0)) + context.addLine(to: CGPoint(x: 1.0, y: size.height - 1.0)) + context.strokePath() + }), for: []) + } + + self.contextContainer.isGestureEnabled = false + + let panelHeight: CGFloat + var hasCloseButton = true + let presentationData = self.context.sharedContext.currentPresentationData.with({ $0 }) + panelHeight = self.enqueueTransition(width: width, leftInset: leftInset, rightInset: rightInset, transition: .immediate, animation: nil, message: info.message, theme: theme, strings: strings, nameDisplayOrder: presentationData.nameDisplayOrder, dateTimeFormat: presentationData.dateTimeFormat, accountPeerId: self.context.account.peerId, firstTime: false, isReplyThread: false, translateToLanguage: nil) + hasCloseButton = info.message.media.isEmpty + + self.contextContainer.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: panelHeight)) + + self.tapButton.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: panelHeight)) + + self.clippingContainer.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: panelHeight)) + self.contentContainer.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: panelHeight)) + + let contentRightInset: CGFloat = 14.0 + rightInset + let closeButtonSize = self.closeButton.measure(CGSize(width: 100.0, height: 100.0)) + self.closeButton.frame = CGRect(origin: CGPoint(x: width - contentRightInset - closeButtonSize.width, y: floorToScreenPixels((panelHeight - closeButtonSize.height) / 2.0)), size: closeButtonSize) + + self.closeButton.isHidden = !hasCloseButton + + self.currentLayout = (width, leftInset, rightInset) + + return panelHeight + } + + private func enqueueTransition(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, animation: PinnedMessageAnimation?, message: EngineMessage, theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, accountPeerId: PeerId, firstTime: Bool, isReplyThread: Bool, translateToLanguage: String?) -> CGFloat { + var animationTransition: ContainedViewLayoutTransition = .immediate + + if let animation = animation { + animationTransition = .animated(duration: 0.2, curve: .easeInOut) + + if let copyView = self.textNode.textNode.view.snapshotView(afterScreenUpdates: false) { + let offset: CGFloat + switch animation { + case .slideToTop: + offset = -10.0 + case .slideToBottom: + offset = 10.0 + } + + copyView.frame = self.textNode.textNode.frame + self.textNode.textNode.view.superview?.addSubview(copyView) + copyView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: offset), duration: 0.2, removeOnCompletion: false, additive: true) + copyView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak copyView] _ in + copyView?.removeFromSuperview() + }) + self.textNode.textNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -offset), to: CGPoint(), duration: 0.2, additive: true) + self.textNode.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + } + + let makeAdLayout = TextNode.asyncLayout(self.adNode) + let makeTitleLayout = TextNode.asyncLayout(self.titleNode) + let makeTextLayout = TextNodeWithEntities.asyncLayout(self.textNode) + let imageNodeLayout = self.imageNode.asyncLayout() + + let previousMediaReference = self.previousMediaReference + let context = self.context + + let contentLeftInset: CGFloat = leftInset + 18.0 + let contentRightInset: CGFloat = rightInset + 9.0 + + var textRightInset: CGFloat = 0.0 + + var updatedMediaReference: AnyMediaReference? + var imageDimensions: CGSize? + + if !message._asMessage().containsSecretMedia { + for media in message.media { + if let image = media as? TelegramMediaImage { + updatedMediaReference = .message(message: MessageReference(message._asMessage()), media: image) + if let representation = largestRepresentationForPhoto(image) { + imageDimensions = representation.dimensions.cgSize + } + break + } else if let file = media as? TelegramMediaFile { + updatedMediaReference = .message(message: MessageReference(message._asMessage()), media: file) + if !file.isInstantVideo && !file.isSticker, let representation = largestImageRepresentation(file.previewRepresentations) { + imageDimensions = representation.dimensions.cgSize + } else if file.isAnimated, let dimensions = file.dimensions { + imageDimensions = dimensions.cgSize + } + break + } else if let paidContent = media as? TelegramMediaPaidContent, let firstMedia = paidContent.extendedMedia.first { + switch firstMedia { + case let .preview(dimensions, immediateThumbnailData, _): + let thumbnailMedia = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: [], immediateThumbnailData: immediateThumbnailData, reference: nil, partialReference: nil, flags: []) + if let dimensions { + imageDimensions = dimensions.cgSize + } + updatedMediaReference = .standalone(media: thumbnailMedia) + case let .full(fullMedia): + updatedMediaReference = .message(message: MessageReference(message._asMessage()), media: fullMedia) + if let image = fullMedia as? TelegramMediaImage { + if let representation = largestRepresentationForPhoto(image) { + imageDimensions = representation.dimensions.cgSize + } + break + } else if let file = fullMedia as? TelegramMediaFile { + if let dimensions = file.dimensions { + imageDimensions = dimensions.cgSize + } + break + } + } + } + } + } + + let imageBoundingSize = CGSize(width: 48.0, height: 48.0) + var applyImage: (() -> Void)? + if let imageDimensions { + applyImage = imageNodeLayout(TransformImageArguments(corners: ImageCorners(radius: 10.0), imageSize: imageDimensions.aspectFilled(imageBoundingSize), boundingSize: imageBoundingSize, intrinsicInsets: UIEdgeInsets())) + textRightInset += imageBoundingSize.width + 18.0 + } else { + textRightInset = 27.0 + } + + var mediaUpdated = false + if let updatedMediaReference = updatedMediaReference, let previousMediaReference = previousMediaReference { + mediaUpdated = !updatedMediaReference.media.isEqual(to: previousMediaReference.media) + } else if (updatedMediaReference != nil) != (previousMediaReference != nil) { + mediaUpdated = true + } + + var updateImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>? + var updatedFetchMediaSignal: Signal? + if mediaUpdated { + if let updatedMediaReference = updatedMediaReference, imageDimensions != nil { + if let imageReference = updatedMediaReference.concrete(TelegramMediaImage.self) { + if imageReference.media.representations.isEmpty { + updateImageSignal = chatSecretPhoto(account: context.account, userLocation: .peer(message.id.peerId), photoReference: imageReference, ignoreFullSize: true, synchronousLoad: true) + } else { + updateImageSignal = chatMessagePhotoThumbnail(account: context.account, userLocation: .peer(message.id.peerId), photoReference: imageReference, blurred: false) + } + } else if let fileReference = updatedMediaReference.concrete(TelegramMediaFile.self) { + if fileReference.media.isAnimatedSticker { + let dimensions = fileReference.media.dimensions ?? PixelDimensions(width: 512, height: 512) + updateImageSignal = chatMessageAnimatedSticker(postbox: context.account.postbox, userLocation: .peer(message.id.peerId), file: fileReference.media, small: false, size: dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0))) + updatedFetchMediaSignal = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, userLocation: .peer(message.id.peerId), userContentType: MediaResourceUserContentType(file: fileReference.media), reference: fileReference.resourceReference(fileReference.media.resource)) + } else if fileReference.media.isVideo || fileReference.media.isAnimated { + updateImageSignal = chatMessageVideoThumbnail(account: context.account, userLocation: .peer(message.id.peerId), fileReference: fileReference, blurred: false) + } else if let iconImageRepresentation = smallestImageRepresentation(fileReference.media.previewRepresentations) { + updateImageSignal = chatWebpageSnippetFile(account: context.account, userLocation: .peer(message.id.peerId), mediaReference: fileReference.abstract, representation: iconImageRepresentation) + } + } + } else { + updateImageSignal = .single({ _ in return nil }) + } + } + + let (adLayout, adApply) = makeAdLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: strings.Chat_BotAd_Title, font: Font.semibold(14.0), textColor: theme.chat.inputPanel.panelControlColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width, height: .greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: .zero)) + + let titleConstrainedSize = CGSize(width: width - contentLeftInset - contentRightInset - textRightInset - adLayout.size.width - 90.0, height: CGFloat.greatestFiniteMagnitude) + let textConstrainedSize = CGSize(width: width - contentLeftInset - contentRightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude) + + var titleText: String = "" + if let author = message.author { + titleText = author.compactDisplayTitle + } + let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: titleText, font: Font.semibold(14.0), textColor: theme.chat.inputPanel.primaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: titleConstrainedSize, alignment: .natural, cutout: nil, insets: .zero)) + + let (textString, _, isText) = descriptionStringForMessage(contentSettings: context.currentContentSettings.with { $0 }, message: message, strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, accountPeerId: accountPeerId) + + let messageText: NSAttributedString + let textFont = Font.regular(14.0) + if isText { + var text = message.text + var messageEntities = message._asMessage().textEntitiesAttribute?.entities ?? [] + + if let translateToLanguage = translateToLanguage, !text.isEmpty { + for attribute in message.attributes { + if let attribute = attribute as? TranslationMessageAttribute, !attribute.text.isEmpty, attribute.toLang == translateToLanguage { + text = attribute.text + messageEntities = attribute.entities + break + } + } + } + + let entities = messageEntities.filter { entity in + switch entity.type { + case .CustomEmoji: + return true + default: + return false + } + } + let textColor = theme.chat.inputPanel.primaryTextColor + if entities.count > 0 { + messageText = stringWithAppliedEntities(trimToLineCount(text, lineCount: 1), entities: entities, baseColor: textColor, linkColor: textColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false, message: message._asMessage()) + } else { + messageText = NSAttributedString(string: foldLineBreaks(text), font: textFont, textColor: textColor) + } + } else { + messageText = NSAttributedString(string: foldLineBreaks(textString.string), font: textFont, textColor: message.media.isEmpty || message.media.first is TelegramMediaWebpage ? theme.chat.inputPanel.primaryTextColor : theme.chat.inputPanel.secondaryTextColor) + } + + let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: messageText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: textConstrainedSize, alignment: .natural, cutout: nil, insets: .zero)) + + var panelHeight: CGFloat = 0.0 + if let _ = imageDimensions { + panelHeight = 9.0 + imageBoundingSize.height + 9.0 + } + + var textHeight: CGFloat + var titleOnSeparateLine = false + if textLayout.numberOfLines == 1 || contentLeftInset + adLayout.size.width + 2.0 + titleLayout.size.width > width - contentRightInset - textRightInset { + textHeight = adLayout.size.height + titleLayout.size.height + textLayout.size.height + 15.0 + titleOnSeparateLine = true + } else { + textHeight = titleLayout.size.height + textLayout.size.height + 15.0 + } + + panelHeight = max(panelHeight, textHeight) + + Queue.mainQueue().async { + let _ = adApply() + let _ = titleApply() + + let textArguments = TextNodeWithEntities.Arguments( + context: self.context, + cache: self.context.animationCache, + renderer: self.context.animationRenderer, + placeholderColor: theme.list.mediaPlaceholderColor, + attemptSynchronous: false + ) + let _ = textApply(textArguments) + + self.previousMediaReference = updatedMediaReference + + let textContainerFrame = CGRect(origin: CGPoint(x: contentLeftInset, y: 0.0), size: CGSize(width: width, height: panelHeight)) + animationTransition.updateFrameAdditive(node: self.contentTextContainer, frame: textContainerFrame) + + let removeTextSize = self.removeTextNode.updateLayout(CGSize(width: width, height: .greatestFiniteMagnitude)) + + if titleOnSeparateLine { + self.adNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 9.0), size: adLayout.size) + self.titleNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 26.0), size: titleLayout.size) + self.textNode.textNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 43.0), size: textLayout.size) + + self.removeTextNode.frame = CGRect(origin: CGPoint(x: contentLeftInset + adLayout.size.width + 8.0, y: 11.0 - UIScreenPixel), size: removeTextSize) + self.removeBackgroundNode.frame = self.removeTextNode.frame.insetBy(dx: -5.0, dy: -1.0) + self.removeButtonNode.frame = self.removeBackgroundNode.frame + } else { + self.adNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 9.0), size: adLayout.size) + self.titleNode.frame = CGRect(origin: CGPoint(x: adLayout.size.width + 2.0, y: 9.0), size: titleLayout.size) + self.textNode.textNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 26.0), size: textLayout.size) + + self.removeTextNode.frame = CGRect(origin: CGPoint(x: contentLeftInset + adLayout.size.width + 2.0 + titleLayout.size.width + 8.0, y: 11.0 - UIScreenPixel), size: removeTextSize) + self.removeBackgroundNode.frame = self.removeTextNode.frame.insetBy(dx: -5.0, dy: -1.0) + self.removeButtonNode.frame = self.removeBackgroundNode.frame + } + + self.textNode.visibilityRect = CGRect.infinite + + self.imageNodeContainer.frame = CGRect(origin: CGPoint(x: width - contentRightInset - imageBoundingSize.width, y: 9.0), size: imageBoundingSize) + self.imageNode.frame = CGRect(origin: CGPoint(), size: imageBoundingSize) + + if let applyImage = applyImage { + applyImage() + + animationTransition.updateSublayerTransformScale(node: self.imageNodeContainer, scale: 1.0) + animationTransition.updateAlpha(node: self.imageNodeContainer, alpha: 1.0, beginWithCurrentState: true) + } else { + animationTransition.updateSublayerTransformScale(node: self.imageNodeContainer, scale: 0.1) + animationTransition.updateAlpha(node: self.imageNodeContainer, alpha: 0.0, beginWithCurrentState: true) + } + + if let updateImageSignal = updateImageSignal { + self.imageNode.setSignal(updateImageSignal) + } + if let updatedFetchMediaSignal = updatedFetchMediaSignal { + self.fetchDisposable.set(updatedFetchMediaSignal.startStrict()) + } + } + + return panelHeight + } + + @objc func tapped() { + guard let message = self.message else { + return + } + self.action(message) + } + + @objc func removePressed() { + guard let message = self.message else { + return + } + self.contextAction(message, self.contextContainer, nil) + } +} diff --git a/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/AdminUserActionsSheet.swift b/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/AdminUserActionsSheet.swift index b6137927..54d828be 100644 --- a/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/AdminUserActionsSheet.swift +++ b/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/AdminUserActionsSheet.swift @@ -1211,8 +1211,7 @@ private final class AdminUserActionsSheetComponent: Component { return } if !isEnabled { - let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } - self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: environment.strings.GroupPermission_PermissionDisabledByDefault, actions: [ + self.environment?.controller()?.present(textAlertController(context: component.context, title: nil, text: environment.strings.GroupPermission_PermissionDisabledByDefault, actions: [ TextAlertAction(type: .defaultAction, title: environment.strings.Common_OK, action: { }) ]), in: .window(.root)) diff --git a/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/RecentActionsSettingsSheet.swift b/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/RecentActionsSettingsSheet.swift index a6dbc7f4..8d4ee376 100644 --- a/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/RecentActionsSettingsSheet.swift +++ b/submodules/TelegramUI/Components/AdminUserActionsSheet/Sources/RecentActionsSettingsSheet.swift @@ -496,7 +496,7 @@ private final class RecentActionsSettingsSheetComponent: Component { component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: environment.theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: environment.theme.chat.inputPanel.panelControlColor ) )), action: { [weak self] _ in diff --git a/submodules/TelegramUI/Components/AlertComponent/AlertCheckComponent/BUILD b/submodules/TelegramUI/Components/AlertComponent/AlertCheckComponent/BUILD new file mode 100644 index 00000000..d6049dc5 --- /dev/null +++ b/submodules/TelegramUI/Components/AlertComponent/AlertCheckComponent/BUILD @@ -0,0 +1,28 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "AlertCheckComponent", + module_name = "AlertCheckComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/ComponentFlow", + "//submodules/TelegramPresentationData", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/TextFormat", + "//submodules/Markdown", + "//submodules/TelegramUI/Components/AlertComponent", + "//submodules/TelegramUI/Components/CheckComponent", + "//submodules/Components/MultilineTextComponent", + "//submodules/TelegramUI/Components/PlainButtonComponent", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/AlertComponent/AlertCheckComponent/Sources/AlertCheckComponent.swift b/submodules/TelegramUI/Components/AlertComponent/AlertCheckComponent/Sources/AlertCheckComponent.swift new file mode 100644 index 00000000..a9c1d04b --- /dev/null +++ b/submodules/TelegramUI/Components/AlertComponent/AlertCheckComponent/Sources/AlertCheckComponent.swift @@ -0,0 +1,186 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import ComponentFlow +import SwiftSignalKit +import TelegramPresentationData +import AlertComponent +import PlainButtonComponent +import MultilineTextComponent +import CheckComponent +import TextFormat +import Markdown + +public final class AlertCheckComponent: Component { + public typealias EnvironmentType = AlertComponentEnvironment + + public class ExternalState { + public fileprivate(set) var value: Bool + fileprivate var valuePromise = Promise() + public var valueSignal: Signal + + public init() { + self.value = false + self.valueSignal = self.valuePromise.get() + } + } + + let title: String + let initialValue: Bool + let externalState: ExternalState + let linkAction: (() -> Void)? + + public init( + title: String, + initialValue: Bool, + externalState: ExternalState, + linkAction: (() -> Void)? = nil + ) { + self.title = title + self.initialValue = initialValue + self.externalState = externalState + self.linkAction = linkAction + } + + public static func ==(lhs: AlertCheckComponent, rhs: AlertCheckComponent) -> Bool { + return true + } + + public final class View: UIView { + private let button = ComponentView() + + private var component: AlertCheckComponent? + private weak var state: EmptyComponentState? + + private var isUpdating = false + + private var valuePromise = ValuePromise(false) + + public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + func findTextView(view: UIView?) -> ImmediateTextView? { + if let view { + if let view = view as? ImmediateTextView { + return view + } + for view in view.subviews { + if let result = findTextView(view: view) { + return result + } + } + } + return nil + } + let result = super.hitTest(point, with: event) + if let textView = findTextView(view: result) { + if let (_, attributes) = textView.attributesAtPoint(self.convert(point, to: textView)) { + if attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] != nil { + return textView + } + } + } + return result + } + + func update(component: AlertCheckComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.isUpdating = true + defer { + self.isUpdating = false + } + if self.component == nil { + component.externalState.value = component.initialValue + component.externalState.valuePromise.set(self.valuePromise.get()) + } + self.component = component + self.state = state + + let environment = environment[AlertComponentEnvironment.self] + + let checkTheme = CheckComponent.Theme( + backgroundColor: environment.theme.list.itemCheckColors.fillColor, + strokeColor: environment.theme.list.itemCheckColors.foregroundColor, + borderColor: environment.theme.actionSheet.primaryTextColor.withMultipliedAlpha(0.15), + overlayBorder: false, + hasInset: false, + hasShadow: false + ) + + let textFont = Font.regular(15.0) + let boldTextFont = Font.semibold(15.0) + let textColor = environment.theme.actionSheet.primaryTextColor + let linkColor = environment.theme.actionSheet.controlAccentColor + let markdownAttributes = MarkdownAttributes( + body: MarkdownAttributeSet(font: textFont, textColor: textColor), + bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), + link: MarkdownAttributeSet(font: textFont, textColor: linkColor), + linkAttribute: { contents in + return (TelegramTextAttributes.URL, contents) + } + ) + + let buttonSize = self.button.update( + transition: transition, + component: AnyComponent(PlainButtonComponent( + content: AnyComponent(HStack([ + AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(CheckComponent( + theme: checkTheme, + size: CGSize(width: 18.0, height: 18.0), + selected: component.externalState.value + ))), + AnyComponentWithIdentity(id: AnyHashable(1), component: AnyComponent(MultilineTextComponent( + text: .markdown(text: component.title, attributes: markdownAttributes), + maximumNumberOfLines: 2, + highlightColor: linkColor.withAlphaComponent(0.1), + highlightAction: { attributes in + if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] { + return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL) + } else { + return nil + } + }, + tapAction: { attributes, _ in + if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String { + component.linkAction?() + } + } + ))) + ], spacing: 10.0)), + effectAlignment: .center, + action: { [weak self] in + guard let self, let component = self.component else { + return + } + component.externalState.value = !component.externalState.value + self.valuePromise.set(component.externalState.value) + + if !self.isUpdating { + self.state?.updated(transition: .spring(duration: 0.4)) + } + }, + animateAlpha: false, + animateScale: false + )), + environment: { + }, + containerSize: CGSize(width: availableSize.width + 20.0, height: 1000.0) + ) + let buttonFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - buttonSize.width) / 2.0), y: 7.0), size: buttonSize) + if let buttonView = self.button.view { + if buttonView.superview == nil { + self.addSubview(buttonView) + } + transition.setFrame(view: buttonView, frame: buttonFrame) + } + + return CGSize(width: availableSize.width, height: buttonSize.height + 7.0) + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/AlertComponent/AlertInputFieldComponent/BUILD b/submodules/TelegramUI/Components/AlertComponent/AlertInputFieldComponent/BUILD new file mode 100644 index 00000000..86dafdff --- /dev/null +++ b/submodules/TelegramUI/Components/AlertComponent/AlertInputFieldComponent/BUILD @@ -0,0 +1,28 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "AlertInputFieldComponent", + module_name = "AlertInputFieldComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/AccountContext", + "//submodules/ComponentFlow", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/TelegramPresentationData", + "//submodules/TextFormat", + "//submodules/Components/MultilineTextComponent", + "//submodules/Components/BundleIconComponent", + "//submodules/TelegramUI/Components/AlertComponent", + "//submodules/TelegramUI/Components/PlainButtonComponent", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/AlertComponent/AlertInputFieldComponent/Sources/AlertInputFieldComponent.swift b/submodules/TelegramUI/Components/AlertComponent/AlertInputFieldComponent/Sources/AlertInputFieldComponent.swift new file mode 100644 index 00000000..34140adf --- /dev/null +++ b/submodules/TelegramUI/Components/AlertComponent/AlertInputFieldComponent/Sources/AlertInputFieldComponent.swift @@ -0,0 +1,353 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import ComponentFlow +import SwiftSignalKit +import TelegramCore +import TelegramPresentationData +import AlertComponent +import MultilineTextComponent +import AccountContext +import TextFormat +import PlainButtonComponent +import BundleIconComponent + +public final class AlertInputFieldComponent: Component { + public typealias EnvironmentType = AlertComponentEnvironment + + public class ExternalState { + public fileprivate(set) var value: String = "" + public fileprivate(set) var animateError: () -> Void = {} + public fileprivate(set) var activateInput: () -> Void = {} + fileprivate let valuePromise = ValuePromise("") + public var valueSignal: Signal { + return self.valuePromise.get() + } + + public init() { + } + } + + let context: AccountContext + let initialValue: String? + let placeholder: String + let characterLimit: Int? + let hasClearButton: Bool + let isSecureTextEntry: Bool + let returnKeyType: UIReturnKeyType + let keyboardType: UIKeyboardType + let autocapitalizationType: UITextAutocapitalizationType + let autocorrectionType: UITextAutocorrectionType + let isInitiallyFocused: Bool + let externalState: ExternalState + let shouldChangeText: ((String) -> Bool)? + let returnKeyAction: (() -> Void)? + + public init( + context: AccountContext, + initialValue: String? = nil, + placeholder: String, + characterLimit: Int? = nil, + hasClearButton: Bool = false, + isSecureTextEntry: Bool = false, + returnKeyType: UIReturnKeyType = .done, + keyboardType: UIKeyboardType = .default, + autocapitalizationType: UITextAutocapitalizationType = .sentences, + autocorrectionType: UITextAutocorrectionType = .default, + isInitiallyFocused: Bool = false, + externalState: ExternalState, + shouldChangeText: ((String) -> Bool)? = nil, + returnKeyAction: (() -> Void)? = nil + ) { + self.context = context + self.initialValue = initialValue + self.placeholder = placeholder + self.characterLimit = characterLimit + self.hasClearButton = hasClearButton + self.isSecureTextEntry = isSecureTextEntry + self.returnKeyType = returnKeyType + self.keyboardType = keyboardType + self.autocapitalizationType = autocapitalizationType + self.autocorrectionType = autocorrectionType + self.isInitiallyFocused = isInitiallyFocused + self.externalState = externalState + self.shouldChangeText = shouldChangeText + self.returnKeyAction = returnKeyAction + } + + public static func ==(lhs: AlertInputFieldComponent, rhs: AlertInputFieldComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.initialValue != rhs.initialValue { + return false + } + if lhs.placeholder != rhs.placeholder { + return false + } + if lhs.characterLimit != rhs.characterLimit { + return false + } + if lhs.hasClearButton != rhs.hasClearButton { + return false + } + if lhs.isSecureTextEntry != rhs.isSecureTextEntry { + return false + } + if lhs.returnKeyType != rhs.returnKeyType { + return false + } + if lhs.keyboardType != rhs.keyboardType { + return false + } + if lhs.autocapitalizationType != rhs.autocapitalizationType { + return false + } + if lhs.autocorrectionType != rhs.autocorrectionType { + return false + } + if lhs.isInitiallyFocused != rhs.isInitiallyFocused { + return false + } + return true + } + + private final class TextField: UITextField { + var sideInset: CGFloat = 0.0 + + override func textRect(forBounds bounds: CGRect) -> CGRect { + return CGRect(origin: CGPoint(x: self.sideInset, y: 0.0), size: CGSize(width: bounds.width - self.sideInset * 2.0, height: bounds.height)) + } + + override func editingRect(forBounds bounds: CGRect) -> CGRect { + return CGRect(origin: CGPoint(x: self.sideInset, y: 0.0), size: CGSize(width: bounds.width - self.sideInset * 2.0, height: bounds.height)) + } + } + + public final class View: UIView, UITextFieldDelegate { + private let background = ComponentView() + private let textField = TextField() + private let placeholder = ComponentView() + private let clearButton = ComponentView() + + private var component: AlertInputFieldComponent? + private weak var state: EmptyComponentState? + + private var isUpdating = false + + var currentText: String { + return self.textField.text ?? "" + } + + private var clearOnce: Bool = false + + func activateInput() { + self.textField.becomeFirstResponder() + } + + func animateError() { + if let component = self.component, component.isInitiallyFocused { + self.clearOnce = true + } + self.textField.layer.addShakeAnimation() + + HapticFeedback().error() + } + + public func textFieldShouldReturn(_ textField: UITextField) -> Bool { + self.component?.returnKeyAction?() + return false + } + + @objc private func textDidChange() { + if !self.isUpdating { + self.state?.updated(transition: .immediate) + } + } + + public func textFieldDidBeginEditing(_ textField: UITextField) { + self.clearButton.view?.isHidden = self.currentText.isEmpty + } + + public func textFieldDidEndEditing(_ textField: UITextField) { + self.clearButton.view?.isHidden = true + } + + public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { + guard let component = self.component else { + return true + } + + if self.clearOnce { + self.clearOnce = false + if range.length > string.count { + textField.text = "" + return false + } + } + + let updatedText = ((textField.text ?? "") as NSString).replacingCharacters(in: range, with: string) + if let shouldChangeText = component.shouldChangeText { + return shouldChangeText(updatedText) + } + return true + } + + public func setText(text: String) { + self.textField.text = text + if !self.isUpdating { + self.state?.updated(transition: .immediate, isLocal: true) + } + } + + func update(component: AlertInputFieldComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.isUpdating = true + defer { + self.isUpdating = false + } + + var resetText: String? + if self.component == nil { + resetText = component.initialValue + component.externalState.animateError = { [weak self] in + self?.animateError() + } + component.externalState.activateInput = { [weak self] in + self?.activateInput() + } + } + + let isFirstTime = self.component == nil + + self.component = component + self.state = state + + let environment = environment[AlertComponentEnvironment.self] + + let topInset: CGFloat = 15.0 + + if self.textField.superview == nil { + self.addSubview(self.textField) + self.textField.delegate = self + self.textField.addTarget(self, action: #selector(self.textDidChange), for: .editingChanged) + } + if self.textField.autocapitalizationType != component.autocapitalizationType { + self.textField.autocapitalizationType = component.autocapitalizationType + } + if self.textField.autocorrectionType != component.autocorrectionType { + self.textField.autocorrectionType = component.autocorrectionType + } + if self.textField.isSecureTextEntry != component.isSecureTextEntry { + self.textField.isSecureTextEntry = component.isSecureTextEntry + } + if self.textField.returnKeyType != component.returnKeyType { + self.textField.returnKeyType = component.returnKeyType + } + self.textField.keyboardAppearance = environment.theme.overallDarkAppearance ? .dark : .light + if let resetText { + self.textField.text = resetText + } + + self.textField.font = Font.regular(17.0) + self.textField.textColor = environment.theme.actionSheet.primaryTextColor + self.textField.tintColor = environment.theme.actionSheet.controlAccentColor + self.textField.sideInset = 16.0 + + let backgroundPadding: CGFloat = 14.0 + let size = CGSize(width: availableSize.width, height: 50.0) + + let backgroundSize = self.background.update( + transition: transition, + component: AnyComponent( + FilledRoundedRectangleComponent(color: environment.theme.actionSheet.primaryTextColor.withMultipliedAlpha(0.1), cornerRadius: .value(25.0), smoothCorners: false) + ), + environment: {}, + containerSize: CGSize(width: size.width + backgroundPadding * 2.0, height: size.height) + ) + let backgroundFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - backgroundSize.width) / 2.0), y: topInset ), size: backgroundSize) + if let backgroundView = self.background.view { + if backgroundView.superview == nil { + self.addSubview(backgroundView) + } + transition.setFrame(view: backgroundView, frame: backgroundFrame) + } + + let textFieldSize = CGSize(width: availableSize.width - 24.0, height: 50.0) + let textFieldFrame = CGRect(origin: CGPoint(x: -12.0, y: topInset), size: textFieldSize) + transition.setFrame(view: self.textField, frame: textFieldFrame) + + let placeholderSize = self.placeholder.update( + transition: .immediate, + component: AnyComponent( + MultilineTextComponent(text: .plain(NSAttributedString( + string: component.placeholder, + font: Font.regular(17.0), + textColor: environment.theme.actionSheet.primaryTextColor.withMultipliedAlpha(0.4) + ))) + ), + environment: {}, + containerSize: CGSize(width: size.width, height: 50.0) + ) + let placeholderFrame = CGRect(origin: CGPoint(x: 4.0, y: floorToScreenPixels(textFieldFrame.midY - placeholderSize.height / 2.0)), size: placeholderSize) + if let placeholderView = self.placeholder.view { + if placeholderView.superview == nil { + placeholderView.isUserInteractionEnabled = false + self.addSubview(placeholderView) + } + placeholderView.frame = placeholderFrame + placeholderView.isHidden = !self.currentText.isEmpty + } + + if component.hasClearButton { + let clearButtonSize = self.clearButton.update( + transition: transition, + component: AnyComponent(PlainButtonComponent( + content: AnyComponent(BundleIconComponent( + name: "Components/Search Bar/Clear", + tintColor: environment.theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.4) + )), + effectAlignment: .center, + minSize: CGSize(width: 44.0, height: 44.0), + action: { [weak self] in + guard let self else { + return + } + self.setText(text: "") + }, + animateAlpha: false, + animateScale: true + )), + environment: {}, + containerSize: CGSize(width: 44.0, height: 44.0) + ) + if let clearButtonView = self.clearButton.view { + if clearButtonView.superview == nil { + self.addSubview(clearButtonView) + } + transition.setFrame(view: clearButtonView, frame: CGRect(origin: CGPoint(x: availableSize.width - clearButtonSize.width + 11.0, y: topInset + floor((size.height - clearButtonSize.height) * 0.5)), size: clearButtonSize)) + clearButtonView.isHidden = self.currentText.isEmpty || !self.textField.isFirstResponder + } + } else if let clearButtonView = self.clearButton.view, clearButtonView.superview != nil { + clearButtonView.removeFromSuperview() + } + + if isFirstTime && component.isInitiallyFocused { + self.activateInput() + } + + component.externalState.value = self.currentText + component.externalState.valuePromise.set(self.currentText) + + return CGSize(width: availableSize.width, height: size.height + topInset) + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/AlertComponent/AlertMultilineInputFieldComponent/BUILD b/submodules/TelegramUI/Components/AlertComponent/AlertMultilineInputFieldComponent/BUILD new file mode 100644 index 00000000..8a8f24ed --- /dev/null +++ b/submodules/TelegramUI/Components/AlertComponent/AlertMultilineInputFieldComponent/BUILD @@ -0,0 +1,27 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "AlertMultilineInputFieldComponent", + module_name = "AlertMultilineInputFieldComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/AccountContext", + "//submodules/ComponentFlow", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/TelegramPresentationData", + "//submodules/TextFormat", + "//submodules/Components/MultilineTextComponent", + "//submodules/TelegramUI/Components/AlertComponent", + "//submodules/TelegramUI/Components/TextFieldComponent", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/AlertComponent/AlertMultilineInputFieldComponent/Sources/AlertMultilineInputFieldComponent.swift b/submodules/TelegramUI/Components/AlertComponent/AlertMultilineInputFieldComponent/Sources/AlertMultilineInputFieldComponent.swift new file mode 100644 index 00000000..49f9b1e7 --- /dev/null +++ b/submodules/TelegramUI/Components/AlertComponent/AlertMultilineInputFieldComponent/Sources/AlertMultilineInputFieldComponent.swift @@ -0,0 +1,361 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import ComponentFlow +import SwiftSignalKit +import TelegramCore +import TelegramPresentationData +import AlertComponent +import TextFieldComponent +import MultilineTextComponent +import AccountContext +import TextFormat + +public final class AlertMultilineInputFieldComponent: Component { + public typealias EnvironmentType = AlertComponentEnvironment + + public class ExternalState { + public fileprivate(set) var value: NSAttributedString = NSAttributedString() + public fileprivate(set) var animateError: () -> Void = {} + public fileprivate(set) var activateInput: () -> Void = {} + fileprivate let valuePromise = ValuePromise(NSAttributedString()) + public var valueSignal: Signal { + return self.valuePromise.get() + } + + public var textAndEntities: (String, [MessageTextEntity]) { + let text = self.value.string + let entities = generateChatInputTextEntities(self.value) + return (text, entities) + } + + public init() { + } + } + + public enum FormatMenuAvailability: Equatable { + public enum Action: CaseIterable { + case bold + case italic + case monospace + case link + case strikethrough + case underline + case spoiler + case quote + case code + + public static var all: [Action] = [ + .bold, + .italic, + .monospace, + .link, + .strikethrough, + .underline, + .spoiler, + .quote, + .code + ] + + var textFieldValue: TextFieldComponent.FormatMenuAvailability.Action { + switch self { + case .bold: + return .bold + case .italic: + return .italic + case .monospace: + return .monospace + case .link: + return .link + case .strikethrough: + return .strikethrough + case .underline: + return .underline + case .spoiler: + return .spoiler + case .quote: + return .quote + case .code: + return .code + } + } + } + case available([Action]) + case none + + var textFieldValue: TextFieldComponent.FormatMenuAvailability { + switch self { + case let .available(actions): + return .available(actions.map { $0.textFieldValue }) + case .none: + return .none + } + } + } + + public enum EmptyLineHandling { + case allowed + case oneConsecutive + case notAllowed + + var textFieldValue: TextFieldComponent.EmptyLineHandling { + switch self { + case .allowed: + return .allowed + case .oneConsecutive: + return .oneConsecutive + case .notAllowed: + return .notAllowed + } + } + } + + let context: AccountContext + let initialValue: NSAttributedString? + let placeholder: String + let prefix: NSAttributedString? + let characterLimit: Int? + let returnKeyType: UIReturnKeyType + let keyboardType: UIKeyboardType + let autocapitalizationType: UITextAutocapitalizationType + let autocorrectionType: UITextAutocorrectionType + let formatMenuAvailability: FormatMenuAvailability + let emptyLineHandling: EmptyLineHandling + let isInitiallyFocused: Bool + let externalState: ExternalState + let present: (ViewController) -> Void + let returnKeyAction: (() -> Void)? + + public init( + context: AccountContext, + initialValue: NSAttributedString? = nil, + placeholder: String, + prefix: NSAttributedString? = nil, + characterLimit: Int? = nil, + returnKeyType: UIReturnKeyType = .default, + keyboardType: UIKeyboardType = .default, + autocapitalizationType: UITextAutocapitalizationType = .sentences, + autocorrectionType: UITextAutocorrectionType = .default, + formatMenuAvailability: FormatMenuAvailability = .none, + emptyLineHandling: EmptyLineHandling = .allowed, + isInitiallyFocused: Bool = false, + externalState: ExternalState, + present: @escaping (ViewController) -> Void = { _ in }, + returnKeyAction: (() -> Void)? = nil + ) { + self.context = context + self.initialValue = initialValue + self.placeholder = placeholder + self.prefix = prefix + self.characterLimit = characterLimit + self.returnKeyType = returnKeyType + self.keyboardType = keyboardType + self.autocapitalizationType = autocapitalizationType + self.autocorrectionType = autocorrectionType + self.formatMenuAvailability = formatMenuAvailability + self.emptyLineHandling = emptyLineHandling + self.isInitiallyFocused = isInitiallyFocused + self.externalState = externalState + self.present = present + self.returnKeyAction = returnKeyAction + } + + public static func ==(lhs: AlertMultilineInputFieldComponent, rhs: AlertMultilineInputFieldComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.initialValue != rhs.initialValue { + return false + } + if lhs.placeholder != rhs.placeholder { + return false + } + if lhs.prefix != rhs.prefix { + return false + } + if lhs.returnKeyType != rhs.returnKeyType { + return false + } + if lhs.characterLimit != rhs.characterLimit { + return false + } + if lhs.keyboardType != rhs.keyboardType { + return false + } + if lhs.autocapitalizationType != rhs.autocapitalizationType { + return false + } + if lhs.autocorrectionType != rhs.autocorrectionType { + return false + } + if lhs.formatMenuAvailability != rhs.formatMenuAvailability { + return false + } + if lhs.emptyLineHandling != rhs.emptyLineHandling { + return false + } + if lhs.isInitiallyFocused != rhs.isInitiallyFocused { + return false + } + return true + } + + public final class View: UIView { + private let background = ComponentView() + private let textField = ComponentView() + private let textFieldExternalState = TextFieldComponent.ExternalState() + private let placeholder = ComponentView() + + private var component: AlertMultilineInputFieldComponent? + private weak var state: EmptyComponentState? + + func activateInput() { + if let textFieldView = self.textField.view as? TextFieldComponent.View { + textFieldView.activateInput() + } + } + + func animateError() { + if let textFieldView = self.textField.view { + textFieldView.layer.addShakeAnimation() + } + HapticFeedback().error() + } + + func update(component: AlertMultilineInputFieldComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + var resetText: NSAttributedString? + if self.component == nil { + resetText = component.initialValue + component.externalState.animateError = { [weak self] in + self?.animateError() + } + component.externalState.activateInput = { [weak self] in + self?.activateInput() + } + } + + let isFirstTime = self.component == nil + + self.component = component + self.state = state + + let environment = environment[AlertComponentEnvironment.self] + + let topInset: CGFloat = 15.0 + let horizontalInset: CGFloat = 4.0 + let verticalInset: CGFloat = 11.0 - UIScreenPixel + + let textFieldSize = self.textField.update( + transition: transition, + component: AnyComponent(TextFieldComponent( + context: component.context, + theme: environment.theme, + strings: environment.strings, + externalState: self.textFieldExternalState, + fontSize: 17.0, + textColor: environment.theme.actionSheet.primaryTextColor, + accentColor: environment.theme.actionSheet.controlAccentColor, + insets: UIEdgeInsets(top: 4.0, left: 0.0, bottom: 4.0, right: 0.0), + hideKeyboard: false, + customInputView: nil, + resetText: resetText, + isOneLineWhenUnfocused: false, + characterLimit: component.characterLimit, + emptyLineHandling: component.emptyLineHandling.textFieldValue, + formatMenuAvailability: component.formatMenuAvailability.textFieldValue, + returnKeyType: component.returnKeyType, + keyboardType: component.keyboardType, + autocapitalizationType: component.autocapitalizationType, + autocorrectionType: component.autocorrectionType, + lockedFormatAction: { + }, + present: { [weak self] c in + guard let self, let component = self.component else { + return + } + component.present(c) + }, + paste: { _ in + }, + returnKeyAction: { [weak self] in + guard let self, let component = self.component else { + return + } + component.returnKeyAction?() + }, + backspaceKeyAction: { + } + )), + environment: {}, + containerSize: CGSize(width: availableSize.width + horizontalInset * 2.0, height: availableSize.height) + ) + component.externalState.value = self.textFieldExternalState.text + component.externalState.valuePromise.set(component.externalState.value) + + let backgroundPadding: CGFloat = 14.0 + let size = CGSize(width: availableSize.width, height: max(50.0, floor(textFieldSize.height + verticalInset * 2.0))) + + let backgroundSize = self.background.update( + transition: transition, + component: AnyComponent( + FilledRoundedRectangleComponent(color: environment.theme.actionSheet.primaryTextColor.withMultipliedAlpha(0.1), cornerRadius: .value(25.0), smoothCorners: false) + ), + environment: {}, + containerSize: CGSize(width: size.width + backgroundPadding * 2.0, height: size.height) + ) + let backgroundFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - backgroundSize.width) / 2.0), y: topInset ), size: backgroundSize) + if let backgroundView = self.background.view { + if backgroundView.superview == nil { + self.addSubview(backgroundView) + } + transition.setFrame(view: backgroundView, frame: backgroundFrame) + } + + let textFieldFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - textFieldSize.width) / 2.0), y: topInset + 11.0 - UIScreenPixel), size: textFieldSize) + if let textFieldView = self.textField.view { + if textFieldView.superview == nil { + self.addSubview(textFieldView) + self.textField.parentState = state + } + transition.setFrame(view: textFieldView, frame: textFieldFrame) + } + + let placeholderSize = self.placeholder.update( + transition: .immediate, + component: AnyComponent( + MultilineTextComponent(text: .plain(NSAttributedString( + string: component.placeholder, + font: Font.regular(17.0), + textColor: environment.theme.actionSheet.primaryTextColor.withMultipliedAlpha(0.4) + ))) + ), + environment: {}, + containerSize: CGSize(width: size.width, height: 50.0) + ) + let placeholderFrame = CGRect(origin: CGPoint(x: 4.0, y: floorToScreenPixels(textFieldFrame.midY - placeholderSize.height / 2.0)), size: placeholderSize) + if let placeholderView = self.placeholder.view { + if placeholderView.superview == nil { + placeholderView.isUserInteractionEnabled = false + self.addSubview(placeholderView) + } + placeholderView.frame = placeholderFrame + placeholderView.isHidden = self.textFieldExternalState.hasText + } + + if isFirstTime && component.isInitiallyFocused { + self.activateInput() + } + + return CGSize(width: availableSize.width, height: size.height + topInset) + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/AlertComponent/AlertTableComponent/BUILD b/submodules/TelegramUI/Components/AlertComponent/AlertTableComponent/BUILD new file mode 100644 index 00000000..9c1d5ce5 --- /dev/null +++ b/submodules/TelegramUI/Components/AlertComponent/AlertTableComponent/BUILD @@ -0,0 +1,23 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "AlertTableComponent", + module_name = "AlertTableComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/ComponentFlow", + "//submodules/TelegramPresentationData", + "//submodules/TelegramUI/Components/AlertComponent", + "//submodules/TelegramUI/Components/Gifts/TableComponent", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/AlertComponent/AlertTableComponent/Sources/AlertTableComponent.swift b/submodules/TelegramUI/Components/AlertComponent/AlertTableComponent/Sources/AlertTableComponent.swift new file mode 100644 index 00000000..aabc6e85 --- /dev/null +++ b/submodules/TelegramUI/Components/AlertComponent/AlertTableComponent/Sources/AlertTableComponent.swift @@ -0,0 +1,70 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import ComponentFlow +import TelegramPresentationData +import AlertComponent +import TableComponent + +public final class AlertTableComponent: Component { + public typealias EnvironmentType = AlertComponentEnvironment + + let items: [TableComponent.Item] + + public init( + items: [TableComponent.Item] + ) { + self.items = items + } + + public static func ==(lhs: AlertTableComponent, rhs: AlertTableComponent) -> Bool { + if lhs.items != rhs.items { + return false + } + return true + } + + public final class View: UIView { + private let table = ComponentView() + + private var component: AlertTableComponent? + private weak var state: EmptyComponentState? + + func update(component: AlertTableComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + self.state = state + + let environment = environment[AlertComponentEnvironment.self] + + let tableSize = self.table.update( + transition: transition, + component: AnyComponent( + TableComponent( + theme: environment.theme, + items: component.items, + semiTransparent: true + ) + ), + environment: {}, + containerSize: CGSize(width: availableSize.width + 20.0, height: availableSize.height) + ) + let tableFrame = CGRect(origin: CGPoint(x: -10.0, y: 5.0), size: tableSize) + if let tableView = self.table.view { + if tableView.superview == nil { + self.addSubview(tableView) + } + transition.setFrame(view: tableView, frame: tableFrame) + } + return CGSize(width: availableSize.width, height: tableSize.height + 10.0) + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/AlertComponent/AlertTransferHeaderComponent/BUILD b/submodules/TelegramUI/Components/AlertComponent/AlertTransferHeaderComponent/BUILD new file mode 100644 index 00000000..c6734ce7 --- /dev/null +++ b/submodules/TelegramUI/Components/AlertComponent/AlertTransferHeaderComponent/BUILD @@ -0,0 +1,23 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "AlertTransferHeaderComponent", + module_name = "AlertTransferHeaderComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/ComponentFlow", + "//submodules/TelegramPresentationData", + "//submodules/Components/BundleIconComponent", + "//submodules/TelegramUI/Components/AlertComponent", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/AlertComponent/AlertTransferHeaderComponent/Sources/AlertTransferHeaderComponent.swift b/submodules/TelegramUI/Components/AlertComponent/AlertTransferHeaderComponent/Sources/AlertTransferHeaderComponent.swift new file mode 100644 index 00000000..b0ddb6e2 --- /dev/null +++ b/submodules/TelegramUI/Components/AlertComponent/AlertTransferHeaderComponent/Sources/AlertTransferHeaderComponent.swift @@ -0,0 +1,126 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import ComponentFlow +import TelegramPresentationData +import AlertComponent +import BundleIconComponent + +public final class AlertTransferHeaderComponent: Component { + public typealias EnvironmentType = AlertComponentEnvironment + + public enum IconType { + case transfer + case take + } + + let fromComponent: AnyComponentWithIdentity + let toComponent: AnyComponentWithIdentity + let type: IconType + + public init( + fromComponent: AnyComponentWithIdentity, + toComponent: AnyComponentWithIdentity, + type: IconType + ) { + self.fromComponent = fromComponent + self.toComponent = toComponent + self.type = type + } + + public static func ==(lhs: AlertTransferHeaderComponent, rhs: AlertTransferHeaderComponent) -> Bool { + if lhs.fromComponent != rhs.fromComponent { + return false + } + if lhs.toComponent != rhs.toComponent { + return false + } + if lhs.type != rhs.type { + return false + } + return true + } + + public final class View: UIView { + private let from = ComponentView() + private let to = ComponentView() + private let arrow = ComponentView() + + private var component: AlertTransferHeaderComponent? + private weak var state: EmptyComponentState? + + func update(component: AlertTransferHeaderComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + self.state = state + + let environment = environment[AlertComponentEnvironment.self] + + let size: CGSize + let iconName: String + switch component.type { + case .transfer: + iconName = "Peer Info/AlertArrow" + size = CGSize(width: 148.0, height: 60.0) + case .take: + iconName = "Media Editor/CutoutUndo" + size = CGSize(width: 154.0, height: 60.0) + } + let sideInset = floorToScreenPixels((availableSize.width - size.width) / 2.0) + + let fromSize = self.from.update( + transition: transition, + component: component.fromComponent.component, + environment: {}, + containerSize: CGSize(width: 60.0, height: 60.0) + ) + let fromFrame = CGRect(origin: CGPoint(x: sideInset, y: 0.0), size: fromSize) + if let fromView = self.from.view { + if fromView.superview == nil { + self.addSubview(fromView) + } + transition.setFrame(view: fromView, frame: fromFrame) + } + + let arrowSize = self.arrow.update( + transition: transition, + component: AnyComponent( + BundleIconComponent(name: iconName, tintColor: environment.theme.actionSheet.primaryTextColor.withMultipliedAlpha(0.2)) + ), + environment: {}, + containerSize: availableSize + ) + let arrowFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - arrowSize.width) / 2.0), y: floorToScreenPixels((size.height - arrowSize.height) / 2.0)), size: arrowSize) + if let arrowView = self.arrow.view { + if arrowView.superview == nil { + self.addSubview(arrowView) + } + transition.setFrame(view: arrowView, frame: arrowFrame) + } + + let toSize = self.to.update( + transition: transition, + component: component.toComponent.component, + environment: {}, + containerSize: CGSize(width: 60.0, height: 60.0) + ) + let toFrame = CGRect(origin: CGPoint(x: availableSize.width - toSize.width - sideInset, y: 0.0), size: toSize) + if let toView = self.to.view { + if toView.superview == nil { + self.addSubview(toView) + } + transition.setFrame(view: toView, frame: toFrame) + } + + return CGSize(width: availableSize.width, height: size.height + 11.0) + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/AlertComponent/BUILD b/submodules/TelegramUI/Components/AlertComponent/BUILD index 076bdbcc..f84c8e13 100644 --- a/submodules/TelegramUI/Components/AlertComponent/BUILD +++ b/submodules/TelegramUI/Components/AlertComponent/BUILD @@ -12,9 +12,19 @@ swift_library( deps = [ "//submodules/AsyncDisplayKit", "//submodules/Display", - "//submodules/TelegramPresentationData", "//submodules/ComponentFlow", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/TelegramPresentationData", + "//submodules/TelegramCore", + "//submodules/AccountContext", + "//submodules/Markdown", + "//submodules/TextFormat", "//submodules/Components/ComponentDisplayAdapters", + "//submodules/Components/ViewControllerComponent", + "//submodules/Components/MultilineTextComponent", + "//submodules/Components/MultilineTextWithEntitiesComponent", + "//submodules/Components/ActivityIndicatorComponent", + "//submodules/TelegramUI/Components/GlassBackgroundComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/AlertComponent/Sources/AlertActionComponent.swift b/submodules/TelegramUI/Components/AlertComponent/Sources/AlertActionComponent.swift new file mode 100644 index 00000000..97c171af --- /dev/null +++ b/submodules/TelegramUI/Components/AlertComponent/Sources/AlertActionComponent.swift @@ -0,0 +1,228 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import ComponentFlow +import SwiftSignalKit +import AccountContext +import TelegramPresentationData +import MultilineTextComponent +import GlassBackgroundComponent +import ActivityIndicatorComponent + +private let titleFont = Font.medium(17.0) +private let boldTitleFont = Font.semibold(17.0) + +final class AlertActionComponent: Component { + typealias EnvironmentType = AlertComponentEnvironment + + static let actionHeight: CGFloat = 48.0 + + struct Theme: Equatable { + enum Font { + case regular + case bold + } + + let background: UIColor + let foreground: UIColor + let secondary: UIColor + let font: Font + } + + let theme: Theme + let title: String + let isHighlighted: Bool + let isEnabled: Signal + let progress: Signal + + init( + theme: Theme, + title: String, + isHighlighted: Bool, + isEnabled: Signal, + progress: Signal + ) { + self.theme = theme + self.title = title + self.isHighlighted = isHighlighted + self.isEnabled = isEnabled + self.progress = progress + } + + static func ==(lhs: AlertActionComponent, rhs: AlertActionComponent) -> Bool { + if lhs.theme != rhs.theme { + return false + } + if lhs.title != rhs.title { + return false + } + if lhs.isHighlighted != rhs.isHighlighted { + return false + } + return true + } + + final class View: UIView { + private let backgroundView = UIView() + private let title = ComponentView() + private var activity: ComponentView? + + private var component: AlertActionComponent? + private weak var state: EmptyComponentState? + + private var isEnabledDisposable: Disposable? + private var isEnabled = true + + private var progressDisposable: Disposable? + private var hasProgress = false + + private var isUpdating = false + + override init(frame: CGRect) { + super.init(frame: frame) + + self.backgroundView.clipsToBounds = true + self.addSubview(self.backgroundView) + } + + required init?(coder: NSCoder) { + preconditionFailure() + } + + deinit { + self.isEnabledDisposable?.dispose() + self.progressDisposable?.dispose() + } + + func update(component: AlertActionComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.isUpdating = true + defer { + self.isUpdating = false + } + if self.component == nil { + self.isEnabledDisposable = (component.isEnabled + |> deliverOnMainQueue).start(next: { [weak self] isEnabled in + guard let self else { + return + } + self.isEnabled = isEnabled + if !self.isUpdating { + self.state?.updated(transition: .easeInOut(duration: 0.25)) + } + }) + + self.progressDisposable = (component.progress + |> deliverOnMainQueue).start(next: { [weak self] hasProgress in + guard let self else { + return + } + self.hasProgress = hasProgress + if !self.isUpdating { + self.state?.updated(transition: .easeInOut(duration: 0.25)) + } + }) + } + self.component = component + self.state = state + + let attributedString = NSMutableAttributedString(string: component.title, font: component.theme.font == .bold ? boldTitleFont : titleFont, textColor: .white, paragraphAlignment: .center) + if let range = attributedString.string.range(of: "$") { + attributedString.addAttribute(.attachment, value: UIImage(bundleImageName: "Item List/PremiumIcon")!, range: NSRange(range, in: attributedString.string)) + attributedString.addAttribute(.foregroundColor, value: UIColor.white, range: NSRange(range, in: attributedString.string)) + attributedString.addAttribute(.baselineOffset, value: 2.0, range: NSRange(range, in: attributedString.string)) + } + + let titlePadding: CGFloat = 16.0 + let titleSize = self.title.update( + transition: .immediate, + component: AnyComponent(MultilineTextComponent( + text: .plain(attributedString), + horizontalAlignment: .center, + maximumNumberOfLines: 1, + tintColor: component.theme.foreground + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - titlePadding * 2.0, height: availableSize.height) + ) + if let titleView = self.title.view { + if titleView.superview == nil { + self.addSubview(titleView) + } + titleView.bounds = CGRect(origin: .zero, size: titleSize) + transition.setAlpha(view: titleView, alpha: self.hasProgress ? 0.0 : 1.0) + } + + if self.hasProgress { + let activity: ComponentView + if let current = self.activity { + activity = current + } else { + activity = ComponentView() + self.activity = activity + } + let activitySize = CGSize(width: 18.0, height: 18.0) + let _ = activity.update( + transition: transition, + component: AnyComponent(ActivityIndicatorComponent(color: component.theme.secondary)), + environment: {}, + containerSize: activitySize + ) + if let activityView = activity.view { + activityView.bounds = CGRect(origin: .zero, size: activitySize) + } + } else if let activity = self.activity { + self.activity = nil + if let activityView = activity.view { + transition.setAlpha(view: activityView, alpha: 0.0, completion: { _ in + activityView.removeFromSuperview() + }) + } + } + + let buttonAlpha: CGFloat + if self.isEnabled { + buttonAlpha = component.isHighlighted ? 0.35 : 1.0 + } else { + buttonAlpha = 0.2 + } + + transition.setBackgroundColor(view: self.backgroundView, color: component.theme.background) + transition.setAlpha(view: self.backgroundView, alpha: buttonAlpha) + self.backgroundView.layer.cornerRadius = availableSize.height * 0.5 + + return CGSize(width: titleSize.width + titlePadding * 2.0, height: availableSize.height) + } + + func applySize(size: CGSize, transition: ComponentTransition) { + transition.setFrame(view: self.backgroundView, frame: CGRect(origin: .zero, size: size)) + + if let titleView = self.title.view { + let titleSize = titleView.bounds.size + let titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: floorToScreenPixels((size.height - titleSize.height) / 2.0)), size: titleSize) + transition.setFrame(view: titleView, frame: titleFrame) + } + + if let activityView = self.activity?.view { + var activityTransition = transition + if activityView.superview == nil { + self.addSubview(activityView) + transition.animateAlpha(view: activityView, from: 0.0, to: 1.0) + activityTransition = .immediate + } + let activitySize = activityView.bounds.size + let activityFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - activitySize.width) / 2.0), y: floorToScreenPixels((size.height - activitySize.height) / 2.0)), size: activitySize) + activityTransition.setPosition(view: activityView, position: activityFrame.center) + activityView.transform = CGAffineTransformMakeScale(0.7, 0.7) + } + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/AlertComponent/Sources/AlertComponent.swift b/submodules/TelegramUI/Components/AlertComponent/Sources/AlertComponent.swift index d7b9b066..bd4550c4 100644 --- a/submodules/TelegramUI/Components/AlertComponent/Sources/AlertComponent.swift +++ b/submodules/TelegramUI/Components/AlertComponent/Sources/AlertComponent.swift @@ -1,395 +1,940 @@ import Foundation import UIKit -import Display -import TelegramPresentationData -import ComponentFlow -import ComponentDisplayAdapters import AsyncDisplayKit +import Display +import ComponentFlow +import SwiftSignalKit +import AccountContext +import TelegramPresentationData +import MultilineTextComponent +import ViewControllerComponent +import ComponentDisplayAdapters +import GlassBackgroundComponent -private let alertWidth: CGFloat = 270.0 - -public enum ComponentAlertActionType { - case genericAction - case defaultAction - case destructiveAction - case defaultDestructiveAction -} - -public struct ComponentAlertAction { - public let type: ComponentAlertActionType - public let title: String - public let action: () -> Void +public final class AlertComponentEnvironment: Equatable { + public let theme: PresentationTheme + public let strings: PresentationStrings - public init(type: ComponentAlertActionType, title: String, action: @escaping () -> Void) { - self.type = type - self.title = title - self.action = action - } -} - -public final class ComponentAlertContentActionNode: HighlightableButtonNode { - private var theme: AlertControllerTheme - public var action: ComponentAlertAction { - didSet { - self.updateTitle() - } - } - - private let backgroundNode: ASDisplayNode - - public var highlightedUpdated: (Bool) -> Void = { _ in } - - public init(theme: AlertControllerTheme, action: ComponentAlertAction) { + public init( + theme: PresentationTheme, + strings: PresentationStrings + ) { self.theme = theme - self.action = action - - self.backgroundNode = ASDisplayNode() - self.backgroundNode.isLayerBacked = true - self.backgroundNode.alpha = 0.0 - - super.init() - - self.titleNode.maximumNumberOfLines = 2 - - self.highligthedChanged = { [weak self] value in - if let strongSelf = self { - strongSelf.setHighlighted(value, animated: true) - } + self.strings = strings + } + + public static func ==(lhs: AlertComponentEnvironment, rhs: AlertComponentEnvironment) -> Bool { + if lhs.theme !== rhs.theme { + return false } - - self.updateTheme(theme) - } - - public override func didLoad() { - super.didLoad() - - self.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside) - - self.pointerInteraction = PointerInteraction(node: self, style: .hover, willEnter: { [weak self] in - if let strongSelf = self { - strongSelf.setHighlighted(true, animated: false) - } - }, willExit: { [weak self] in - if let strongSelf = self { - strongSelf.setHighlighted(false, animated: false) - } - }) - } - - public func performAction() { - if self.actionEnabled { - self.action.action() + if lhs.strings !== rhs.strings { + return false } - } - - public func setHighlighted(_ highlighted: Bool, animated: Bool) { - self.highlightedUpdated(highlighted) - if highlighted { - if self.backgroundNode.supernode == nil { - self.insertSubnode(self.backgroundNode, at: 0) - } - self.backgroundNode.alpha = 1.0 - } else { - if animated { - UIView.animate(withDuration: 0.3, animations: { - self.backgroundNode.alpha = 0.0 - }) - } else { - self.backgroundNode.alpha = 0.0 - } - } - } - public var actionEnabled: Bool = true { - didSet { - self.isUserInteractionEnabled = self.actionEnabled - self.updateTitle() - } - } - - public func updateTheme(_ theme: AlertControllerTheme) { - self.theme = theme - self.backgroundNode.backgroundColor = theme.highlightedItemColor - self.updateTitle() - } - - private func updateTitle() { - var font = Font.regular(theme.baseFontSize) - var color: UIColor - switch self.action.type { - case .defaultAction, .genericAction: - color = self.actionEnabled ? self.theme.accentColor : self.theme.disabledColor - case .destructiveAction, .defaultDestructiveAction: - color = self.actionEnabled ? self.theme.destructiveColor : self.theme.disabledColor - } - switch self.action.type { - case .defaultAction, .defaultDestructiveAction: - font = Font.semibold(theme.baseFontSize) - case .destructiveAction, .genericAction: - break - } - self.setAttributedTitle(NSAttributedString(string: self.action.title, font: font, textColor: color, paragraphAlignment: .center), for: []) - self.accessibilityLabel = self.action.title - self.accessibilityTraits = [.button] - } - - @objc func pressed() { - self.action.action() - } - - override public func layout() { - super.layout() - - self.backgroundNode.frame = self.bounds + return true } } -public enum ComponentAlertContentActionLayout { - case horizontal - case vertical -} - -public final class ComponentAlertContentNode: AlertContentNode { - private var theme: AlertControllerTheme - private let actionLayout: ComponentAlertContentActionLayout +private final class AlertScreenComponent: Component { + typealias EnvironmentType = ViewControllerComponentContainer.Environment - private let content: AnyComponent - private let contentView = ComponentView() + let configuration: AlertScreen.Configuration + let content: Signal<[AnyComponentWithIdentity], NoError> + let actions: Signal<[AlertScreen.Action], NoError> + let ready: Promise - private let actionNodesSeparator: ASDisplayNode - private let actionNodes: [ComponentAlertContentActionNode] - private let actionVerticalSeparators: [ASDisplayNode] - - private var validLayout: CGSize? - - private let _dismissOnOutsideTap: Bool - override public var dismissOnOutsideTap: Bool { - return self._dismissOnOutsideTap - } - - private var highlightedItemIndex: Int? = nil - - public init(theme: AlertControllerTheme, content: AnyComponent, actions: [ComponentAlertAction], actionLayout: ComponentAlertContentActionLayout, dismissOnOutsideTap: Bool) { - self.theme = theme - self.actionLayout = actionLayout - self._dismissOnOutsideTap = dismissOnOutsideTap + init( + configuration: AlertScreen.Configuration, + content: Signal<[AnyComponentWithIdentity], NoError>, + actions: Signal<[AlertScreen.Action], NoError>, + ready: Promise + ) { + self.configuration = configuration self.content = content + self.actions = actions + self.ready = ready + } + + static func ==(lhs: AlertScreenComponent, rhs: AlertScreenComponent) -> Bool { + return true + } + + enum KeyCommand { + case up + case down + case left + case right + case escape + case enter + } + + final class View: UIView, UIGestureRecognizerDelegate { + private let dimView = UIView() + private let containerView = GlassBackgroundContainerView() + private let backgroundView = GlassBackgroundView() - self.actionNodesSeparator = ASDisplayNode() - self.actionNodesSeparator.isUserInteractionEnabled = false - self.actionNodesSeparator.backgroundColor = theme.separatorColor + private var disposable: Disposable? + private var content: [AnyComponentWithIdentity]? + private var actions: [AlertScreen.Action]? - self.actionNodes = actions.map { action -> ComponentAlertContentActionNode in - return ComponentAlertContentActionNode(theme: theme, action: action) - } + private var contentItems: [AnyHashable: ComponentView] = [:] + private var actionItems: [AnyHashable: ComponentView] = [:] - var actionVerticalSeparators: [ASDisplayNode] = [] - if actions.count > 1 { - for _ in 0 ..< actions.count - 1 { - let separatorNode = ASDisplayNode() - separatorNode.isLayerBacked = true - separatorNode.backgroundColor = theme.separatorColor - actionVerticalSeparators.append(separatorNode) - } - } - self.actionVerticalSeparators = actionVerticalSeparators - - super.init() - - self.addSubnode(self.actionNodesSeparator) - - var i = 0 - for actionNode in self.actionNodes { - self.addSubnode(actionNode) + private var highlightedAction: AnyHashable? + private let hapticFeedback = HapticFeedback() + + private enum ActionLayout { + case horizontal + case vertical + case verticalReversed - let index = i - actionNode.highlightedUpdated = { [weak self] highlighted in - if highlighted { - self?.highlightedItemIndex = index + var isVertical: Bool { + switch self { + case .vertical, .verticalReversed: + return true + default: + return false } } - i += 1 + } + private var effectiveActionLayout: ActionLayout = .horizontal + + fileprivate var dismissedByTapOutside = false + + private var isUpdating: Bool = false + private var component: AlertScreenComponent? + private var environment: EnvironmentType? + private weak var state: EmptyComponentState? + + override init(frame: CGRect) { + super.init(frame: frame) + + self.dimView.alpha = 0.0 + self.dimView.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.2) + + self.addSubview(self.dimView) + self.addSubview(self.containerView) + self.containerView.contentView.addSubview(self.backgroundView) + + self.dimView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapped))) + + let tapRecognizer = ActionSelectionGestureRecognizer(target: self, action: #selector(self.actionTapped(_:))) + tapRecognizer.delegate = self + self.backgroundView.addGestureRecognizer(tapRecognizer) } - for separatorNode in self.actionVerticalSeparators { - self.addSubnode(separatorNode) + required init?(coder: NSCoder) { + preconditionFailure() } - } - - func setHighlightedItemIndex(_ index: Int?, update: Bool = false) { - self.highlightedItemIndex = index - if update { - var i = 0 - for actionNode in self.actionNodes { - if i == index { - actionNode.setHighlighted(true, animated: false) - } else { - actionNode.setHighlighted(false, animated: false) + deinit { + self.disposable?.dispose() + } + + override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + if gestureRecognizer is ActionSelectionGestureRecognizer { + let location = gestureRecognizer.location(in: self.backgroundView) + for (_, action) in self.actionItems { + if let actionView = action.view, actionView.frame.contains(location) { + return true + } } - i += 1 + return false + } else { + return super.gestureRecognizerShouldBegin(gestureRecognizer) } } - } - - override public func decreaseHighlightedIndex() { - let currentHighlightedIndex = self.highlightedItemIndex ?? 0 - self.setHighlightedItemIndex(max(0, currentHighlightedIndex - 1), update: true) - } - - override public func increaseHighlightedIndex() { - let currentHighlightedIndex = self.highlightedItemIndex ?? -1 - - self.setHighlightedItemIndex(min(self.actionNodes.count - 1, currentHighlightedIndex + 1), update: true) - } - - override public func performHighlightedAction() { - guard let highlightedItemIndex = self.highlightedItemIndex else { - return + @objc private func actionTapped(_ gestureRecognizer: ActionSelectionGestureRecognizer) { + let location = gestureRecognizer.location(in: self.backgroundView) + switch gestureRecognizer.state { + case .began, .changed: + var highlightedActionId: AnyHashable? + for (actionId, action) in self.actionItems { + if let actionView = action.view, actionView.frame.contains(location) { + highlightedActionId = actionId + break + } + } + if self.highlightedAction != highlightedActionId { + self.highlightedAction = highlightedActionId + self.state?.updated(transition: .easeInOut(duration: 0.2)) + + if case .changed = gestureRecognizer.state, highlightedActionId != nil { + self.hapticFeedback.tap() + } + } + case .ended: + if let _ = self.highlightedAction { + self.performHighlightedAction() + self.highlightedAction = nil + self.state?.updated(transition: .easeInOut(duration: 0.2)) + } + case .cancelled: + self.highlightedAction = nil + self.state?.updated(transition: .easeInOut(duration: 0.2)) + default: + break + } } - var i = 0 - for itemNode in self.actionNodes { - if i == highlightedItemIndex { - itemNode.performAction() + @objc private func dimTapped() { + guard let component = self.component, component.configuration.dismissOnOutsideTap else { return } - i += 1 - } - } - - override public func updateTheme(_ theme: AlertControllerTheme) { - self.theme = theme - - self.actionNodesSeparator.backgroundColor = theme.separatorColor - for actionNode in self.actionNodes { - actionNode.updateTheme(theme) - } - for separatorNode in self.actionVerticalSeparators { - separatorNode.backgroundColor = theme.separatorColor + self.dismissedByTapOutside = true + self.requestDismiss() } - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) + func animateIn() { + let alphaTransition = ComponentTransition(animation: .curve(duration: 0.2, curve: .linear)) + let scaleTransition = ComponentTransition(animation: .curve(duration: 0.4, curve: .spring)) + alphaTransition.setAlpha(view: self.dimView, alpha: 1.0) + + scaleTransition.animateScale(view: self.backgroundView, from: 1.15, to: 1.0) + alphaTransition.animateAlpha(view: self.containerView, from: 0.0, to: 1.0) } - } - - override public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - self.validLayout = size - let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0) - - var size = size - size.width = min(size.width, alertWidth) - - let contentSize = self.contentView.update( - transition: ComponentTransition(transition), - component: self.content, - environment: {}, - containerSize: CGSize(width: size.width - insets.left - insets.right, height: 10000.0) - ) - - let actionButtonHeight: CGFloat = 44.0 - - var minActionsWidth: CGFloat = 0.0 - let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) - let actionTitleInsets: CGFloat = 8.0 - - var effectiveActionLayout = self.actionLayout - for actionNode in self.actionNodes { - let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) - if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 { - effectiveActionLayout = .vertical + func animateOut(completion: @escaping () -> Void) { + let transition = ComponentTransition(animation: .curve(duration: 0.2, curve: .linear)) + transition.setAlpha(view: self.dimView, alpha: 0.0, completion: { _ in + completion() + }) + var initialAlpha: CGFloat = 1.0 + if let presentationLayer = self.containerView.layer.presentation() { + initialAlpha = CGFloat(presentationLayer.opacity) } - switch effectiveActionLayout { - case .horizontal: - minActionsWidth += actionTitleSize.width + actionTitleInsets - case .vertical: - minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) + self.containerView.layer.animateAlpha(from: initialAlpha, to: 0.0, duration: 0.2, removeOnCompletion: false) + } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + let result = super.hitTest(point, with: event) + if result === self.containerView.contentView { + return self.dimView + } + return result + } + + func requestDismiss() { + guard let controller = self.environment?.controller() as? AlertScreen else { + return + } + controller.dismiss(completion: nil) + } + + func handleKeyCommand(_ command: KeyCommand) { + switch command { + case .up: + guard self.effectiveActionLayout.isVertical else { + return + } + self.updateActionHighlight(previous: false) + case .down: + guard self.effectiveActionLayout.isVertical else { + return + } + self.updateActionHighlight(previous: true) + case .left: + guard !self.effectiveActionLayout.isVertical else { + return + } + self.updateActionHighlight(previous: true) + case .right: + guard !self.effectiveActionLayout.isVertical else { + return + } + self.updateActionHighlight(previous: false) + case .escape: + self.requestDismiss() + case .enter: + self.performHighlightedAction() } } - let resultSize: CGSize - - var actionsHeight: CGFloat = 0.0 - switch effectiveActionLayout { - case .horizontal: - actionsHeight = actionButtonHeight - case .vertical: - actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) - } - - let contentWidth = alertWidth - insets.left - insets.right - - let contentFrame = CGRect(origin: CGPoint(x: insets.left + floor((contentWidth - contentSize.width) / 2.0), y: insets.top), size: contentSize) - if let contentComponentView = self.contentView.view { - if contentComponentView.superview == nil { - self.view.insertSubview(contentComponentView, belowSubview: self.actionNodesSeparator.view) - transition.updateFrame(view: contentComponentView, frame: contentFrame) + func updateActionHighlight(previous: Bool) { + guard let actions = self.actions else { + return } - } - - resultSize = CGSize(width: contentWidth + insets.left + insets.right, height: contentSize.height + actionsHeight + insets.top + insets.bottom) - - self.actionNodesSeparator.frame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)) - - var actionOffset: CGFloat = 0.0 - let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) - var separatorIndex = -1 - var nodeIndex = 0 - for actionNode in self.actionNodes { - if separatorIndex >= 0 { - let separatorNode = self.actionVerticalSeparators[separatorIndex] - switch effectiveActionLayout { - case .horizontal: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) - case .vertical: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) + guard let highlightedAction = self.highlightedAction else { + if let action = actions.first(where: { $0.type == .default }) { + self.highlightedAction = action.id + } else if let action = actions.first(where: { $0.type == .defaultDestructive }) { + self.highlightedAction = action.id + } else if case .verticalReversed = self.effectiveActionLayout, let action = actions.last { + self.highlightedAction = action.id + } else if let action = actions.first { + self.highlightedAction = action.id + } + self.state?.updated(transition: .easeInOut(duration: 0.2)) + return + } + + let sequence = previous ? actions.reversed() : actions + var selectNext = false + var newHighlightedAction: AnyHashable? + + for action in sequence { + let id = AnyHashable(action.id) + if selectNext { + newHighlightedAction = id + break + } else if id == highlightedAction { + selectNext = true } } - separatorIndex += 1 - - let currentActionWidth: CGFloat - switch effectiveActionLayout { - case .horizontal: - if nodeIndex == self.actionNodes.count - 1 { - currentActionWidth = resultSize.width - actionOffset - } else { - currentActionWidth = actionWidth - } - case .vertical: - currentActionWidth = resultSize.width + guard let newHighlightedAction else { + return } - - let actionNodeFrame: CGRect - switch effectiveActionLayout { - case .horizontal: - actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += currentActionWidth - case .vertical: - actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += actionButtonHeight - } - - transition.updateFrame(node: actionNode, frame: actionNodeFrame) - - nodeIndex += 1 + self.highlightedAction = newHighlightedAction + self.state?.updated(transition: .easeInOut(duration: 0.2)) } - return resultSize + func performHighlightedAction() { + guard let actions = self.actions else { + return + } + guard let highlightedAction = self.highlightedAction else { + return + } + guard let action = actions.first(where: { AnyHashable($0.id) == highlightedAction }) else { + return + } + action.action() + if action.autoDismiss { + self.requestDismiss() + } + } + + func update(component: AlertScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.isUpdating = true + defer { + self.isUpdating = false + } + + let environment = environment[ViewControllerComponentContainer.Environment.self].value + self.environment = environment + self.state = state + + if self.component == nil { + self.disposable = (combineLatest( + queue: Queue.mainQueue(), + component.content, + component.actions + ) |> deliverOnMainQueue).start(next: { [weak self] content, actions in + guard let self else { + return + } + self.content = content + self.actions = actions + + if !self.isUpdating { + self.state?.updated(transition: .easeInOut(duration: 0.25)) + } + }) + } + + self.component = component + + var alertHeight: CGFloat = 0.0 + let alertWidth: CGFloat = 300.0 + let contentTopInset: CGFloat = 22.0 + let contentBottomInset: CGFloat = 21.0 + let contentSideInset: CGFloat = 30.0 + let contentSpacing: CGFloat = 8.0 + let actionSideInset: CGFloat = 16.0 + let actionSpacing: CGFloat = 8.0 + let fullWidthActionSize = CGSize(width: alertWidth - actionSideInset * 2.0, height: AlertActionComponent.actionHeight) + let halfWidthActionSize = CGSize(width: (alertWidth - actionSideInset * 2.0 - actionSpacing) / 2.0, height: AlertActionComponent.actionHeight) + + let alertEnvironment = AlertComponentEnvironment(theme: environment.theme, strings: environment.strings) + + var contentOriginY: CGFloat = 0.0 + var validContentIds: Set = Set() + if let content = self.content { + for content in content { + if contentOriginY.isZero { + contentOriginY += contentTopInset + } else { + contentOriginY += contentSpacing + } + validContentIds.insert(content.id) + + let item: ComponentView + var itemTransition = transition + if let current = self.contentItems[content.id] { + item = current + } else { + item = ComponentView() + if !transition.animation.isImmediate { + itemTransition = .immediate + } + self.contentItems[content.id] = item + } + + let itemSize = item.update( + transition: itemTransition, + component: content.component, + environment: { alertEnvironment }, + containerSize: CGSize(width: alertWidth - contentSideInset * 2.0, height: availableSize.height) + ) + let itemFrame = CGRect(origin: CGPoint(x: contentSideInset, y: contentOriginY), size: itemSize) + if let itemView = item.view { + if itemView.superview == nil { + self.backgroundView.contentView.addSubview(itemView) + item.parentState = state + } + transition.setFrame(view: itemView, frame: itemFrame) + } + contentOriginY += itemSize.height + } + } + + if !contentOriginY.isZero { + alertHeight += contentOriginY + alertHeight += contentBottomInset + } + + if let actions = self.actions { + let genericActionTheme = AlertActionComponent.Theme( + background: environment.theme.actionSheet.primaryTextColor.withMultipliedAlpha(0.1), + foreground: environment.theme.actionSheet.primaryTextColor, + secondary: environment.theme.actionSheet.secondaryTextColor, + font: .regular + ) + let defaultActionTheme = AlertActionComponent.Theme( + background: environment.theme.actionSheet.controlAccentColor, + foreground: environment.theme.list.itemCheckColors.foregroundColor, + secondary: environment.theme.list.itemCheckColors.foregroundColor.withMultipliedAlpha(0.85), + font: .bold + ) + let destructiveActionTheme = AlertActionComponent.Theme( + background: environment.theme.list.itemDestructiveColor, + foreground: .white, + secondary: .white.withMultipliedAlpha(0.6), + font: .regular + ) + let defaultDestructiveActionTheme = AlertActionComponent.Theme( + background: environment.theme.list.itemDestructiveColor, + foreground: .white, + secondary: .white.withMultipliedAlpha(0.6), + font: .bold + ) + + var effectiveActionLayout: ActionLayout = .horizontal + if case .vertical = component.configuration.actionAlignment { + effectiveActionLayout = .vertical + } else if actions.count == 1 { + effectiveActionLayout = .vertical + } + var actionTransitions: [AnyHashable: ComponentTransition] = [:] + var validActionIds: Set = Set() + for action in actions { + validActionIds.insert(action.id) + + let item: ComponentView + var itemTransition = transition + if let current = self.actionItems[action.id] { + item = current + } else { + item = ComponentView() + if !transition.animation.isImmediate { + itemTransition = .immediate + } + self.actionItems[action.id] = item + } + actionTransitions[action.id] = itemTransition + + let actionTheme: AlertActionComponent.Theme + switch action.type { + case .generic: + actionTheme = genericActionTheme + case .default: + actionTheme = defaultActionTheme + case .destructive: + actionTheme = destructiveActionTheme + case .defaultDestructive: + actionTheme = defaultDestructiveActionTheme + } + let itemSize = item.update( + transition: itemTransition, + component: AnyComponent(AlertActionComponent( + theme: actionTheme, + title: action.title, + isHighlighted: AnyHashable(action.id) == self.highlightedAction, + isEnabled: action.isEnabled, + progress: action.progress + )), + environment: { alertEnvironment }, + containerSize: fullWidthActionSize + ) + if let itemView = item.view { + if itemView.superview == nil { + self.backgroundView.contentView.addSubview(itemView) + } + } + + if case .horizontal = effectiveActionLayout, itemSize.width > halfWidthActionSize.width { + effectiveActionLayout = .verticalReversed + } + } + self.effectiveActionLayout = effectiveActionLayout + + if !actions.isEmpty { + let actionsHeight: CGFloat + if self.effectiveActionLayout.isVertical { + actionsHeight = fullWidthActionSize.height * CGFloat(actions.count) + actionSpacing * CGFloat(actions.count - 1) + } else { + actionsHeight = fullWidthActionSize.height + } + alertHeight += actionsHeight + alertHeight += actionSideInset + } + + var actionOriginX: CGFloat = actionSideInset + var actionOriginY: CGFloat + switch self.effectiveActionLayout { + case .horizontal, .verticalReversed: + actionOriginY = alertHeight - actionSideInset - fullWidthActionSize.height + case .vertical: + actionOriginY = alertHeight - actionSideInset - fullWidthActionSize.height * CGFloat( actions.count) - actionSpacing * CGFloat(actions.count - 1) + } + for action in actions { + guard let item = self.actionItems[action.id], let itemView = item.view as? AlertActionComponent.View else { + continue + } + let itemTransition = actionTransitions[action.id] ?? transition + let itemFrame: CGRect + switch self.effectiveActionLayout { + case .horizontal: + itemFrame = CGRect(origin: CGPoint(x: actionOriginX, y: actionOriginY), size: halfWidthActionSize) + actionOriginX += halfWidthActionSize.width + actionSpacing + case .vertical: + itemFrame = CGRect(origin: CGPoint(x: actionOriginX, y: actionOriginY), size: fullWidthActionSize) + actionOriginY += fullWidthActionSize.height + actionSpacing + case .verticalReversed: + itemFrame = CGRect(origin: CGPoint(x: actionOriginX, y: actionOriginY), size: fullWidthActionSize) + actionOriginY -= fullWidthActionSize.height + actionSpacing + } + itemView.applySize(size: itemFrame.size, transition: itemTransition) + itemTransition.setFrame(view: itemView, frame: itemFrame) + } + + var removeActionIds: [AnyHashable] = [] + for (id, item) in self.actionItems { + if !validActionIds.contains(id) { + removeActionIds.append(id) + if let itemView = item.view { + if !transition.animation.isImmediate { + itemView.layer.animateScale(from: 1.0, to: 0.01, duration: 0.25, removeOnCompletion: false) + itemView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in + itemView.removeFromSuperview() + }) + } else { + itemView.removeFromSuperview() + } + } + } + } + for id in removeActionIds { + self.actionItems.removeValue(forKey: id) + } + } + + let alertSize = CGSize(width: alertWidth, height: alertHeight) + let bounds = CGRect(origin: .zero, size: availableSize) + + transition.setFrame(view: self.dimView, frame: bounds) + transition.setFrame(view: self.containerView, frame: bounds) + self.containerView.update(size: availableSize, isDark: environment.theme.overallDarkAppearance, transition: transition) + + var availableHeight = availableSize.height + if component.configuration.allowInputInset, environment.inputHeight > 0.0 { + availableHeight -= environment.inputHeight + } + + transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - alertSize.width) / 2.0), y: floorToScreenPixels((availableHeight - alertSize.height) / 2.0)), size: alertSize)) + self.backgroundView.update(size: alertSize, shape: .roundedRect(cornerRadius: 35.0), isDark: environment.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: .white), isInteractive: true, transition: transition) + + return availableSize + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) } } -public func componentAlertController(theme: AlertControllerTheme, content: AnyComponent, actions: [ComponentAlertAction], actionLayout: ComponentAlertContentActionLayout = .horizontal, dismissOnOutsideTap: Bool = true) -> AlertController { - var dismissImpl: (() -> Void)? - let controller = AlertController(theme: theme, contentNode: ComponentAlertContentNode(theme: theme, content: content, actions: actions.map { action in - return ComponentAlertAction(type: action.type, title: action.title, action: { - dismissImpl?() - action.action() - }) - }, actionLayout: actionLayout, dismissOnOutsideTap: dismissOnOutsideTap)) - dismissImpl = { [weak controller] in - controller?.dismissAnimated() +open class AlertScreen: ViewControllerComponentContainer, KeyShortcutResponder { + public enum ActionAligmnent: Equatable { + case `default` + case vertical + } + + public struct Configuration: Equatable { + let actionAlignment: ActionAligmnent + let dismissOnOutsideTap: Bool + let allowInputInset: Bool + + public init( + actionAlignment: ActionAligmnent = .default, + dismissOnOutsideTap: Bool = true, + allowInputInset: Bool = false + ) { + self.actionAlignment = actionAlignment + self.dismissOnOutsideTap = dismissOnOutsideTap + self.allowInputInset = allowInputInset + } + } + + public struct Action: Equatable { + public enum ActionType: Equatable { + case generic + case `default` + case destructive + case defaultDestructive + } + public let title: String + public let type: ActionType + public let action: () -> Void + public let autoDismiss: Bool + public let isEnabled: Signal + public let progress: Signal + + public init( + id: AnyHashable? = nil, + title: String, + type: ActionType = .generic, + action: @escaping () -> Void = {}, + autoDismiss: Bool = true, + isEnabled: Signal = .single(true), + progress: Signal = .single(false) + ) { + self.type = type + self.title = title + self.action = action + self.autoDismiss = autoDismiss + self.isEnabled = isEnabled + self.progress = progress + + if let id { + self.id = id + } else { + self.id = title + } + } + + public static func ==(lhs: Action, rhs: Action) -> Bool { + if lhs.title != rhs.title { + return false + } + if lhs.type != rhs.type { + return false + } + if lhs.autoDismiss != rhs.autoDismiss { + return false + } + return true + } + + fileprivate let id: AnyHashable + } + + private var processedDidAppear: Bool = false + private var processedDidDisappear: Bool = false + + private let readyValue = Promise(true) + override public var ready: Promise { + return self.readyValue + } + + public var dismissed: ((Bool) -> Void)? + + public init( + configuration: Configuration = Configuration(), + contentSignal: Signal<[AnyComponentWithIdentity], NoError>, + actionsSignal: Signal<[Action], NoError>, + updatedPresentationData: (initial: PresentationData, signal: Signal) + ) { + let componentReady = Promise() + + super.init( + component: AlertScreenComponent( + configuration: configuration, + content: contentSignal, + actions: actionsSignal, + ready: componentReady + ), + navigationBarAppearance: .none, + statusBarStyle: .ignore, + presentationMode: .default, + updatedPresentationData: updatedPresentationData + ) + self.navigationPresentation = .flatModal + + //self.readyValue.set(componentReady.get() |> timeout(1.0, queue: .mainQueue(), alternate: .single(true))) + } + + public convenience init( + configuration: Configuration = Configuration(), + content: [AnyComponentWithIdentity], + actions: [Action], + updatedPresentationData: (initial: PresentationData, signal: Signal) + ) { + self.init( + configuration: configuration, + contentSignal: .single(content), + actionsSignal: .single(actions), + updatedPresentationData: updatedPresentationData + ) + } + + public convenience init( + context: AccountContext, + configuration: Configuration = Configuration(), + content: [AnyComponentWithIdentity], + actions: [Action] + ) { + self.init( + sharedContext: context.sharedContext, + configuration: configuration, + content: content, + actions: actions, + ) + } + + public convenience init( + sharedContext: SharedAccountContext, + configuration: Configuration = Configuration(), + content: [AnyComponentWithIdentity], + actions: [Action] + ) { + let presentationData = sharedContext.currentPresentationData.with { $0 } + let updatedPresentationDataSignal = sharedContext.presentationData + self.init( + configuration: configuration, + content: content, + actions: actions, + updatedPresentationData: (initial: presentationData, signal: updatedPresentationDataSignal) + ) + } + + public convenience init( + configuration: Configuration = Configuration(), + title: String? = nil, + text: String, + textAction: @escaping ([NSAttributedString.Key: Any]) -> Void = { _ in }, + actions: [Action], + updatedPresentationData: (initial: PresentationData, signal: Signal) + ) { + var content: [AnyComponentWithIdentity] = [] + if let title { + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: title) + ) + )) + } + if !text.isEmpty { + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(text), action: textAction) + ) + )) + } + + self.init( + configuration: configuration, + content: content, + actions: actions, + updatedPresentationData: updatedPresentationData + ) + } + + public convenience init( + context: AccountContext, + configuration: Configuration = Configuration(), + title: String? = nil, + text: String, + actions: [Action] + ) { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let updatedPresentationDataSignal = context.sharedContext.presentationData + self.init( + configuration: configuration, + title: title, + text: text, + actions: actions, + updatedPresentationData: (initial: presentationData, signal: updatedPresentationDataSignal) + ) + } + + required public init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + } + + override public func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + if !self.processedDidAppear { + self.processedDidAppear = true + if let componentView = self.node.hostView.componentView as? AlertScreenComponent.View { + componentView.animateIn() + } + } + } + + private func superDismiss() { + super.dismiss() + } + + override open func dismiss(completion: (() -> Void)? = nil) { + if !self.processedDidDisappear { + self.processedDidDisappear = true + + if let componentView = self.node.hostView.componentView as? AlertScreenComponent.View { + let dismissedByTapOutside = componentView.dismissedByTapOutside + componentView.animateOut(completion: { [weak self] in + if let self { + self.dismissed?(dismissedByTapOutside) + self.superDismiss() + } + completion?() + }) + } else { + super.dismiss(completion: completion) + } + } + } + + public var keyShortcuts: [KeyShortcut] { + return [ + KeyShortcut( + input: UIKeyCommand.inputEscape, + modifiers: [], + action: { [weak self] in + if let componentView = self?.node.hostView.componentView as? AlertScreenComponent.View { + componentView.handleKeyCommand(.escape) + } + } + ), + KeyShortcut( + input: "W", + modifiers: [.command], + action: { [weak self] in + if let componentView = self?.node.hostView.componentView as? AlertScreenComponent.View { + componentView.handleKeyCommand(.escape) + } + } + ), + KeyShortcut( + input: "\r", + modifiers: [], + action: { [weak self] in + if let componentView = self?.node.hostView.componentView as? AlertScreenComponent.View { + componentView.handleKeyCommand(.enter) + } + } + ), + KeyShortcut( + input: UIKeyCommand.inputUpArrow, + modifiers: [], + action: { [weak self] in + if let componentView = self?.node.hostView.componentView as? AlertScreenComponent.View { + componentView.handleKeyCommand(.up) + } + } + ), + KeyShortcut( + input: UIKeyCommand.inputDownArrow, + modifiers: [], + action: { [weak self] in + if let componentView = self?.node.hostView.componentView as? AlertScreenComponent.View { + componentView.handleKeyCommand(.down) + } + } + ), + KeyShortcut( + input: UIKeyCommand.inputLeftArrow, + modifiers: [], + action: { [weak self] in + if let componentView = self?.node.hostView.componentView as? AlertScreenComponent.View { + componentView.handleKeyCommand(.left) + } + } + ), + KeyShortcut( + input: UIKeyCommand.inputRightArrow, + modifiers: [], + action: { [weak self] in + if let componentView = self?.node.hostView.componentView as? AlertScreenComponent.View { + componentView.handleKeyCommand(.right) + } + } + ) + ] + } +} + +public final class ActionSelectionGestureRecognizer: UIGestureRecognizer { + private var initialLocation: CGPoint? + private var currentLocation: CGPoint? + + public override init(target: Any?, action: Selector?) { + super.init(target: target, action: action) + + self.delaysTouchesBegan = false + self.delaysTouchesEnded = false + } + + public override func reset() { + super.reset() + + self.initialLocation = nil + } + + public override func touchesBegan(_ touches: Set, with event: UIEvent) { + super.touchesBegan(touches, with: event) + + if self.initialLocation == nil { + self.initialLocation = touches.first?.location(in: self.view) + } + self.currentLocation = self.initialLocation + + self.state = .began + } + + public override func touchesEnded(_ touches: Set, with event: UIEvent) { + super.touchesEnded(touches, with: event) + + self.state = .ended + } + + public override func touchesCancelled(_ touches: Set, with event: UIEvent) { + super.touchesCancelled(touches, with: event) + + self.state = .cancelled + } + + public override func touchesMoved(_ touches: Set, with event: UIEvent) { + super.touchesMoved(touches, with: event) + + self.currentLocation = touches.first?.location(in: self.view) + + self.state = .changed + } + + public func translation(in: UIView?) -> CGPoint { + if let initialLocation = self.initialLocation, let currentLocation = self.currentLocation { + return CGPoint(x: currentLocation.x - initialLocation.x, y: currentLocation.y - initialLocation.y) + } + return CGPoint() } - return controller } diff --git a/submodules/TelegramUI/Components/AlertComponent/Sources/AlertContent.swift b/submodules/TelegramUI/Components/AlertComponent/Sources/AlertContent.swift new file mode 100644 index 00000000..8e68f3a1 --- /dev/null +++ b/submodules/TelegramUI/Components/AlertComponent/Sources/AlertContent.swift @@ -0,0 +1,366 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import ComponentFlow +import TelegramCore +import TelegramPresentationData +import MultilineTextComponent +import MultilineTextWithEntitiesComponent +import Markdown +import TextFormat +import AccountContext + +private let titleFont = Font.bold(17.0) +private let defaultTextFont = Font.regular(15.0) +private let defaultBoldTextFont = Font.semibold(15.0) +private let defaultItalicTextFont = Font.italic(15.0) +private let defaultBoldItalicTextFont = Font.with(size: 15.0, weight: .semibold, traits: [.italic]) +private let defaultFixedTextFont = Font.monospace(15.0) +private let smallTextFont = Font.regular(14.0) +private let smallBoldTextFont = Font.semibold(14.0) +private let smallItalicTextFont = Font.italic(14.0) +private let smallBoldItalicTextFont = Font.with(size: 14.0, weight: .semibold, traits: [.italic]) +private let smallFixedTextFont = Font.monospace(14.0) +private let backgroundInset: CGFloat = 8.0 + +public final class AlertTitleComponent: Component { + public typealias EnvironmentType = AlertComponentEnvironment + + public enum Alignment { + case `default` + case center + } + + let title: String + let alignment: Alignment + + public init( + title: String, + alignment: Alignment = .default + ) { + self.title = title + self.alignment = alignment + } + + public static func ==(lhs: AlertTitleComponent, rhs: AlertTitleComponent) -> Bool { + if lhs.title != rhs.title { + return false + } + if lhs.alignment != rhs.alignment { + return false + } + return true + } + + public final class View: UIView { + private let title = ComponentView() + + private var component: AlertTitleComponent? + private weak var state: EmptyComponentState? + + func update(component: AlertTitleComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + self.state = state + + let environment = environment[AlertComponentEnvironment.self] + + let titleSize = self.title.update( + transition: transition, + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString( + string: component.title, + font: titleFont, + textColor: environment.theme.actionSheet.primaryTextColor + )), + horizontalAlignment: component.alignment == .center ? .center : .natural, + maximumNumberOfLines: 0 + )), + environment: {}, + containerSize: availableSize + ) + + let titleOriginX: CGFloat + switch component.alignment { + case .default: + titleOriginX = 0.0 + case .center: + titleOriginX = floorToScreenPixels((availableSize.width - titleSize.width) / 2.0) + } + let titleFrame = CGRect(origin: CGPoint(x: titleOriginX, y: 0.0), size: titleSize) + if let titleView = self.title.view { + if titleView.superview == nil { + self.addSubview(titleView) + } + transition.setFrame(view: titleView, frame: titleFrame) + } + return CGSize(width: availableSize.width, height: titleSize.height) + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + +public final class AlertTextComponent: Component { + public typealias EnvironmentType = AlertComponentEnvironment + + public enum Content: Equatable { + case plain(String) + case attributed(NSAttributedString) + case textWithEntities(AccountContext, String, [MessageTextEntity]) + + public static func ==(lhs: Content, rhs: Content) -> Bool { + switch lhs { + case let .plain(text): + if case .plain(text) = rhs { + return true + } else { + return false + } + case let .attributed(text): + if case .attributed(text) = rhs { + return true + } else { + return false + } + case let .textWithEntities(_, lhsText, lhsEntities): + if case let .textWithEntities(_, rhsText, rhsEntities) = rhs { + return lhsText == rhsText && lhsEntities == rhsEntities + } else { + return false + } + } + } + } + + public enum Alignment: Equatable { + case `default` + case center + } + + public enum Color: Equatable { + case primary + case secondary + case destructive + } + + public enum TextStyle: Equatable { + case `default` + case small + case bold + } + + public enum Style: Equatable { + case plain(TextStyle) + case background(TextStyle) + } + + let content: Content + let alignment: Alignment + let color: Color + let style: Style + let insets: UIEdgeInsets + let action: ([NSAttributedString.Key: Any]) -> Void + + public init( + content: Content, + alignment: Alignment = .default, + color: Color = .primary, + style: Style = .plain(.default), + insets: UIEdgeInsets = .zero, + action: @escaping ([NSAttributedString.Key: Any]) -> Void = { _ in } + ) { + self.content = content + self.alignment = alignment + self.color = color + self.style = style + self.insets = insets + self.action = action + } + + public static func ==(lhs: AlertTextComponent, rhs: AlertTextComponent) -> Bool { + if lhs.content != rhs.content { + return false + } + if lhs.alignment != rhs.alignment { + return false + } + if lhs.color != rhs.color { + return false + } + if lhs.style != rhs.style { + return false + } + if lhs.insets != rhs.insets { + return false + } + return true + } + + public final class View: UIView { + private let background = ComponentView() + private let text = ComponentView() + + private var component: AlertTextComponent? + private weak var state: EmptyComponentState? + + func update(component: AlertTextComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + self.state = state + + let environment = environment[AlertComponentEnvironment.self] + + let textColor: UIColor + switch component.color { + case .primary: + textColor = environment.theme.actionSheet.primaryTextColor + case .secondary: + textColor = environment.theme.actionSheet.primaryTextColor.withMultipliedAlpha(0.35) + case .destructive: + textColor = environment.theme.actionSheet.destructiveActionTextColor + } + let linkColor = environment.theme.actionSheet.controlAccentColor + + let textFont: UIFont + let boldTextFont: UIFont + let italicTextFont: UIFont + let fixedTextFont: UIFont + switch component.style { + case let .plain(textStyle), let .background(textStyle): + switch textStyle { + case .default: + textFont = defaultTextFont + boldTextFont = defaultBoldTextFont + italicTextFont = defaultItalicTextFont + fixedTextFont = defaultFixedTextFont + case .small: + textFont = smallTextFont + boldTextFont = smallBoldTextFont + italicTextFont = smallItalicTextFont + fixedTextFont = smallFixedTextFont + case .bold: + textFont = defaultBoldTextFont + boldTextFont = defaultBoldTextFont + italicTextFont = defaultBoldItalicTextFont + fixedTextFont = defaultFixedTextFont + } + } + + var finalText: NSAttributedString + var context: AccountContext? + switch component.content { + case let .plain(text): + let markdownAttributes = MarkdownAttributes( + body: MarkdownAttributeSet(font: textFont, textColor: textColor), + bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), + link: MarkdownAttributeSet(font: textFont, textColor: linkColor), + linkAttribute: { contents in + return (TelegramTextAttributes.URL, contents) + } + ) + finalText = parseMarkdownIntoAttributedString(text, attributes: markdownAttributes) + case let .attributed(attributedText): + finalText = attributedText + case let .textWithEntities(accountContext, text, entities): + context = accountContext + finalText = stringWithAppliedEntities(text, entities: entities, baseColor: textColor, linkColor: linkColor, baseFont: textFont, linkFont: textFont, boldFont: boldTextFont, italicFont: italicTextFont, boldItalicFont: italicTextFont, fixedFont: fixedTextFont, blockQuoteFont: textFont, message: nil) + } + + var hasCenterAlignment = component.alignment == .center + switch component.style { + case .background: + hasCenterAlignment = true + default: + break + } + + let textConstrainedSize = CGSize(width: availableSize.width, height: availableSize.height) + + let textSize = self.text.update( + transition: transition, + component: AnyComponent( + MultilineTextWithEntitiesComponent( + context: context, + animationCache: context?.animationCache, + animationRenderer: context?.animationRenderer, + placeholderColor: textColor.withMultipliedAlpha(0.1), + text: .plain(finalText), + horizontalAlignment: hasCenterAlignment ? .center : .natural, + maximumNumberOfLines: 0, + lineSpacing: 0.2, + spoilerColor: textColor, + highlightColor: linkColor.withAlphaComponent(0.2), + manualVisibilityControl: true, + resetAnimationsOnVisibilityChange: true, + highlightAction: { attributes in + if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] { + return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL) + } else { + return nil + } + }, + tapAction: { attributes, _ in + component.action(attributes) + } + ) + ), + environment: {}, + containerSize: textConstrainedSize + ) + + var textOffset = CGPoint() + if hasCenterAlignment { + textOffset.x = floorToScreenPixels((availableSize.width - textSize.width) / 2.0) + } + var size = CGSize(width: availableSize.width, height: textSize.height) + if case .background = component.style { + let backgroundSize = CGSize(width: availableSize.width + 20.0, height: textSize.height + backgroundInset * 2.0) + size = backgroundSize + textOffset = CGPoint(x: textOffset.x, y: backgroundInset) + + let _ = self.background.update( + transition: transition, + component: AnyComponent( + FilledRoundedRectangleComponent( + color: textColor.withMultipliedAlpha(0.1), + cornerRadius: .value(10.0), + smoothCorners: true + ) + ), + environment: {}, + containerSize: backgroundSize + ) + let backgroundFrame = CGRect(origin: CGPoint(x: -10.0, y: component.insets.top), size: backgroundSize) + if let backgroundView = self.background.view { + if backgroundView.superview == nil { + self.addSubview(backgroundView) + } + transition.setFrame(view: backgroundView, frame: backgroundFrame) + } + } + + let textFrame = CGRect(origin: textOffset.offsetBy(dx: 0.0, dy: component.insets.top), size: textSize) + if let textView = self.text.view { + if textView.superview == nil { + self.addSubview(textView) + } + transition.setFrame(view: textView, frame: textFrame) + } + return CGSize(width: size.width, height: size.height + component.insets.top + component.insets.bottom) + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/AnimatedTextComponent/BUILD b/submodules/TelegramUI/Components/AnimatedTextComponent/BUILD index f6ee40df..782ad3be 100644 --- a/submodules/TelegramUI/Components/AnimatedTextComponent/BUILD +++ b/submodules/TelegramUI/Components/AnimatedTextComponent/BUILD @@ -15,6 +15,7 @@ swift_library( "//submodules/ComponentFlow", "//submodules/TelegramPresentationData", "//submodules/Components/BundleIconComponent", + "//submodules/Components/MultilineTextComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/AnimatedTextComponent/Sources/AnimatedTextComponent.swift b/submodules/TelegramUI/Components/AnimatedTextComponent/Sources/AnimatedTextComponent.swift index cc44a9c2..0d6438d4 100644 --- a/submodules/TelegramUI/Components/AnimatedTextComponent/Sources/AnimatedTextComponent.swift +++ b/submodules/TelegramUI/Components/AnimatedTextComponent/Sources/AnimatedTextComponent.swift @@ -4,6 +4,7 @@ import Display import ComponentFlow import TelegramPresentationData import BundleIconComponent +import MultilineTextComponent extension ComponentTransition { func animateBlur(layer: CALayer, from: CGFloat, to: CGFloat, delay: Double = 0.0, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) { @@ -46,6 +47,7 @@ public final class AnimatedTextComponent: Component { public let items: [Item] public let noDelay: Bool public let animateScale: Bool + public let animateSlide: Bool public let preferredDirectionIsDown: Bool public let blur: Bool @@ -55,6 +57,7 @@ public final class AnimatedTextComponent: Component { items: [Item], noDelay: Bool = false, animateScale: Bool = true, + animateSlide: Bool = true, preferredDirectionIsDown: Bool = false, blur: Bool = false ) { @@ -63,6 +66,7 @@ public final class AnimatedTextComponent: Component { self.items = items self.noDelay = noDelay self.animateScale = animateScale + self.animateSlide = animateSlide self.preferredDirectionIsDown = preferredDirectionIsDown self.blur = blur } @@ -83,6 +87,9 @@ public final class AnimatedTextComponent: Component { if lhs.animateScale != rhs.animateScale { return false } + if lhs.animateSlide != rhs.animateSlide { + return false + } if lhs.preferredDirectionIsDown != rhs.preferredDirectionIsDown { return false } @@ -101,6 +108,8 @@ public final class AnimatedTextComponent: Component { public final class View: UIView { private var characters: [CharacterKey: ComponentView] = [:] + private var spaceSize: CGSize? + private var component: AnimatedTextComponent? private weak var state: EmptyComponentState? @@ -113,6 +122,15 @@ public final class AnimatedTextComponent: Component { } func update(component: AnimatedTextComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + let spaceSize: CGSize + if let current = self.spaceSize, self.component?.font == component.font { + spaceSize = current + } else { + let spaceSizeValue = NSAttributedString(string: " ", font: component.font, textColor: .black).boundingRect(with: CGSize(width: 100.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil).size + spaceSize = CGSize(width: ceil(spaceSizeValue.width), height: ceil(spaceSizeValue.height)) + self.spaceSize = spaceSize + } + self.component = component self.state = state @@ -220,7 +238,7 @@ public final class AnimatedTextComponent: Component { itemText = [.icon(iconName, tint, offset)] } var index = 0 - for character in itemText { + characterLoop: for character in itemText { let characterKey = CharacterKey(itemId: item.id, index: index, value: character.value) index += 1 @@ -236,13 +254,23 @@ public final class AnimatedTextComponent: Component { let characterComponent: AnyComponent var characterOffset: CGPoint = .zero + var addTrailingSpace = false switch character { case let .text(text): - characterComponent = AnyComponent(Text( - text: String(text), - font: component.font, - color: component.color - )) + if text == " " { + size.height = max(size.height, ceil(spaceSize.height)) + size.width += max(0.0, ceil(spaceSize.width)) + + continue characterLoop + } else { + if text.hasSuffix(" ") { + addTrailingSpace = true + } + + characterComponent = AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: text, font: component.font, textColor: component.color)) + )) + } case let .icon(iconName, tint, offset): characterComponent = AnyComponent(BundleIconComponent( name: iconName, @@ -263,6 +291,7 @@ public final class AnimatedTextComponent: Component { if characterComponentView.superview == nil { characterComponentView.layer.rasterizationScale = UIScreenScale self.addSubview(characterComponentView) + characterComponentView.layer.anchorPoint = CGPoint() animateIn = true } @@ -282,8 +311,9 @@ public final class AnimatedTextComponent: Component { } characterComponentView.bounds = CGRect(origin: CGPoint(), size: characterFrame.size) - let deltaPosition = CGPoint(x: characterFrame.midX - characterComponentView.frame.midX, y: characterFrame.midY - characterComponentView.frame.midY) - characterComponentView.center = characterFrame.center + + let deltaPosition = CGPoint(x: characterFrame.minX - characterComponentView.frame.minX, y: characterFrame.minY - characterComponentView.frame.minY) + characterComponentView.center = characterFrame.origin characterComponentView.layer.animatePosition(from: CGPoint(x: -deltaPosition.x, y: -deltaPosition.y), to: CGPoint(), duration: 0.4, delay: delayNorm * delayWidth, timingFunction: kCAMediaTimingFunctionSpring, additive: true) } } @@ -305,13 +335,20 @@ public final class AnimatedTextComponent: Component { if component.blur { ComponentTransition.easeInOut(duration: 0.2).animateBlur(layer: characterComponentView.layer, from: transitionBlurRadius, to: 0.0, delay: delayNorm * delayWidth) } - characterComponentView.layer.animatePosition(from: CGPoint(x: 0.0, y: characterSize.height * offsetNorm), to: CGPoint(), duration: 0.4, delay: delayNorm * delayWidth, timingFunction: kCAMediaTimingFunctionSpring, additive: true) + if component.animateSlide { + characterComponentView.layer.animatePosition(from: CGPoint(x: 0.0, y: characterSize.height * offsetNorm), to: CGPoint(), duration: 0.4, delay: delayNorm * delayWidth, timingFunction: kCAMediaTimingFunctionSpring, additive: true) + } characterComponentView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18, delay: delayNorm * delayWidth) } } size.height = max(size.height, characterSize.height) - size.width += max(0.0, characterSize.width - UIScreenPixel * 2.0) + size.width += max(0.0, characterSize.width - UIScreenPixel) + + if addTrailingSpace { + size.height = max(size.height, ceil(spaceSize.height)) + size.width += max(0.0, ceil(spaceSize.width)) + } } } @@ -342,7 +379,9 @@ public final class AnimatedTextComponent: Component { } else { targetY = characterComponentView.center.y - characterComponentView.bounds.height * offsetNorm } - outScaleTransition.setPosition(view: characterComponentView, position: CGPoint(x: characterComponentView.center.x, y: targetY), delay: delayNorm * delayWidth) + if component.animateSlide { + outScaleTransition.setPosition(view: characterComponentView, position: CGPoint(x: characterComponentView.center.x, y: targetY), delay: delayNorm * delayWidth) + } outAlphaTransition.setAlpha(view: characterComponentView, alpha: 0.0, delay: delayNorm * delayWidth, completion: { [weak characterComponentView] _ in characterComponentView?.removeFromSuperview() }) diff --git a/submodules/TelegramUI/Components/AsyncListComponent/Sources/AsyncListComponent.swift b/submodules/TelegramUI/Components/AsyncListComponent/Sources/AsyncListComponent.swift index 4b7fac1b..5d798af8 100644 --- a/submodules/TelegramUI/Components/AsyncListComponent/Sources/AsyncListComponent.swift +++ b/submodules/TelegramUI/Components/AsyncListComponent/Sources/AsyncListComponent.swift @@ -363,7 +363,7 @@ public final class AsyncListComponent: Component { init() { self.contentContainer = UIView() - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.view.addSubview(self.contentContainer) diff --git a/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileController.swift b/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileController.swift index 59b5b88a..3a3a979c 100644 --- a/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileController.swift +++ b/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileController.swift @@ -212,7 +212,7 @@ private func attachmentFileControllerEntries(presentationData: PresentationData, if case .audio = mode { if let savedMusic, savedMusic.count > 0 { - entries.append(.savedHeader(presentationData.theme, "SAVED MUSIC".uppercased())) + entries.append(.savedHeader(presentationData.theme, presentationData.strings.MediaEditor_Audio_SavedMusic.uppercased())) var savedMusic = savedMusic var showMore = false if savedMusic.count > 4 && !state.savedMusicExpanded { @@ -225,7 +225,7 @@ private func attachmentFileControllerEntries(presentationData: PresentationData, i += 1 } if showMore { - entries.append(.showMore(presentationData.theme, "Show More")) + entries.append(.showMore(presentationData.theme, presentationData.strings.MediaEditor_Audio_ShowMore)) } } } @@ -459,16 +459,17 @@ public func makeAttachmentFileControllerImpl(context: AccountContext, updatedPre let updatedTheme = presentationData.theme.withModalBlocksBackground() presentationData = presentationData.withUpdated(theme: updatedTheme) - let barButtonSize = CGSize(width: 40.0, height: 40.0) + let barButtonSize = CGSize(width: 44.0, height: 44.0) let closeButton = GlassBarButtonComponent( size: barButtonSize, - backgroundColor: presentationData.theme.rootController.navigationBar.glassBarButtonBackgroundColor, + backgroundColor: nil, isDark: presentationData.theme.overallDarkAppearance, state: .generic, + animateScale: false, component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: presentationData.theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: presentationData.theme.chat.inputPanel.panelControlColor ) )), action: { _ in @@ -489,13 +490,14 @@ public func makeAttachmentFileControllerImpl(context: AccountContext, updatedPre let searchButton = GlassBarButtonComponent( size: barButtonSize, - backgroundColor: presentationData.theme.rootController.navigationBar.glassBarButtonBackgroundColor, + backgroundColor: nil, isDark: presentationData.theme.overallDarkAppearance, state: .generic, + animateScale: false, component: AnyComponentWithIdentity(id: "search", component: AnyComponent( BundleIconComponent( name: "Navigation/Search", - tintColor: presentationData.theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: presentationData.theme.chat.inputPanel.panelControlColor ) )), action: { _ in @@ -509,7 +511,7 @@ public func makeAttachmentFileControllerImpl(context: AccountContext, updatedPre } ) let searchButtonComponent = state.searching ? nil : AnyComponentWithIdentity(id: "search", component: AnyComponent(searchButton)) - let searchButtonNode = existingSearchButton.modify { current in + let searchButtonNode: BarComponentHostNode? = !state.searching ? existingSearchButton.modify { current in let buttonNode: BarComponentHostNode if let current { buttonNode = current @@ -518,7 +520,7 @@ public func makeAttachmentFileControllerImpl(context: AccountContext, updatedPre buttonNode = BarComponentHostNode(component: searchButtonComponent, size: barButtonSize) } return buttonNode - } + } : nil let previousRecentDocuments = previousRecentDocuments.swap(recentDocuments) let crossfade = previousRecentDocuments == nil && recentDocuments != nil @@ -542,7 +544,7 @@ public func makeAttachmentFileControllerImpl(context: AccountContext, updatedPre case .recent: title = presentationData.strings.Attachment_File case .audio: - title = "Audio" + title = presentationData.strings.MediaEditor_Audio_Title } let controllerState = ItemListControllerState( diff --git a/submodules/TelegramUI/Components/AvatarComponent/BUILD b/submodules/TelegramUI/Components/AvatarComponent/BUILD new file mode 100644 index 00000000..f52981e8 --- /dev/null +++ b/submodules/TelegramUI/Components/AvatarComponent/BUILD @@ -0,0 +1,24 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "AvatarComponent", + module_name = "AvatarComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit:AsyncDisplayKit", + "//submodules/Display:Display", + "//submodules/ComponentFlow", + "//submodules/TelegramCore", + "//submodules/TelegramPresentationData", + "//submodules/AvatarNode", + "//submodules/AccountContext", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/AvatarComponent/Sources/AvatarComponent.swift b/submodules/TelegramUI/Components/AvatarComponent/Sources/AvatarComponent.swift new file mode 100644 index 00000000..a9d18e4e --- /dev/null +++ b/submodules/TelegramUI/Components/AvatarComponent/Sources/AvatarComponent.swift @@ -0,0 +1,116 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import TelegramCore +import TelegramPresentationData +import AvatarNode +import AccountContext + +public final class AvatarComponent: Component { + let context: AccountContext + let theme: PresentationTheme + let peer: EnginePeer + let icon: AnyComponent? + + public init( + context: AccountContext, + theme: PresentationTheme, + peer: EnginePeer, + icon: AnyComponent? = nil + ) { + self.context = context + self.theme = theme + self.peer = peer + self.icon = icon + } + + public static func ==(lhs: AvatarComponent, rhs: AvatarComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.theme !== rhs.theme { + return false + } + if lhs.peer != rhs.peer { + return false + } + if lhs.icon != rhs.icon { + return false + } + return true + } + + public final class View: UIView { + private let avatarNode: AvatarNode + private var icon: ComponentView? + + private var component: AvatarComponent? + private weak var state: EmptyComponentState? + + override init(frame: CGRect) { + self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 42.0)) + + super.init(frame: frame) + + self.addSubnode(self.avatarNode) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(component: AvatarComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + if self.component == nil { + self.avatarNode.font = avatarPlaceholderFont(size: ceil(42.0 * availableSize.width / 100.0)) + } + + self.component = component + self.state = state + + var cutoutRect: CGRect? + if let icon = component.icon { + let iconView: ComponentView + if let current = self.icon { + iconView = current + } else { + iconView = ComponentView() + self.icon = iconView + } + let iconSize = iconView.update( + transition: .immediate, + component: icon, + environment: {}, + containerSize: availableSize + ) + let iconFrame = CGRect(origin: CGPoint(x: availableSize.width - iconSize.width + 2.0, y: availableSize.height - iconSize.height + 2.0), size: iconSize) + if let iconView = iconView.view { + if iconView.superview == nil { + self.addSubview(iconView) + } + iconView.frame = iconFrame + } + cutoutRect = CGRect(origin: CGPoint(x: iconFrame.minX, y: availableSize.height - iconFrame.maxY), size: iconFrame.size).insetBy(dx: -2.0 + UIScreenPixel, dy: -2.0 + UIScreenPixel) + } + + self.avatarNode.frame = CGRect(origin: .zero, size: availableSize) + self.avatarNode.setPeer( + context: component.context, + theme: component.theme, + peer: component.peer, + synchronousLoad: true, + cutoutRect: cutoutRect + ) + + return availableSize + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/AvatarEditorScreen/BUILD b/submodules/TelegramUI/Components/AvatarEditorScreen/BUILD index a8753199..c8f7959a 100644 --- a/submodules/TelegramUI/Components/AvatarEditorScreen/BUILD +++ b/submodules/TelegramUI/Components/AvatarEditorScreen/BUILD @@ -41,7 +41,7 @@ swift_library( "//submodules/TelegramUI/Components/MediaEditor", "//submodules/TelegramUI/Components/AvatarBackground", "//submodules/TelegramUI/Components/LottieComponent", - "//submodules/TelegramUI/Components/Premium/PremiumStarComponent", + "//submodules/TelegramUI/Components/PremiumAlertController", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift b/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift index 67727795..a43f3615 100644 --- a/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift +++ b/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift @@ -27,6 +27,7 @@ import MediaEditor import AvatarBackground import LottieComponent import UndoUI +import PremiumAlertController public struct AvatarKeyboardInputData: Equatable { var emoji: EmojiPagerContentComponent diff --git a/submodules/TelegramUI/Components/BackButtonComponent/Sources/BackButtonComponent.swift b/submodules/TelegramUI/Components/BackButtonComponent/Sources/BackButtonComponent.swift index d31c4d1e..01f09a66 100644 --- a/submodules/TelegramUI/Components/BackButtonComponent/Sources/BackButtonComponent.swift +++ b/submodules/TelegramUI/Components/BackButtonComponent/Sources/BackButtonComponent.swift @@ -88,7 +88,7 @@ public final class BackButtonComponent: Component { self.component = component if self.arrowView.image == nil { - self.arrowView.image = NavigationBar.backArrowImage(color: .white)?.withRenderingMode(.alwaysTemplate) + self.arrowView.image = navigationBarBackArrowImage(color: .white)?.withRenderingMode(.alwaysTemplate) } self.arrowView.tintColor = component.color diff --git a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/BackButtonView.swift b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/BackButtonView.swift index 696d0a57..a8a355ba 100644 --- a/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/BackButtonView.swift +++ b/submodules/TelegramUI/Components/Calls/CallScreen/Sources/Components/BackButtonView.swift @@ -29,7 +29,7 @@ final class BackButtonView: HighlightableButton { var pressAction: (() -> Void)? override init(frame: CGRect) { - self.iconView = UIImageView(image: NavigationBar.backArrowImage(color: .white)) + self.iconView = UIImageView(image: navigationBarBackArrowImage(color: .white)) self.iconView.isUserInteractionEnabled = false self.textView = TextView() diff --git a/submodules/TelegramUI/Components/CameraScreen/BUILD b/submodules/TelegramUI/Components/CameraScreen/BUILD index b169af47..5d92b5d4 100644 --- a/submodules/TelegramUI/Components/CameraScreen/BUILD +++ b/submodules/TelegramUI/Components/CameraScreen/BUILD @@ -95,6 +95,8 @@ swift_library( "//submodules/TelegramCallsUI", "//submodules/TelegramUI/Components/Stories/StoryContainerScreen", "//submodules/TelegramUI/Components/ChatEntityKeyboardInputNode", + "//submodules/TelegramUI/Components/LiquidLens", + "//submodules/TelegramUI/Components/TabSelectionRecognizer", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift index b29f30a5..8f05aa00 100644 --- a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift @@ -2097,7 +2097,7 @@ private final class CameraScreenComponent: CombinedComponent { if !isSticker, case .none = component.cameraState.recording, component.cameraState.isStreaming == .none && !state.isTransitioning && hasAllRequiredAccess && component.cameraState.collageProgress < 1.0 - .ulpOfOne { let availableModeControlSize: CGSize if isTablet { - availableModeControlSize = CGSize(width: panelWidth, height: 120.0) + availableModeControlSize = CGSize(width: floor(panelWidth), height: 120.0) } else { availableModeControlSize = availableSize } @@ -2131,7 +2131,6 @@ private final class CameraScreenComponent: CombinedComponent { modeControlPosition = CGPoint(x: availableSize.width / 2.0, y: availableSize.height - environment.safeInsets.bottom + modeControl.size.height / 2.0 + controlsBottomInset + 16.0) } context.add(modeControl - .clipsToBounds(true) .position(modeControlPosition) .appear(.default(alpha: true)) .disappear(.default(alpha: true)) diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/ModeComponent.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/ModeComponent.swift index cfb581ad..5936b26a 100644 --- a/submodules/TelegramUI/Components/CameraScreen/Sources/ModeComponent.swift +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/ModeComponent.swift @@ -5,6 +5,8 @@ import ComponentFlow import MultilineTextComponent import TelegramPresentationData import GlassBackgroundComponent +import LiquidLens +import TabSelectionRecognizer extension CameraMode { func title(strings: PresentationStrings) -> String { @@ -70,28 +72,17 @@ final class ModeComponent: Component { final class View: UIView, ComponentTaggedView { private var component: ModeComponent? + private var state: EmptyComponentState? final class ItemView: HighlightTrackingButton { - var pressed: () -> Void = { - - } - init() { super.init(frame: .zero) - - self.isExclusiveTouch = true - - self.addTarget(self, action: #selector(self.buttonPressed), for: .touchUpInside) } required init(coder: NSCoder) { preconditionFailure() } - @objc func buttonPressed() { - self.pressed() - } - func update(isTablet: Bool, value: String, selected: Bool, tintColor: UIColor) -> CGSize { let accentColor: UIColor let normalColor: UIColor @@ -113,9 +104,15 @@ final class ModeComponent: Component { } private var backgroundView = UIView() - private var glassContainerView = GlassBackgroundContainerView() - private var selectionView = GlassBackgroundView() - private var itemViews: [Int32: ItemView] = [:] + private var backgroundContainer = GlassBackgroundContainerView() + + private var liquidLensView: LiquidLensView? + + private var itemViews: [AnyHashable: ItemView] = [:] + private var selectedItemViews: [AnyHashable: ItemView] = [:] + + private var tabSelectionRecognizer: TabSelectionRecognizer? + private var selectionGestureState: (startX: CGFloat, currentX: CGFloat, itemId: AnyHashable)? public func matches(tag: Any) -> Bool { if let component = self.component, let componentTag = component.tag { @@ -132,12 +129,11 @@ final class ModeComponent: Component { self.backgroundView.backgroundColor = UIColor(rgb: 0xffffff, alpha: 0.11) self.backgroundView.layer.cornerRadius = 24.0 - + self.layer.allowsGroupOpacity = true self.addSubview(self.backgroundView) - self.backgroundView.addSubview(self.glassContainerView) - self.glassContainerView.contentView.addSubview(self.selectionView) + self.backgroundView.addSubview(self.backgroundContainer) } required init?(coder aDecoder: NSCoder) { @@ -162,14 +158,82 @@ final class ModeComponent: Component { override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { return self.backgroundView.frame.contains(point) } - - func update(component: ModeComponent, availableSize: CGSize, transition: ComponentTransition) -> CGSize { - self.component = component - let isTablet = component.isTablet - let updatedMode = component.updatedMode + private func item(at point: CGPoint) -> AnyHashable? { + var closestItem: (AnyHashable, CGFloat)? + for (id, itemView) in self.itemViews { + if itemView.frame.contains(point) { + return id + } else { + let distance = abs(point.x - itemView.center.x) + if let closestItemValue = closestItem { + if closestItemValue.1 > distance { + closestItem = (id, distance) + } + } else { + closestItem = (id, distance) + } + } + } + return closestItem?.0 + } + + @objc private func onTabSelectionGesture(_ recognizer: TabSelectionRecognizer) { + guard let component = self.component, let liquidLensView = self.liquidLensView else { + return + } + let location = recognizer.location(in: liquidLensView.contentView) + switch recognizer.state { + case .began: + if let itemId = self.item(at: location), let itemView = self.itemViews[itemId] { + let startX = itemView.frame.minX - 4.0 + self.selectionGestureState = (startX, startX, itemId) + self.state?.updated(transition: .spring(duration: 0.4), isLocal: true) + } + case .changed: + if var selectionGestureState = self.selectionGestureState { + selectionGestureState.currentX = selectionGestureState.startX + recognizer.translation(in: self).x + if let itemId = self.item(at: location) { + selectionGestureState.itemId = itemId + } + self.selectionGestureState = selectionGestureState + self.state?.updated(transition: .immediate, isLocal: true) + } + case .ended, .cancelled: + if let selectionGestureState = self.selectionGestureState { + self.selectionGestureState = nil + if case .ended = recognizer.state { + guard let item = component.availableModes.first(where: { AnyHashable($0.rawValue) == selectionGestureState.itemId }) else { + return + } + component.updatedMode(item) + } + self.state?.updated(transition: .spring(duration: 0.4), isLocal: true) + } + default: + break + } + } + + func update(component: ModeComponent, availableSize: CGSize, state: EmptyComponentState, transition: ComponentTransition) -> CGSize { + self.component = component + self.state = state + + let isTablet = component.isTablet + + let liquidLensView: LiquidLensView + if let current = self.liquidLensView { + liquidLensView = current + } else { + liquidLensView = LiquidLensView(kind: isTablet ? .noContainer : .externalContainer) + self.liquidLensView = liquidLensView + self.backgroundContainer.contentView.addSubview(liquidLensView) + + let tabSelectionRecognizer = TabSelectionRecognizer(target: self, action: #selector(self.onTabSelectionGesture(_:))) + self.tabSelectionRecognizer = tabSelectionRecognizer + liquidLensView.addGestureRecognizer(tabSelectionRecognizer) + } - self.glassContainerView.isHidden = component.isTablet self.backgroundView.backgroundColor = component.isTablet ? .clear : UIColor(rgb: 0xffffff, alpha: 0.11) let inset: CGFloat = 23.0 @@ -177,28 +241,36 @@ final class ModeComponent: Component { var i = 0 var itemFrame = CGRect(origin: isTablet ? .zero : CGPoint(x: inset, y: 0.0), size: buttonSize) - var selectedCenter = itemFrame.minX var selectedFrame = itemFrame - var validKeys: Set = Set() + var validKeys: Set = Set() for mode in component.availableModes.reversed() { let id = mode.rawValue validKeys.insert(id) let itemView: ItemView - if let current = self.itemViews[id] { + let selectedItemView: ItemView + if let current = self.itemViews[id], let currentSelected = self.selectedItemViews[id] { itemView = current + selectedItemView = currentSelected } else { itemView = ItemView() - self.backgroundView.addSubview(itemView) + itemView.isUserInteractionEnabled = false self.itemViews[id] = itemView - } - itemView.pressed = { - updatedMode(mode) + liquidLensView.contentView.addSubview(itemView) + + selectedItemView = ItemView() + selectedItemView.isUserInteractionEnabled = false + self.selectedItemViews[id] = selectedItemView + liquidLensView.selectedContentView.addSubview(selectedItemView) } - let itemSize = itemView.update(isTablet: component.isTablet, value: mode.title(strings: component.strings), selected: mode == component.currentMode, tintColor: component.tintColor) + let itemSize = itemView.update(isTablet: component.isTablet, value: mode.title(strings: component.strings), selected: false, tintColor: component.tintColor) itemView.bounds = CGRect(origin: .zero, size: itemSize) + + let _ = selectedItemView.update(isTablet: component.isTablet, value: mode.title(strings: component.strings), selected: true, tintColor: component.tintColor) + selectedItemView.bounds = CGRect(origin: .zero, size: itemSize) + itemFrame = CGRect(origin: itemFrame.origin, size: itemSize) if mode == component.currentMode { @@ -207,21 +279,17 @@ final class ModeComponent: Component { if isTablet { itemView.center = CGPoint(x: availableSize.width / 2.0, y: itemFrame.midY) - if mode == component.currentMode { - selectedCenter = itemFrame.midY - } + selectedItemView.center = itemView.center itemFrame = itemFrame.offsetBy(dx: 0.0, dy: tabletButtonSize.height + spacing) } else { itemView.center = CGPoint(x: itemFrame.midX, y: itemFrame.midY) - if mode == component.currentMode { - selectedCenter = itemFrame.midX - } + selectedItemView.center = itemView.center itemFrame = itemFrame.offsetBy(dx: itemFrame.width + spacing, dy: 0.0) } i += 1 } - var removeKeys: [Int32] = [] + var removeKeys: [AnyHashable] = [] for (id, itemView) in self.itemViews { if !validKeys.contains(id) { removeKeys.append(id) @@ -237,10 +305,12 @@ final class ModeComponent: Component { let totalSize: CGSize let size: CGSize + var cornerRadius: CGFloat? if isTablet { totalSize = CGSize(width: availableSize.width, height: tabletButtonSize.height * CGFloat(component.availableModes.count) + spacing * CGFloat(component.availableModes.count - 1)) size = CGSize(width: availableSize.width, height: availableSize.height) - transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(x: 0.0, y: availableSize.height / 2.0 - selectedCenter), size: totalSize)) + transition.setFrame(view: self.backgroundView, frame: CGRect(origin: .zero, size: totalSize)) + cornerRadius = 20.0 } else { size = CGSize(width: availableSize.width, height: buttonSize.height) totalSize = CGSize(width: itemFrame.minX - spacing + inset, height: buttonSize.height) @@ -248,12 +318,26 @@ final class ModeComponent: Component { } let containerFrame = CGRect(origin: .zero, size: self.backgroundView.frame.size) - transition.setFrame(view: self.glassContainerView, frame: containerFrame) + transition.setFrame(view: self.backgroundContainer, frame: containerFrame) - let selectionFrame = selectedFrame.insetBy(dx: -20.0, dy: 3.0) - self.glassContainerView.update(size: containerFrame.size, isDark: true, transition: .immediate) - self.selectionView.update(size: selectionFrame.size, cornerRadius: selectionFrame.height * 0.5, isDark: true, tintColor: .init(kind: .custom, color: UIColor(rgb: 0xffffff, alpha: 0.16)), transition: transition) - transition.setFrame(view: self.selectionView, frame: selectionFrame) + let selectionFrame = selectedFrame.insetBy(dx: -23.0, dy: 3.0) + var lensSelection: (origin: CGPoint, size: CGSize) + if let selectionGestureState = self.selectionGestureState, !isTablet { + lensSelection = (CGPoint(x: selectionGestureState.currentX, y: 0.0), selectionFrame.size) + } else { + lensSelection = (CGPoint(x: selectionFrame.minX, y: selectionFrame.minY), selectionFrame.size) + } + + if isTablet { + lensSelection.size.width = size.width + } else { + lensSelection.size.height = containerFrame.size.height + lensSelection.origin.y = 0.0 + } + + transition.setFrame(view: liquidLensView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: containerFrame.size)) + liquidLensView.update(size: containerFrame.size, cornerRadius: cornerRadius, selectionOrigin: CGPoint(x: max(0.0, min(lensSelection.origin.x, containerFrame.size.width - lensSelection.size.width)), y: lensSelection.origin.y), selectionSize: lensSelection.size, inset: 3.0, isDark: true, isLifted: self.selectionGestureState != nil && !isTablet, isCollapsed: false, transition: transition) + self.backgroundContainer.update(size: containerFrame.size, isDark: true, transition: .immediate) return size } @@ -264,7 +348,7 @@ final class ModeComponent: Component { } func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { - return view.update(component: self, availableSize: availableSize, transition: transition) + return view.update(component: self, availableSize: availableSize, state: state, transition: transition) } } diff --git a/submodules/TelegramUI/Components/Chat/ChatAgeRestrictionAlertController/BUILD b/submodules/TelegramUI/Components/Chat/ChatAgeRestrictionAlertController/BUILD new file mode 100644 index 00000000..c60929ee --- /dev/null +++ b/submodules/TelegramUI/Components/Chat/ChatAgeRestrictionAlertController/BUILD @@ -0,0 +1,27 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "ChatAgeRestrictionAlertController", + module_name = "ChatAgeRestrictionAlertController", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", + "//submodules/AsyncDisplayKit:AsyncDisplayKit", + "//submodules/Display:Display", + "//submodules/Postbox:Postbox", + "//submodules/TelegramCore:TelegramCore", + "//submodules/AccountContext:AccountContext", + "//submodules/TelegramPresentationData:TelegramPresentationData", + "//submodules/ComponentFlow", + "//submodules/TelegramUI/Components/AlertComponent", + "//submodules/TelegramUI/Components/AlertComponent/AlertCheckComponent", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/Chat/ChatAgeRestrictionAlertController/Sources/ChatAgeRestrictionAlertController.swift b/submodules/TelegramUI/Components/Chat/ChatAgeRestrictionAlertController/Sources/ChatAgeRestrictionAlertController.swift new file mode 100644 index 00000000..cf87df83 --- /dev/null +++ b/submodules/TelegramUI/Components/Chat/ChatAgeRestrictionAlertController/Sources/ChatAgeRestrictionAlertController.swift @@ -0,0 +1,64 @@ +import Foundation +import UIKit +import SwiftSignalKit +import AsyncDisplayKit +import Display +import Postbox +import TelegramCore +import TelegramPresentationData +import AccountContext +import ComponentFlow +import AlertComponent +import AlertCheckComponent + +public func chatAgeRestrictionAlertController( + context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal)?, + parentController: ViewController, + completion: @escaping (Bool) -> Void +) -> ViewController { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let strings = presentationData.strings + + let checkState = AlertCheckComponent.ExternalState() + + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: strings.SensitiveContent_Title) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(strings.SensitiveContent_Text)) + ) + )) + content.append(AnyComponentWithIdentity( + id: "check", + component: AnyComponent( + AlertCheckComponent(title: strings.SensitiveContent_ShowAlways, initialValue: false, externalState: checkState) + ) + )) + + var effectiveUpdatedPresentationData: (PresentationData, Signal) + if let updatedPresentationData { + effectiveUpdatedPresentationData = updatedPresentationData + } else { + effectiveUpdatedPresentationData = (presentationData, context.sharedContext.presentationData) + } + + let alertController = AlertScreen( + configuration: AlertScreen.Configuration(actionAlignment: .vertical), + content: content, + actions: [ + .init(title: strings.SensitiveContent_ViewAnyway, type: .default, action: { + completion(checkState.value) + }), + .init(title: strings.Common_Cancel) + ], + updatedPresentationData: effectiveUpdatedPresentationData + ) + return alertController +} diff --git a/submodules/TelegramUI/Components/Chat/ChatAvatarNavigationNode/Sources/ChatAvatarNavigationNode.swift b/submodules/TelegramUI/Components/Chat/ChatAvatarNavigationNode/Sources/ChatAvatarNavigationNode.swift index 671acfb7..4687fad8 100644 --- a/submodules/TelegramUI/Components/Chat/ChatAvatarNavigationNode/Sources/ChatAvatarNavigationNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatAvatarNavigationNode/Sources/ChatAvatarNavigationNode.swift @@ -75,12 +75,8 @@ public final class ChatAvatarNavigationNode: ASDisplayNode { strongSelf.contextAction?(strongSelf.containerNode, gesture) } - self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 37.0, height: 37.0)).offsetBy(dx: 10.0, dy: 1.0) - self.avatarNode.frame = self.containerNode.bounds - - #if DEBUG - //self.hasUnseenStories = true - #endif + self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 44.0, height: 44.0)) + self.avatarNode.frame = self.containerNode.bounds.insetBy(dx: 3.0, dy: 3.0) } deinit { @@ -265,7 +261,7 @@ public final class ChatAvatarNavigationNode: ASDisplayNode { } override public func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { - return CGSize(width: 37.0, height: 37.0) + return CGSize(width: 44.0, height: 44.0) } public func onLayout() { diff --git a/submodules/TelegramUI/Components/Chat/ChatBotInfoItem/Sources/ChatBotInfoItem.swift b/submodules/TelegramUI/Components/Chat/ChatBotInfoItem/Sources/ChatBotInfoItem.swift index 19a4ebbc..ed37649c 100644 --- a/submodules/TelegramUI/Components/Chat/ChatBotInfoItem/Sources/ChatBotInfoItem.swift +++ b/submodules/TelegramUI/Components/Chat/ChatBotInfoItem/Sources/ChatBotInfoItem.swift @@ -118,7 +118,7 @@ public final class ChatBotInfoItemNode: ListViewItemNode { self.textNode = TextNode() self.titleNode = TextNode() - super.init(layerBacked: false, dynamicBounce: true, rotated: true) + super.init(layerBacked: false, rotated: true) self.transform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) diff --git a/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift index 3d6f717b..80662ed3 100644 --- a/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatChannelSubscriberInputPanelNode/Sources/ChatChannelSubscriberInputPanelNode.swift @@ -177,51 +177,8 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode { private var layoutData: (CGFloat, CGFloat, CGFloat, CGFloat, UIEdgeInsets, CGFloat, CGFloat, Bool, LayoutMetrics)? public override init() { - /*self.button = HighlightableButton() - self.buttonBackgroundView = GlassBackgroundView() - self.buttonBackgroundView.isUserInteractionEnabled = false - self.buttonTitle = ImmediateTextNode() - self.buttonTitle.isUserInteractionEnabled = false - self.buttonTintTitle = ImmediateTextNode() - self.buttonBackgroundView.contentView.addSubview(self.buttonTitle.view) - self.buttonBackgroundView.maskContentView.addSubview(self.buttonTintTitle.view) - self.buttonBackgroundView.contentView.addSubview(self.button) - - self.helpButton = HighlightableButton() - self.helpButtonBackgroundView = GlassBackgroundView() - self.helpButtonBackgroundView.isUserInteractionEnabled = false - self.helpButtonIconView = GlassBackgroundView.ContentImageView() - self.helpButtonBackgroundView.contentView.addSubview(self.helpButtonIconView) - self.helpButtonBackgroundView.contentView.addSubview(self.helpButton) - self.helpButtonBackgroundView.isHidden = true - - self.giftButton = HighlightableButton() - self.giftButtonBackgroundView = GlassBackgroundView() - self.giftButtonBackgroundView.isUserInteractionEnabled = false - self.giftButtonIconView = GlassBackgroundView.ContentImageView() - self.giftButtonBackgroundView.contentView.addSubview(self.giftButtonIconView) - self.giftButtonBackgroundView.contentView.addSubview(self.giftButton) - self.giftButtonBackgroundView.isHidden = true - - self.suggestedPostButton = HighlightableButton() - self.suggestedPostButtonBackgroundView = GlassBackgroundView() - self.suggestedPostButtonBackgroundView.isUserInteractionEnabled = false - self.suggestedPostButtonIconView = GlassBackgroundView.ContentImageView() - self.suggestedPostButtonBackgroundView.contentView.addSubview(self.suggestedPostButtonIconView) - self.suggestedPostButtonBackgroundView.contentView.addSubview(self.suggestedPostButton) - self.suggestedPostButtonBackgroundView.isHidden = true*/ - super.init() - /*self.view.addSubview(self.buttonBackgroundView) - self.view.addSubview(self.helpButtonBackgroundView) - self.view.addSubview(self.giftButtonBackgroundView) - self.view.addSubview(self.suggestedPostButtonBackgroundView) - self.button.addTarget(self, action: #selector(self.buttonPressed), for: .touchUpInside) - self.helpButton.addTarget(self, action: #selector(self.helpPressed), for: .touchUpInside) - self.giftButton.addTarget(self, action: #selector(self.giftPressed), for: .touchUpInside) - self.suggestedPostButton.addTarget(self, action: #selector(self.suggestedPostPressed), for: .touchUpInside)*/ - self.view.addSubview(self.panelContainer) } @@ -495,7 +452,8 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode { self?.buttonPressed() } )], - background: centerAction.isAccent ? .activeTint : .panel + background: centerAction.isAccent ? .activeTint : .panel, + keepWide: true ) } diff --git a/submodules/TelegramUI/Components/Chat/ChatHistoryEntry/Sources/ChatHistoryEntry.swift b/submodules/TelegramUI/Components/Chat/ChatHistoryEntry/Sources/ChatHistoryEntry.swift index aa000a8a..f2009782 100644 --- a/submodules/TelegramUI/Components/Chat/ChatHistoryEntry/Sources/ChatHistoryEntry.swift +++ b/submodules/TelegramUI/Components/Chat/ChatHistoryEntry/Sources/ChatHistoryEntry.swift @@ -56,53 +56,53 @@ public enum ChatHistoryEntry: Identifiable, Comparable { case UnreadEntry(MessageIndex, ChatPresentationData) case ReplyCountEntry(MessageIndex, Bool, Int, ChatPresentationData) case ChatInfoEntry(ChatInfoData, ChatPresentationData) - case SearchEntry(PresentationTheme, PresentationStrings) public var stableId: UInt64 { switch self { - case let .MessageEntry(message, _, _, _, _, attributes): - let type: UInt64 - switch attributes.contentTypeHint { - case .generic: - type = 2 - case .largeEmoji: - type = 3 - case .animatedEmoji: - type = 4 - } - return UInt64(message.stableId) | ((type << 40)) - case let .MessageGroupEntry(groupInfo, _, _): - return UInt64(bitPattern: groupInfo) | ((UInt64(2) << 40)) - case .UnreadEntry: - return UInt64(4) << 40 - case .ReplyCountEntry: - return UInt64(5) << 40 - case .ChatInfoEntry: - return UInt64(6) << 40 - case .SearchEntry: + case let .MessageEntry(message, _, _, _, _, attributes): + let type: UInt64 + switch attributes.contentTypeHint { + case .generic: + type = 2 + case .largeEmoji: + type = 3 + case .animatedEmoji: + type = 4 + } + return UInt64(message.stableId) | ((type << 40)) + case let .MessageGroupEntry(groupInfo, _, _): + return UInt64(bitPattern: groupInfo) | ((UInt64(2) << 40)) + case .UnreadEntry: + return UInt64(4) << 40 + case .ReplyCountEntry: + return UInt64(5) << 40 + case let .ChatInfoEntry(infoData, _): + switch infoData { + case .newThreadInfo: return UInt64(7) << 40 + default: + return UInt64(6) << 40 + } } } public var index: MessageIndex { switch self { - case let .MessageEntry(message, _, _, _, _, _): - return message.index - case let .MessageGroupEntry(_, messages, _): - return messages[messages.count - 1].0.index - case let .UnreadEntry(index, _): - return index - case let .ReplyCountEntry(index, _, _, _): - return index - case let .ChatInfoEntry(infoData, _): - switch infoData { - case .newThreadInfo: - return MessageIndex.absoluteUpperBound() - default: - return MessageIndex.absoluteLowerBound() - } - case .SearchEntry: + case let .MessageEntry(message, _, _, _, _, _): + return message.index + case let .MessageGroupEntry(_, messages, _): + return messages[messages.count - 1].0.index + case let .UnreadEntry(index, _): + return index + case let .ReplyCountEntry(index, _, _, _): + return index + case let .ChatInfoEntry(infoData, _): + switch infoData { + case .newThreadInfo: + return MessageIndex.absoluteUpperBound() + default: return MessageIndex.absoluteLowerBound() + } } } @@ -123,8 +123,6 @@ public enum ChatHistoryEntry: Identifiable, Comparable { default: return MessageIndex.absoluteLowerBound() } - case .SearchEntry: - return MessageIndex.absoluteLowerBound() } } @@ -297,12 +295,6 @@ public enum ChatHistoryEntry: Identifiable, Comparable { } else { return false } - case let .SearchEntry(lhsTheme, lhsStrings): - if case let .SearchEntry(rhsTheme, rhsStrings) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings { - return true - } else { - return false - } } } diff --git a/submodules/TelegramUI/Components/Chat/ChatHistorySearchContainerNode/Sources/ChatHistorySearchContainerNode.swift b/submodules/TelegramUI/Components/Chat/ChatHistorySearchContainerNode/Sources/ChatHistorySearchContainerNode.swift index e45a3a02..417e776d 100644 --- a/submodules/TelegramUI/Components/Chat/ChatHistorySearchContainerNode/Sources/ChatHistorySearchContainerNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatHistorySearchContainerNode/Sources/ChatHistorySearchContainerNode.swift @@ -158,7 +158,7 @@ public final class ChatHistorySearchContainerNode: SearchDisplayControllerConten self.themeAndStringsPromise = Promise((self.presentationData.theme, self.presentationData.strings, self.presentationData.dateTimeFormat, self.presentationData.listsFontSize)) self.dimNode = ASDisplayNode() - self.dimNode.backgroundColor = UIColor.black.withAlphaComponent(0.5) + self.dimNode.backgroundColor = .clear self.listNode = ListView() self.listNode.accessibilityPageScrolledString = { row, count in return presentationData.strings.VoiceOver_ScrollStatus(row, count).string diff --git a/submodules/TelegramUI/Components/Chat/ChatInlineSearchResultsListComponent/Sources/ChatInlineSearchResultsListComponent.swift b/submodules/TelegramUI/Components/Chat/ChatInlineSearchResultsListComponent/Sources/ChatInlineSearchResultsListComponent.swift index ba8e6418..c84ec35a 100644 --- a/submodules/TelegramUI/Components/Chat/ChatInlineSearchResultsListComponent/Sources/ChatInlineSearchResultsListComponent.swift +++ b/submodules/TelegramUI/Components/Chat/ChatInlineSearchResultsListComponent/Sources/ChatInlineSearchResultsListComponent.swift @@ -901,8 +901,6 @@ public final class ChatInlineSearchResultsListComponent: Component { }, openStarsTopup: { _ in }, - dismissNotice: { _ in - }, editPeer: { _ in }, openWebApp: { _ in diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageActionButtonsNode/Sources/ChatMessageActionButtonsNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageActionButtonsNode/Sources/ChatMessageActionButtonsNode.swift index 80adb8f2..cb3801d0 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageActionButtonsNode/Sources/ChatMessageActionButtonsNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageActionButtonsNode/Sources/ChatMessageActionButtonsNode.swift @@ -440,6 +440,7 @@ private final class ChatMessageActionButtonNode: ASDisplayNode { if node.iconNode == nil { let iconNode = ASImageNode() iconNode.contentMode = .center + iconNode.isUserInteractionEnabled = false node.iconNode = iconNode node.addSubnode(iconNode) } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/BUILD b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/BUILD index b5507029..30fadb76 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/BUILD +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/BUILD @@ -32,6 +32,7 @@ swift_library( "//submodules/WallpaperBackgroundNode", "//submodules/LocalMediaResources", "//submodules/AppBundle", + "//submodules/TelegramStringFormatting", "//submodules/ChatPresentationInterfaceState", "//submodules/TelegramUI/Components/TextNodeWithEntities", "//submodules/TelegramUI/Components/ChatControllerInteraction", diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift index 001202a7..9a835b50 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -47,6 +47,7 @@ import ManagedDiceAnimationNode import MessageHaptics import ChatMessageTransitionNode import ChatMessageSuggestedPostInfoNode +import TelegramStringFormatting private let nameFont = Font.medium(14.0) private let inlineBotPrefixFont = Font.regular(14.0) @@ -99,6 +100,11 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { private var swipeToReplyNode: ChatMessageSwipeToReplyNode? private var swipeToReplyFeedback: HapticFeedback? + private let labelNode: TextNodeWithEntities + private var labelBackgroundNode: WallpaperBubbleBackgroundNode? + private let labelBackgroundMaskNode: ASImageNode + private var cachedMaskLabelBackgroundImage: (CGPoint, UIImage, [CGRect])? + private var selectionNode: ChatMessageSelectionNode? private var deliveryFailedNode: ChatMessageDeliveryFailedNode? private var shareButtonNode: ChatMessageShareButton? @@ -161,6 +167,12 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { self.textNode.textNode.displaysAsynchronously = false self.textNode.textNode.isUserInteractionEnabled = false + self.labelNode = TextNodeWithEntities() + self.labelNode.textNode.isUserInteractionEnabled = false + self.labelNode.textNode.displaysAsynchronously = false + + self.labelBackgroundMaskNode = ASImageNode() + super.init(rotated: rotated) self.containerNode.shouldBegin = { [weak self] location in @@ -469,9 +481,24 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } } else if let telegramDice = self.telegramDice, let diceNode = self.animationNode as? ManagedDiceAnimationNode { if let value = telegramDice.value { + let wasRolling = diceNode.isRolling diceNode.setState(value == 0 ? .rolling : .value(value, true)) + + if wasRolling && !diceNode.isRolling { + Queue.mainQueue().after(3.0, { + self.labelNode.textNode.alpha = 1.0 + self.labelBackgroundNode?.alpha = 1.0 + self.labelNode.textNode.layer.animateScale(from: 0.01, to: 1.0, duration: 0.25) + self.labelBackgroundNode?.layer.animateScale(from: 0.01, to: 1.0, duration: 0.25) + self.labelNode.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) + self.labelBackgroundNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) + }) + } } else { diceNode.setState(.rolling) + + self.labelNode.textNode.alpha = 0.0 + self.labelBackgroundNode?.alpha = 0.0 } } else if self.telegramFile == nil && self.telegramDice == nil { let (emoji, fitz) = item.message.text.basicEmoji @@ -816,6 +843,9 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { let actionButtonsLayout = ChatMessageActionButtonsNode.asyncLayout(self.actionButtonsNode) let reactionButtonsLayout = ChatMessageReactionButtonsNode.asyncLayout(self.reactionButtonsNode) + let makeLabelLayout = TextNodeWithEntities.asyncLayout(self.labelNode) + let cachedMaskLabelBackgroundImage = self.cachedMaskLabelBackgroundImage + let makeForwardInfoLayout = ChatMessageForwardInfoNode.asyncLayout(self.forwardInfoNode) let viaBotLayout = TextNode.asyncLayout(self.viaBotNode) @@ -850,6 +880,44 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { let avatarInset: CGFloat var hasAvatar = false + let labelAttributedText = universalServiceMessageString(presentationData: (item.presentationData.theme.theme, item.presentationData.theme.wallpaper), strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, message: EngineMessage(item.message), accountPeerId: item.context.account.peerId, forChatList: false, forForumOverview: false, forAdditionalServiceMessage: true) + + let (labelLayout, labelApply) = makeLabelLayout(TextNodeLayoutArguments(attributedString: labelAttributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets())) + + var labelRects = labelLayout.linesRects() + if labelRects.count > 1 { + let sortedIndices = (0 ..< labelRects.count).sorted(by: { labelRects[$0].width > labelRects[$1].width }) + for i in 0 ..< sortedIndices.count { + let index = sortedIndices[i] + for j in -1 ... 1 { + if j != 0 && index + j >= 0 && index + j < sortedIndices.count { + if abs(labelRects[index + j].width - labelRects[index].width) < 40.0 { + labelRects[index + j].size.width = max(labelRects[index + j].width, labelRects[index].width) + labelRects[index].size.width = labelRects[index + j].size.width + } + } + } + } + } + for i in 0 ..< labelRects.count { + labelRects[i] = labelRects[i].insetBy(dx: -7.0, dy: floor((labelRects[i].height - 22.0) / 2.0)) + labelRects[i].size.height = 22.0 + labelRects[i].origin.x = floor((labelLayout.size.width - labelRects[i].width) / 2.0) + } + + let backgroundMaskImage: (CGPoint, UIImage)? + var backgroundMaskUpdated = false + if labelLayout.size.height > 0.0 { + if let (currentOffset, currentImage, currentRects) = cachedMaskLabelBackgroundImage, currentRects == labelRects { + backgroundMaskImage = (currentOffset, currentImage) + } else { + backgroundMaskImage = LinkHighlightingNode.generateImage(color: .black, inset: 0.0, innerRadius: 11.0, outerRadius: 11.0, rects: labelRects, useModernPathCalculation: false) + backgroundMaskUpdated = true + } + } else { + backgroundMaskImage = nil + } + switch item.chatLocation { case let .peer(peerId): if peerId != item.context.account.peerId { @@ -1061,6 +1129,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { var viewCount: Int? = nil var dateReplies = 0 var starsCount: Int64? + var tonAmount: Int64? var dateReactionsAndPeers = mergedMessageReactionsAndPeers(accountPeerId: item.context.account.peerId, accountPeer: item.associatedData.accountPeer, message: item.message) if item.message.isRestricted(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) { dateReactionsAndPeers = ([], []) @@ -1079,6 +1148,10 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } } + if let stakeTonAmount = telegramDice?.tonAmount { + tonAmount = stakeTonAmount + } + let dateText = stringForMessageTimestampStatus(accountPeerId: item.context.account.peerId, message: item.message, dateTimeFormat: item.presentationData.dateTimeFormat, nameDisplayOrder: item.presentationData.nameDisplayOrder, strings: item.presentationData.strings, format: .regular, associatedData: item.associatedData) var isReplyThread = false @@ -1107,6 +1180,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { messageEffect: messageEffect, replyCount: dateReplies, starsCount: starsCount, + tonAmount: tonAmount, isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread, hasAutoremove: item.message.isSelfExpiring, canViewReactionList: canViewMessageReactionList(message: item.message), @@ -1211,6 +1285,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { quote: replyQuote, todoItemId: replyTodoItemId, story: replyStory, + isSummarized: false, parentMessage: item.message, constrainedSize: CGSize(width: availableContentWidth, height: CGFloat.greatestFiniteMagnitude), animationCache: item.controllerInteraction.presentationContext.animationCache, @@ -1501,6 +1576,10 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } else { updatedImageFrame = imageFrame.offsetBy(dx: 0.0, dy: floor((contentHeight - imageSize.height) / 2.0)) contextContentFrame = updatedImageFrame + + if let telegramDice, let _ = telegramDice.tonAmount { + updatedImageFrame = updatedImageFrame.offsetBy(dx: 0.0, dy: -30.0) + } } var updatedContentFrame = updatedImageFrame if isEmoji && emojiString == nil { @@ -1508,6 +1587,51 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { contextContentFrame = updatedContentFrame } + let labelFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((params.width - labelLayout.size.width) / 2.0), y: updatedImageFrame.maxY + 6.0), size: labelLayout.size) + strongSelf.labelNode.textNode.frame = labelFrame + if strongSelf.labelNode.textNode.supernode == nil, labelLayout.size.height > 0.0 { + strongSelf.addSubnode(strongSelf.labelNode.textNode) + } + + let _ = labelApply(TextNodeWithEntities.Arguments( + context: item.context, + cache: item.controllerInteraction.presentationContext.animationCache, + renderer: item.controllerInteraction.presentationContext.animationRenderer, + placeholderColor: item.presentationData.theme.theme.chat.message.freeform.withWallpaper.reactionInactiveBackground, + attemptSynchronous: synchronousLoads + )) + + let baseBackgroundFrame = labelFrame.offsetBy(dx: 0.0, dy: -11.0) + if let (offset, image) = backgroundMaskImage { + if strongSelf.labelBackgroundNode == nil { + if let backgroundNode = item.controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) { + backgroundNode.alpha = strongSelf.labelNode.textNode.alpha + strongSelf.labelBackgroundNode = backgroundNode + strongSelf.insertSubnode(backgroundNode, at: 0) + } + } + + if backgroundMaskUpdated, let backgroundNode = strongSelf.labelBackgroundNode { + if labelRects.count == 1 { + backgroundNode.clipsToBounds = true + backgroundNode.cornerRadius = labelRects[0].height / 2.0 + backgroundNode.view.mask = nil + } else { + backgroundNode.clipsToBounds = false + backgroundNode.cornerRadius = 0.0 + backgroundNode.view.mask = strongSelf.labelBackgroundMaskNode.view + } + } + + if let backgroundNode = strongSelf.labelBackgroundNode { + backgroundNode.layer.frame = CGRect(origin: CGPoint(x: baseBackgroundFrame.minX + offset.x, y: baseBackgroundFrame.minY + offset.y), size: image.size) + } + strongSelf.labelBackgroundMaskNode.image = image + strongSelf.labelBackgroundMaskNode.frame = CGRect(origin: CGPoint(), size: image.size) + + strongSelf.cachedMaskLabelBackgroundImage = (offset, image, labelRects) + } + if let (_, textApply) = textLayoutAndApply { let placeholderColor = bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.stickerPlaceholderColor, wallpaper: item.presentationData.theme.wallpaper) let _ = textApply(TextNodeWithEntities.Arguments(context: item.context, cache: item.controllerInteraction.presentationContext.animationCache, renderer: item.controllerInteraction.presentationContext.animationRenderer, placeholderColor: placeholderColor, attemptSynchronous: synchronousLoads)) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/BUILD b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/BUILD index cfc11990..d764dbd7 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/BUILD +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/BUILD @@ -94,6 +94,7 @@ swift_library( "//submodules/TelegramStringFormatting", "//submodules/AvatarNode", "//submodules/TelegramUI/Components/Chat/ChatMessageSuggestedPostInfoNode", + "//submodules/TelegramUI/Components/PremiumAlertController", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index 2d11aad1..1d378b4f 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -83,6 +83,7 @@ import TelegramAnimatedStickerNode import LottieMetal import AvatarNode import ChatMessageSuggestedPostInfoNode +import PremiumAlertController private struct BubbleItemAttributes { var index: Int? @@ -689,6 +690,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI private var unlockButtonNode: ChatMessageUnlockMediaNode? private var mediaInfoNode: ChatMessageStarsMediaInfoNode? + private var summarizeButtonNode: ChatMessageShareButton? private var shareButtonNode: ChatMessageShareButton? private let messageAccessibilityArea: AccessibilityAreaNode @@ -1222,6 +1224,10 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } } + if let summarizeButtonNode = strongSelf.summarizeButtonNode, summarizeButtonNode.frame.contains(point) { + return .fail + } + if let shareButtonNode = strongSelf.shareButtonNode, shareButtonNode.frame.contains(point) { return .fail } @@ -1724,6 +1730,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp()) var needsShareButton = false + var needsSummarizeButton = false if incoming, case let .customChatContents(contents) = item.associatedData.subject, case .hashTagSearch = contents.kind { needsShareButton = true @@ -1751,6 +1758,10 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI needsShareButton = true } + if let _ = item.message.attributes.first(where: { $0 is SummarizationMessageAttribute }) { + needsSummarizeButton = true + } + if let peer = item.message.peers[item.message.id.peerId] { if let channel = peer as? TelegramChannel { if case .broadcast = channel.info { @@ -1789,6 +1800,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI loop: for media in item.message.media { if media is TelegramMediaAction { needsShareButton = false + needsSummarizeButton = false break loop } else if let media = media as? TelegramMediaFile, media.isInstantVideo { mayHaveSeparateCommentsButton = true @@ -1801,12 +1813,14 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI if mayHaveSeparateCommentsButton && hasCommentButton(item: item) { } else { needsShareButton = false + needsSummarizeButton = false } } } if isPreview { needsShareButton = false + needsSummarizeButton = false } let isAd = item.content.firstMessage.adAttribute != nil if isAd { @@ -1815,13 +1829,14 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI for attribute in item.content.firstMessage.attributes { if let attribute = attribute as? RestrictedContentMessageAttribute, attribute.platformText(platform: "ios", contentSettings: item.context.currentContentSettings.with { $0 }) != nil { needsShareButton = false + needsSummarizeButton = false } } if let subject = item.associatedData.subject, case .messageOptions = subject { needsShareButton = false } - + var tmpWidth: CGFloat if allowFullWidth { tmpWidth = baseWidth @@ -2312,6 +2327,17 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } } + let translateToLanguage = item.associatedData.translateToLanguage + var isSummarized = false + if item.controllerInteraction.summarizedMessageIds.contains(item.message.id) { + for attribute in item.message.attributes { + if let attribute = attribute as? SummarizationMessageAttribute, attribute.summaryForLang(translateToLanguage) != nil { + initialDisplayHeader = true + isSummarized = true + } + } + } + var displayHeader = false if initialDisplayHeader { if authorNameString != nil { @@ -2339,6 +2365,9 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI displayHeader = true } } + if isSummarized { + displayHeader = true + } } let firstNodeTopPosition: ChatMessageBubbleRelativePosition @@ -2717,7 +2746,11 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI hasReply = false } - if !isInstantVideo, hasReply, (replyMessage != nil || replyForward != nil || replyStory != nil) { + if isSummarized { + hasReply = true + } + + if !isInstantVideo, hasReply, (replyMessage != nil || replyForward != nil || replyStory != nil || isSummarized) { if headerSize.height.isZero { headerSize.height += 11.0 } else { @@ -2733,6 +2766,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI quote: replyQuote, todoItemId: replyTodoItemId, story: replyStory, + isSummarized: isSummarized, parentMessage: item.message, constrainedSize: CGSize(width: maximumNodeWidth - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right - 6.0, height: CGFloat.greatestFiniteMagnitude), animationCache: item.controllerInteraction.presentationContext.animationCache, @@ -3491,6 +3525,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI unlockButtonSizeAndApply: unlockButtonSizeApply, mediaInfoOrigin: mediaInfoOrigin?.offsetBy(dx: 0.0, dy: layoutInsets.top), mediaInfoSizeAndApply: mediaInfoSizeApply, + needsSummarizeButton: needsSummarizeButton, needsShareButton: needsShareButton, shareButtonOffset: shareButtonOffset, avatarOffset: avatarOffset, @@ -3556,6 +3591,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI unlockButtonSizeAndApply: (CGSize, (Bool) -> ChatMessageUnlockMediaNode?), mediaInfoOrigin: CGPoint?, mediaInfoSizeAndApply: (CGSize, (Bool) -> ChatMessageStarsMediaInfoNode?), + needsSummarizeButton: Bool, needsShareButton: Bool, shareButtonOffset: CGPoint?, avatarOffset: CGFloat?, @@ -4787,6 +4823,20 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI }) } + if needsSummarizeButton { + if strongSelf.summarizeButtonNode == nil { + let summarizeButtonNode = ChatMessageShareButton() + strongSelf.summarizeButtonNode = summarizeButtonNode + strongSelf.insertSubnode(summarizeButtonNode, belowSubnode: strongSelf.messageAccessibilityArea) + summarizeButtonNode.pressed = { [weak strongSelf] in + strongSelf?.toggleSummarization() + } + } + } else if let summarizeButtonNode = strongSelf.summarizeButtonNode { + strongSelf.summarizeButtonNode = nil + summarizeButtonNode.removeFromSupernode() + } + if needsShareButton { if strongSelf.shareButtonNode == nil { let shareButtonNode = ChatMessageShareButton() @@ -4960,6 +5010,30 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } strongSelf.messageAccessibilityArea.frame = backgroundFrame } + if let summarizeButtonNode = strongSelf.summarizeButtonNode { + let buttonSize = summarizeButtonNode.update(presentationData: item.presentationData, controllerInteraction: item.controllerInteraction, chatLocation: item.chatLocation, subject: item.associatedData.subject, message: item.message, account: item.context.account, disableComments: disablesComments, isSummarize: true) + + var buttonFrame = CGRect(origin: CGPoint(x: !incoming ? backgroundFrame.minX - buttonSize.width - 8.0 : backgroundFrame.maxX + 8.0, y: backgroundFrame.minY + 1.0), size: buttonSize) + + if let shareButtonOffset = shareButtonOffset { + if incoming { + buttonFrame.origin.x = shareButtonOffset.x + } + buttonFrame.origin.y = buttonFrame.origin.y + shareButtonOffset.y - (buttonSize.height - 30.0) + } else if !disablesComments { + buttonFrame.origin.y = buttonFrame.origin.y - (buttonSize.height - 30.0) + } + + if isSidePanelOpen { + buttonFrame.origin.x -= buttonFrame.width * 0.5 + buttonFrame.origin.y += buttonFrame.height * 0.5 + } + + animation.animator.updatePosition(layer: summarizeButtonNode.layer, position: buttonFrame.center, completion: nil) + animation.animator.updateBounds(layer: summarizeButtonNode.layer, bounds: CGRect(origin: CGPoint(), size: buttonFrame.size), completion: nil) + animation.animator.updateAlpha(layer: summarizeButtonNode.layer, alpha: (isCurrentlyPlayingMedia || isSidePanelOpen) ? 0.0 : 1.0, completion: nil) + animation.animator.updateScale(layer: summarizeButtonNode.layer, scale: (isCurrentlyPlayingMedia || isSidePanelOpen) ? 0.001 : 1.0, completion: nil) + } if let shareButtonNode = strongSelf.shareButtonNode { let buttonSize = shareButtonNode.update(presentationData: item.presentationData, controllerInteraction: item.controllerInteraction, chatLocation: item.chatLocation, subject: item.associatedData.subject, message: item.message, account: item.context.account, disableComments: disablesComments) @@ -4994,6 +5068,30 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI strongSelf.backgroundFrameTransition = nil }*/ strongSelf.messageAccessibilityArea.frame = backgroundFrame + if let summarizeButtonNode = strongSelf.summarizeButtonNode { + let buttonSize = summarizeButtonNode.update(presentationData: item.presentationData, controllerInteraction: item.controllerInteraction, chatLocation: item.chatLocation, subject: item.associatedData.subject, message: item.message, account: item.context.account, disableComments: disablesComments, isSummarize: true) + + var buttonFrame = CGRect(origin: CGPoint(x: !incoming ? backgroundFrame.minX - buttonSize.width - 8.0 : backgroundFrame.maxX + 8.0, y: backgroundFrame.minY + 1.0), size: buttonSize) + + if let shareButtonOffset = shareButtonOffset { + if incoming { + buttonFrame.origin.x = shareButtonOffset.x + } + buttonFrame.origin.y = buttonFrame.origin.y + shareButtonOffset.y - (buttonSize.height - 30.0) + } else if !disablesComments { + buttonFrame.origin.y = buttonFrame.origin.y - (buttonSize.height - 30.0) + } + + if isSidePanelOpen { + buttonFrame.origin.x -= buttonFrame.width * 0.5 + buttonFrame.origin.y += buttonFrame.height * 0.5 + } + + animation.animator.updatePosition(layer: summarizeButtonNode.layer, position: buttonFrame.center, completion: nil) + animation.animator.updateBounds(layer: summarizeButtonNode.layer, bounds: CGRect(origin: CGPoint(), size: buttonFrame.size), completion: nil) + animation.animator.updateAlpha(layer: summarizeButtonNode.layer, alpha: (isCurrentlyPlayingMedia || isSidePanelOpen) ? 0.0 : 1.0, completion: nil) + animation.animator.updateScale(layer: summarizeButtonNode.layer, scale: (isCurrentlyPlayingMedia || isSidePanelOpen) ? 0.001 : 1.0, completion: nil) + } if let shareButtonNode = strongSelf.shareButtonNode { let buttonSize = shareButtonNode.update(presentationData: item.presentationData, controllerInteraction: item.controllerInteraction, chatLocation: item.chatLocation, subject: item.associatedData.subject, message: item.message, account: item.context.account, disableComments: disablesComments) @@ -5275,6 +5373,15 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } } else if let replyInfoNode = self.replyInfoNode, self.item?.controllerInteraction.tapMessage == nil, replyInfoNode.frame.contains(location) { if let item = self.item { + if item.controllerInteraction.summarizedMessageIds.contains(item.message.id) { + return .action(InternalBubbleTapAction.Action({ [weak self] in + guard let self else { + return + } + self.toggleSummarization() + })) + } + for attribute in item.message.attributes { if let attribute = attribute as? ReplyMessageAttribute { if let threadId = item.message.threadId, Int32(clamping: threadId) == attribute.messageId.id, let quotedReply = item.message.attributes.first(where: { $0 is QuotedReplyMessageAttribute }) as? QuotedReplyMessageAttribute { @@ -5824,6 +5931,10 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI return boostButtonNode.view } + if let summarizeButtonNode = self.summarizeButtonNode, summarizeButtonNode.frame.contains(point) { + return summarizeButtonNode.view.hitTest(self.view.convert(point, to: summarizeButtonNode.view), with: event) + } + if let shareButtonNode = self.shareButtonNode, shareButtonNode.frame.contains(point) { return shareButtonNode.view.hitTest(self.view.convert(point, to: shareButtonNode.view), with: event) } @@ -6516,6 +6627,14 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI container.updateAbsoluteRect(containerFrame, within: containerSize) } + if let summarizeButtonNode = self.summarizeButtonNode { + var summarizeButtonNodeFrame = summarizeButtonNode.frame + summarizeButtonNodeFrame.origin.x += rect.minX + summarizeButtonNodeFrame.origin.y += rect.minY + + summarizeButtonNode.updateAbsoluteRect(summarizeButtonNodeFrame, within: containerSize) + } + if let shareButtonNode = self.shareButtonNode { var shareButtonNodeFrame = shareButtonNode.frame shareButtonNodeFrame.origin.x += rect.minX @@ -6854,6 +6973,45 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI self.updateVisibility(isScroll: false) } + private func toggleSummarization() { + guard let item = self.item else { + return + } + + if item.controllerInteraction.summarizedMessageIds.contains(item.message.id) { + item.controllerInteraction.summarizedMessageIds.remove(item.message.id) + let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, false) + } else { + item.controllerInteraction.summarizedMessageIds.insert(item.message.id) + let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, false) + + let translateToLanguage = item.associatedData.translateToLanguage + var requestSummary = true + for attribute in item.message.attributes { + if let attribute = attribute as? SummarizationMessageAttribute, attribute.summaryForLang(translateToLanguage) != nil { + requestSummary = false + break + } + } + if requestSummary { + let _ = (item.context.engine.messages.summarizeMessage(messageId: item.message.id, translateToLang: translateToLanguage) + |> deliverOnMainQueue).start(error: { error in + if case .limitExceededPremium = error, let parentController = item.controllerInteraction.navigationController()?.topViewController as? ViewController { + item.controllerInteraction.summarizedMessageIds.remove(item.message.id) + let _ = item.controllerInteraction.requestMessageUpdate(item.message.id, false) + let controller = premiumAlertController( + context: item.context, + parentController: parentController, + title: item.presentationData.strings.Conversation_Summary_Limit_Title, + text: item.presentationData.strings.Conversation_Summary_Limit_Text + ) + parentController.present(controller, in: .window(.root)) + } + }) + } + } + } + private func updateVisibility(isScroll: Bool) { guard let item = self.item else { return diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift index 864920d6..eb78bd5a 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift @@ -12,6 +12,7 @@ import ReactionButtonListComponent import ReactionImageComponent import AnimationCache import MultiAnimationRenderer +import TelegramStringFormatting private func maybeAddRotationAnimation(_ layer: CALayer, duration: Double) { if let _ = layer.animation(forKey: "clockFrameAnimation") { @@ -197,8 +198,10 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { var messageEffect: AvailableMessageEffects.MessageEffect? var replyCount: Int var starsCount: Int64? + var tonAmount: Int64? var isPinned: Bool var hasAutoremove: Bool + var isDeleted: Bool var canViewReactionList: Bool var animationCache: AnimationCache var animationRenderer: MultiAnimationRenderer @@ -222,8 +225,10 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { messageEffect: AvailableMessageEffects.MessageEffect?, replyCount: Int, starsCount: Int64?, + tonAmount: Int64? = nil, isPinned: Bool, hasAutoremove: Bool, + isDeleted: Bool = false, canViewReactionList: Bool, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer @@ -246,8 +251,10 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { self.messageEffect = messageEffect self.replyCount = replyCount self.starsCount = starsCount + self.tonAmount = tonAmount self.isPinned = isPinned self.hasAutoremove = hasAutoremove + self.isDeleted = isDeleted self.canViewReactionList = canViewReactionList self.animationCache = animationCache self.animationRenderer = animationRenderer @@ -270,6 +277,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { private var replyCountNode: TextNode? private var starsIcon: ASImageNode? private var starsCountNode: TextNode? + private var deletedIcon: ASImageNode? private var type: ChatMessageDateAndStatusType? private var theme: ChatPresentationThemeData? @@ -417,8 +425,10 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { } else if arguments.isPinned { repliesImage = graphics.incomingDateAndStatusPinnedIcon } - if (arguments.starsCount ?? 0) != 0 { + if (arguments.starsCount ?? 0) != 0 { starsImage = graphics.incomingDateAndStatusStarsIcon + } else if (arguments.tonAmount ?? 0) != 0 { + starsImage = graphics.incomingDateAndStatusTonIcon } case let .BubbleOutgoing(status): dateColor = arguments.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor @@ -438,6 +448,8 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { } if (arguments.starsCount ?? 0) != 0 { starsImage = graphics.outgoingDateAndStatusStarsIcon + } else if (arguments.tonAmount ?? 0) != 0 { + starsImage = graphics.outgoingDateAndStatusTonIcon } case .ImageIncoming: dateColor = arguments.presentationData.theme.theme.chat.message.mediaDateAndStatusTextColor @@ -457,6 +469,8 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { } if (arguments.starsCount ?? 0) != 0 { starsImage = graphics.mediaStarsIcon + } else if (arguments.tonAmount ?? 0) != 0 { + starsImage = graphics.mediaTonIcon } case let .ImageOutgoing(status): dateColor = arguments.presentationData.theme.theme.chat.message.mediaDateAndStatusTextColor @@ -477,6 +491,8 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { } if (arguments.starsCount ?? 0) != 0 { starsImage = graphics.mediaStarsIcon + } else if (arguments.tonAmount ?? 0) != 0 { + starsImage = graphics.mediaTonIcon } case .FreeIncoming: let serviceColor = serviceMessageColorComponents(theme: arguments.presentationData.theme.theme, wallpaper: arguments.presentationData.theme.wallpaper) @@ -498,6 +514,8 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { } if (arguments.starsCount ?? 0) != 0 { starsImage = graphics.freeStarsIcon + } else if (arguments.tonAmount ?? 0) != 0 { + starsImage = graphics.freeTonIcon } case let .FreeOutgoing(status): let serviceColor = serviceMessageColorComponents(theme: arguments.presentationData.theme.theme, wallpaper: arguments.presentationData.theme.wallpaper) @@ -519,6 +537,8 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { } if (arguments.starsCount ?? 0) != 0 { starsImage = graphics.freeStarsIcon + } else if (arguments.tonAmount ?? 0) != 0 { + starsImage = graphics.freeTonIcon } } @@ -586,6 +606,36 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { currentStarsIcon = nil } + // ANTIDELETE: Deleted message icon + var currentDeletedIcon = self?.deletedIcon + var deletedIconSize = CGSize() + if arguments.isDeleted { + if currentDeletedIcon == nil { + let iconNode = ASImageNode() + iconNode.isLayerBacked = true + iconNode.displayWithoutProcessing = true + iconNode.displaysAsynchronously = false + currentDeletedIcon = iconNode + } + // Use trash icon from bundle or create simple one + let deletedImage = UIImage(bundleImageName: "Chat/Message/DeletedIcon") ?? generateTintedImage(image: UIImage(systemName: "trash"), color: dateColor) + deletedIconSize = deletedImage?.size ?? CGSize(width: 10, height: 10) + // Scale down the image + if let img = deletedImage { + let scaledSize = CGSize(width: 10, height: 10) + UIGraphicsBeginImageContextWithOptions(scaledSize, false, 0.0) + img.draw(in: CGRect(origin: .zero, size: scaledSize)) + let scaledImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + currentDeletedIcon?.image = scaledImage + deletedIconSize = scaledSize + } else { + currentDeletedIcon?.image = deletedImage + } + } else { + currentDeletedIcon = nil + } + if let outgoingStatus = outgoingStatus { switch outgoingStatus { case .Sending: @@ -717,7 +767,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { let layoutAndApply = makeReplyCountLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: countString, font: dateFont, textColor: dateColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: 100.0))) reactionInset += 14.0 + layoutAndApply.0.size.width + 4.0 - if arguments.starsCount != nil { + if arguments.starsCount != nil || arguments.tonAmount != nil { reactionInset += 3.0 } replyCountLayoutAndApply = layoutAndApply @@ -735,6 +785,11 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { countString = "\(starsCount)" } + let layoutAndApply = makeStarsCountLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: countString, font: dateFont, textColor: dateColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: 100.0))) + reactionInset += 14.0 + layoutAndApply.0.size.width + 4.0 + starsCountLayoutAndApply = layoutAndApply + } else if let tonAmount = arguments.tonAmount, tonAmount > 0 { + let countString = formatTonAmountText(tonAmount, dateTimeFormat: arguments.presentationData.dateTimeFormat) let layoutAndApply = makeStarsCountLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: countString, font: dateFont, textColor: dateColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 100.0, height: 100.0))) reactionInset += 14.0 + layoutAndApply.0.size.width + 4.0 starsCountLayoutAndApply = layoutAndApply @@ -744,9 +799,15 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { reactionInset += 13.0 } + // ANTIDELETE: Add deleted icon space + var deletedIconWidth: CGFloat = 0.0 + if arguments.isDeleted { + deletedIconWidth = deletedIconSize.width + 3.0 + } + leftInset += reactionInset - let layoutSize = CGSize(width: leftInset + impressionWidth + date.size.width + statusWidth + backgroundInsets.left + backgroundInsets.right, height: date.size.height + backgroundInsets.top + backgroundInsets.bottom) + let layoutSize = CGSize(width: leftInset + deletedIconWidth + impressionWidth + date.size.width + statusWidth + backgroundInsets.left + backgroundInsets.right, height: date.size.height + backgroundInsets.top + backgroundInsets.bottom) let verticalReactionsInset: CGFloat let verticalInset: CGFloat @@ -1089,7 +1150,22 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { strongSelf.impressionIcon = nil } - animation.animator.updateFrame(layer: strongSelf.dateNode.layer, frame: CGRect(origin: CGPoint(x: leftOffset + leftInset + backgroundInsets.left + impressionWidth, y: backgroundInsets.top + 1.0 + offset + verticalInset), size: date.size), completion: nil) + // ANTIDELETE: Position deleted icon + if let currentDeletedIcon = currentDeletedIcon { + let deletedIconFrame = CGRect(origin: CGPoint(x: leftOffset + leftInset + backgroundInsets.left + impressionWidth, y: backgroundInsets.top + 1.0 + offset + verticalInset + floor((date.size.height - deletedIconSize.height) / 2.0)), size: deletedIconSize) + if currentDeletedIcon.supernode == nil { + strongSelf.deletedIcon = currentDeletedIcon + strongSelf.addSubnode(currentDeletedIcon) + currentDeletedIcon.frame = deletedIconFrame + } else { + animation.animator.updateFrame(layer: currentDeletedIcon.layer, frame: deletedIconFrame, completion: nil) + } + } else if let deletedIcon = strongSelf.deletedIcon { + deletedIcon.removeFromSupernode() + strongSelf.deletedIcon = nil + } + + animation.animator.updateFrame(layer: strongSelf.dateNode.layer, frame: CGRect(origin: CGPoint(x: leftOffset + leftInset + backgroundInsets.left + impressionWidth + deletedIconWidth, y: backgroundInsets.top + 1.0 + offset + verticalInset), size: date.size), completion: nil) if let clockFrameNode = clockFrameNode { let clockPosition = CGPoint(x: leftOffset + backgroundInsets.left + clockPosition.x + reactionInset, y: backgroundInsets.top + clockPosition.y + verticalInset) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift index 730e74f1..b19d721b 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift @@ -455,7 +455,9 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { switch action.action { case let .giftPremium(_, _, daysValue, _, _, giftText, giftEntities): months = max(3, Int32(round(Float(daysValue) / 30.0))) - if months == 12 { + if daysValue < 30 { + title = item.presentationData.strings.Notification_PremiumGift_DaysTitle(daysValue) + } else if months == 12 { title = item.presentationData.strings.Notification_PremiumGift_YearsTitle(1) } else { title = item.presentationData.strings.Notification_PremiumGift_MonthsTitle(months) @@ -513,7 +515,8 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { title = item.presentationData.strings.Notification_StarsGiveaway_Title let starsString = item.presentationData.strings.Notification_StarsGiveaway_Subtitle_Stars(Int32(clamping: count)).replacingOccurrences(of: " ", with: "\u{00A0}") text = item.presentationData.strings.Notification_StarsGiveaway_Subtitle(peerName, starsString).string - case let .giftCode(_, fromGiveaway, unclaimed, channelId, monthsValue, _, _, _, _, giftText, giftEntities): + case let .giftCode(_, fromGiveaway, unclaimed, channelId, daysValue, _, _, _, _, giftText, giftEntities): + let monthsValue = max(3, Int32(round(Float(daysValue) / 30.0))) if channelId == nil { months = monthsValue if months == 12 { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageGiftOfferBubbleContentNode/Sources/ChatMessageGiftOfferBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageGiftOfferBubbleContentNode/Sources/ChatMessageGiftOfferBubbleContentNode.swift index f50e5b49..ea4683c8 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageGiftOfferBubbleContentNode/Sources/ChatMessageGiftOfferBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageGiftOfferBubbleContentNode/Sources/ChatMessageGiftOfferBubbleContentNode.swift @@ -131,7 +131,7 @@ public class ChatMessageGiftOfferBubbleContentNode: ChatMessageBubbleContentNode case .stars: priceString = item.presentationData.strings.Notification_StarGiftOffer_Offer_Stars(Int32(clamping: amount.amount.value)) case .ton: - priceString = "\(amount.amount) TON" + priceString = formatTonAmountText(amount.amount.value, dateTimeFormat: item.presentationData.dateTimeFormat) + " TON" } let peerName = item.message.peers[item.message.id.peerId].flatMap { EnginePeer($0) }?.compactDisplayTitle ?? "" diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift index f5bdcefb..c1ae3540 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift @@ -530,6 +530,7 @@ public class ChatMessageInstantVideoItemNode: ChatMessageItemView, ASGestureReco quote: replyQuote, todoItemId: replyTodoItemId, story: replyStory, + isSummarized: false, parentMessage: item.message, constrainedSize: CGSize(width: max(0, availableWidth), height: CGFloat.greatestFiniteMagnitude), animationCache: item.controllerInteraction.presentationContext.animationCache, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift index 550414b4..7e79f7dc 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveFileNode/Sources/ChatMessageInteractiveFileNode.swift @@ -349,7 +349,7 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode { } private func transcribe() { - guard let arguments = self.arguments, let context = self.context, let message = self.message else { + guard let _ = self.arguments, let context = self.context, let message = self.message else { return } @@ -358,43 +358,7 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode { } let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let premiumConfiguration = PremiumConfiguration.with(appConfiguration: arguments.context.currentAppConfiguration.with { $0 }) - - let transcriptionText = self.forcedAudioTranscriptionText ?? transcribedText(message: message) - if transcriptionText == nil && !arguments.associatedData.alwaysDisplayTranscribeButton.providedByGroupBoost { - if premiumConfiguration.audioTransciptionTrialCount > 0 { - if !arguments.associatedData.isPremium { - if self.presentAudioTranscriptionTooltip(finished: false) { - return - } - } - } else { - guard arguments.associatedData.isPremium else { - if self.hapticFeedback == nil { - self.hapticFeedback = HapticFeedback() - } - self.hapticFeedback?.impact(.medium) - - let tipController = UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_voiceToText", scale: 0.065, colors: [:], title: nil, text: presentationData.strings.Message_AudioTranscription_SubscribeToPremium, customUndoText: presentationData.strings.Message_AudioTranscription_SubscribeToPremiumAction, timeout: nil), elevatedLayout: false, position: .top, animateInAsReplacement: false, action: { action in - if case .undo = action { - var replaceImpl: ((ViewController) -> Void)? - let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .voiceToText, forceDark: false, action: { - let controller = context.sharedContext.makePremiumIntroController(context: context, source: .settings, forceDark: false, dismissed: nil) - replaceImpl?(controller) - }, dismissed: nil) - replaceImpl = { [weak controller] c in - controller?.replace(with: c) - } - arguments.controllerInteraction.navigationController()?.pushViewController(controller, animated: true) - - let _ = ApplicationSpecificNotice.incrementAudioTranscriptionSuggestion(accountManager: context.sharedContext.accountManager).startStandalone() - } - return false }) - arguments.controllerInteraction.presentControllerInCurrent(tipController, nil) - return - } - } - } + // GHOSTGRAM: Premium check removed - local transcription is free! var shouldBeginTranscription = false var shouldExpandNow = false @@ -420,7 +384,8 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode { self.audioTranscriptionState = .inProgress self.requestUpdateLayout(true) - if context.sharedContext.immediateExperimentalUISettings.localTranscription { + // GHOSTGRAM: Always use local transcription (free, private, on-device!) + if true { let appLocale = presentationData.strings.baseLanguageCode let signal: Signal = context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: message.id)) @@ -640,8 +605,6 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode { var audioWaveform: AudioWaveform? var isVoice = false var audioDuration: Int32 = 0 - var isConsumed: Bool? - var consumableContentIcon: UIImage? for attribute in arguments.message.attributes { if let attribute = attribute as? ConsumableContentMessageAttribute { @@ -652,7 +615,6 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode { consumableContentIcon = PresentationResourcesChat.chatBubbleConsumableContentOutgoingIcon(arguments.presentationData.theme.theme) } } - isConsumed = attribute.consumed break } } @@ -771,24 +733,8 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode { if Namespaces.Message.allNonRegular.contains(arguments.message.id.namespace) { displayTranscribe = false } else if arguments.message.id.peerId.namespace != Namespaces.Peer.SecretChat && !isViewOnceMessage && !arguments.presentationData.isPreview { - let premiumConfiguration = PremiumConfiguration.with(appConfiguration: arguments.context.currentAppConfiguration.with { $0 }) - if arguments.associatedData.isPremium { - displayTranscribe = true - } else if premiumConfiguration.audioTransciptionTrialCount > 0 { - if arguments.incoming { - if audioDuration < premiumConfiguration.audioTransciptionTrialMaxDuration { - displayTranscribe = true - } - } - } else if arguments.associatedData.alwaysDisplayTranscribeButton.canBeDisplayed { - if audioDuration >= 60 { - displayTranscribe = true - } else if arguments.incoming && isConsumed == false && arguments.associatedData.alwaysDisplayTranscribeButton.displayForNotConsumed { - displayTranscribe = true - } - } else if arguments.associatedData.alwaysDisplayTranscribeButton.providedByGroupBoost { - displayTranscribe = true - } + // GHOSTGRAM: Always show transcribe button for voice messages + displayTranscribe = true } let transcribedText = forcedAudioTranscriptionText ?? transcribedText(message: arguments.message) @@ -1564,8 +1510,15 @@ public final class ChatMessageInteractiveFileNode: ASDisplayNode { if isTranslating, !rects.isEmpty { if self.shimmeringNodes.isEmpty { + let color: UIColor + let isIncoming = arguments.message.effectivelyIncoming(arguments.context.account.peerId) + if arguments.presentationData.theme.theme.overallDarkAppearance { + color = isIncoming ? arguments.presentationData.theme.theme.chat.message.incoming.primaryTextColor.withAlphaComponent(0.1) : arguments.presentationData.theme.theme.chat.message.outgoing.primaryTextColor.withAlphaComponent(0.1) + } else { + color = isIncoming ? arguments.presentationData.theme.theme.chat.message.incoming.accentTextColor.withAlphaComponent(0.1) : arguments.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor.withAlphaComponent(0.1) + } for rects in rects { - let shimmeringNode = ShimmeringLinkNode(color: arguments.message.effectivelyIncoming(arguments.context.account.peerId) ? arguments.presentationData.theme.theme.chat.message.incoming.secondaryTextColor.withAlphaComponent(0.1) : arguments.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor.withAlphaComponent(0.1)) + let shimmeringNode = ShimmeringLinkNode(color: color) shimmeringNode.updateRects(rects) shimmeringNode.frame = self.bounds shimmeringNode.updateLayout(self.bounds.size) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift index b4605b04..b13d2699 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -404,6 +404,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { quote: replyQuote, todoItemId: replyTodoItemId, story: replyStory, + isSummarized: false, parentMessage: item.message, constrainedSize: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude), animationCache: item.controllerInteraction.presentationContext.animationCache, @@ -1830,45 +1831,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { return } - let presentationData = item.context.sharedContext.currentPresentationData.with { $0 } - let premiumConfiguration = PremiumConfiguration.with(appConfiguration: item.context.currentAppConfiguration.with { $0 }) - - let transcriptionText = transcribedText(message: item.message) - if transcriptionText == nil && !item.associatedData.alwaysDisplayTranscribeButton.providedByGroupBoost { - if premiumConfiguration.audioTransciptionTrialCount > 0 { - if !item.associatedData.isPremium { - if self.presentAudioTranscriptionTooltip(finished: false) { - return - } - } - } else { - guard item.associatedData.isPremium else { - if self.hapticFeedback == nil { - self.hapticFeedback = HapticFeedback() - } - self.hapticFeedback?.impact(.medium) - - let tipController = UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_voiceToText", scale: 0.065, colors: [:], title: nil, text: presentationData.strings.Message_AudioTranscription_SubscribeToPremium, customUndoText: presentationData.strings.Message_AudioTranscription_SubscribeToPremiumAction, timeout: nil), elevatedLayout: false, position: .top, animateInAsReplacement: false, action: { action in - if case .undo = action { - let context = item.context - var replaceImpl: ((ViewController) -> Void)? - let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .voiceToText, forceDark: false, action: { - let controller = context.sharedContext.makePremiumIntroController(context: context, source: .settings, forceDark: false, dismissed: nil) - replaceImpl?(controller) - }, dismissed: nil) - replaceImpl = { [weak controller] c in - controller?.replace(with: c) - } - item.controllerInteraction.navigationController()?.pushViewController(controller, animated: true) - - let _ = ApplicationSpecificNotice.incrementAudioTranscriptionSuggestion(accountManager: item.context.sharedContext.accountManager).startStandalone() - } - return false }) - item.controllerInteraction.presentControllerInCurrent(tipController, nil) - return - } - } - } + // GHOSTGRAM: Premium check removed - local transcription is free! var shouldBeginTranscription = false var shouldExpandNow = false diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageDateHeader.swift b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageDateHeader.swift index 99026ce1..e706cb2c 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageDateHeader.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatMessageDateHeader.swift @@ -645,7 +645,7 @@ public final class ChatMessageDateHeaderNodeImpl: ListViewItemHeaderNode, ChatMe let isRotated = controllerInteraction?.chatIsRotated ?? true - super.init(layerBacked: false, dynamicBounce: true, isRotated: isRotated, seeThrough: false) + super.init(layerBacked: false, isRotated: isRotated, seeThrough: false) if isRotated { self.transform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) @@ -1012,7 +1012,7 @@ public final class ChatMessageAvatarHeaderNodeImpl: ListViewItemHeaderNode, Chat let isRotated = controllerInteraction?.chatIsRotated ?? true - super.init(layerBacked: false, dynamicBounce: true, isRotated: isRotated, seeThrough: false) + super.init(layerBacked: false, isRotated: isRotated, seeThrough: false) if isRotated { self.transform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatReplyCountItem.swift b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatReplyCountItem.swift index 70311fed..4c393049 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatReplyCountItem.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatReplyCountItem.swift @@ -81,7 +81,7 @@ public class ChatReplyCountItemNode: ListViewItemNode { self.backgroundColorNode = ASDisplayNode() - super.init(layerBacked: false, dynamicBounce: true, rotated: true) + super.init(layerBacked: false, rotated: true) self.addSubnode(self.labelNode) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatUnreadItem.swift b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatUnreadItem.swift index 65c004db..63c5f26b 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatUnreadItem.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageItemImpl/Sources/ChatUnreadItem.swift @@ -94,7 +94,7 @@ public class ChatUnreadItemNode: ListViewItemNode { self.activateArea = AccessibilityAreaNode() self.activateArea.accessibilityTraits = .staticText - super.init(layerBacked: false, dynamicBounce: true, rotated: true) + super.init(layerBacked: false, rotated: true) self.addSubnode(self.backgroundNode) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageItemView/Sources/ChatMessageItemView.swift b/submodules/TelegramUI/Components/Chat/ChatMessageItemView/Sources/ChatMessageItemView.swift index 2a3b973f..7d0d161a 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageItemView/Sources/ChatMessageItemView.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageItemView/Sources/ChatMessageItemView.swift @@ -112,7 +112,7 @@ public final class ChatMessageAccessibilityData { if let chatPeer = message.peers[item.message.id.peerId] { let authorName = message.author.flatMap(EnginePeer.init)?.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) - let (_, _, messageText, _, _) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: [EngineMessage(message)], chatPeer: EngineRenderedPeer(peer: EnginePeer(chatPeer)), accountPeerId: item.context.account.peerId) + let (_, _, messageText, _, _, _) = chatListItemStrings(strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, contentSettings: item.context.currentContentSettings.with { $0 }, messages: [EngineMessage(message)], chatPeer: EngineRenderedPeer(peer: EnginePeer(chatPeer)), accountPeerId: item.context.account.peerId) var text = messageText @@ -664,7 +664,7 @@ open class ChatMessageItemView: ListViewItemNode, ChatMessageItemNodeProtocol { public var effectAnimationNodes: [ChatMessageTransitionNode.DecorationItemNode] = [] public required init(rotated: Bool) { - super.init(layerBacked: false, dynamicBounce: true, rotated: rotated) + super.init(layerBacked: false, rotated: rotated) if rotated { self.transform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageNotificationItem/Sources/ChatCallNotificationItem.swift b/submodules/TelegramUI/Components/Chat/ChatMessageNotificationItem/Sources/ChatCallNotificationItem.swift index 087a18dd..0c2b0da4 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageNotificationItem/Sources/ChatCallNotificationItem.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageNotificationItem/Sources/ChatCallNotificationItem.swift @@ -105,7 +105,7 @@ final class ChatCallNotificationItemNode: NotificationItemNode { override public func updateLayout(width: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat { self.validLayout = width - let panelHeight: CGFloat = 66.0 + let panelHeight: CGFloat = 64.0 guard let item = self.item else { return panelHeight @@ -113,19 +113,19 @@ final class ChatCallNotificationItemNode: NotificationItemNode { let presentationData = item.context.sharedContext.currentPresentationData.with { $0 } - let leftInset: CGFloat = 14.0 - let rightInset: CGFloat = 14.0 - let avatarSize: CGFloat = 38.0 + let leftInset: CGFloat = 12.0 + let rightInset: CGFloat = 12.0 + let avatarSize: CGFloat = 40.0 let avatarTextSpacing: CGFloat = 10.0 let buttonSpacing: CGFloat = 14.0 - let titleTextSpacing: CGFloat = 0.0 + let titleTextSpacing: CGFloat = 1.0 let maxTextWidth: CGFloat = width - leftInset - avatarTextSpacing - rightInset - avatarSize * 2.0 - buttonSpacing - avatarTextSpacing let titleSize = self.title.update( transition: .immediate, component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: item.peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), font: Font.semibold(16.0), textColor: presentationData.theme.list.itemPrimaryTextColor)) + text: .plain(NSAttributedString(string: item.peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), font: Font.semibold(15.0), textColor: presentationData.theme.list.itemPrimaryTextColor)) )), environment: {}, containerSize: CGSize(width: maxTextWidth, height: 100.0) @@ -134,7 +134,7 @@ final class ChatCallNotificationItemNode: NotificationItemNode { let textSize = self.text.update( transition: .immediate, component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: item.isVideo ? presentationData.strings.Notification_VideoCallIncoming : presentationData.strings.Notification_CallIncoming, font: Font.regular(13.0), textColor: presentationData.theme.list.itemPrimaryTextColor)) + text: .plain(NSAttributedString(string: item.isVideo ? presentationData.strings.Notification_VideoCallIncoming : presentationData.strings.Notification_CallIncoming, font: Font.regular(15.0), textColor: presentationData.theme.list.itemPrimaryTextColor)) )), environment: {}, containerSize: CGSize(width: maxTextWidth, height: 100.0) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageNotificationItem/Sources/ChatMessageNotificationItem.swift b/submodules/TelegramUI/Components/Chat/ChatMessageNotificationItem/Sources/ChatMessageNotificationItem.swift index bf30319a..466f3f0e 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageNotificationItem/Sources/ChatMessageNotificationItem.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageNotificationItem/Sources/ChatMessageNotificationItem.swift @@ -381,11 +381,11 @@ final class ChatMessageNotificationItemNode: NotificationItemNode { var applyImage: (() -> Void)? if let imageDimensions = imageDimensions { let boundingSize = CGSize(width: 55.0, height: 55.0) - var radius: CGFloat = 6.0 + var radius: CGFloat = 20.0 if isRound { radius = floor(boundingSize.width / 2.0) } - applyImage = imageNodeLayout(TransformImageArguments(corners: ImageCorners(radius: radius), imageSize: imageDimensions.aspectFilled(boundingSize), boundingSize: boundingSize, intrinsicInsets: UIEdgeInsets())) + applyImage = imageNodeLayout(TransformImageArguments(corners: ImageCorners(radius: radius, curve: isRound ? .circular : .continuous), imageSize: imageDimensions.aspectFilled(boundingSize), boundingSize: boundingSize, intrinsicInsets: UIEdgeInsets())) } var updateImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>? @@ -424,16 +424,16 @@ final class ChatMessageNotificationItemNode: NotificationItemNode { let compact = self.compact ?? false let panelHeight: CGFloat = compact ? 64.0 : 74.0 - let imageSize: CGSize = compact ? CGSize(width: 44.0, height: 44.0) : CGSize(width: 54.0, height: 54.0) - let imageSpacing: CGFloat = compact ? 19.0 : 23.0 + let imageSize: CGSize = compact ? CGSize(width: 40.0, height: 40.0) : CGSize(width: 54.0, height: 54.0) + let imageSpacing: CGFloat = compact ? 22.0 : 23.0 let leftInset: CGFloat = imageSize.width + imageSpacing - var rightInset: CGFloat = 8.0 + var rightInset: CGFloat = 10.0 if !self.imageNode.isHidden { - rightInset += imageSize.width + 8.0 + rightInset += imageSize.width + 10.0 } - transition.updateFrame(node: self.avatarNode, frame: CGRect(origin: CGPoint(x: 10.0, y: (panelHeight - imageSize.height) / 2.0), size: imageSize)) + transition.updateFrame(node: self.avatarNode, frame: CGRect(origin: CGPoint(x: 12.0, y: (panelHeight - imageSize.height) / 2.0), size: imageSize)) var titleInset: CGFloat = 0.0 if let image = self.titleIconNode.image { @@ -465,7 +465,7 @@ final class ChatMessageNotificationItemNode: NotificationItemNode { let textSpacing: CGFloat = 1.0 - let titleFrame = CGRect(origin: CGPoint(x: leftInset + titleInset, y: 1.0 + floor((panelHeight - textLayout.size.height - titleLayout.size.height - textSpacing) / 2.0)), size: titleLayout.size) + let titleFrame = CGRect(origin: CGPoint(x: leftInset + titleInset, y: floor((panelHeight - textLayout.size.height - titleLayout.size.height - textSpacing) / 2.0)), size: titleLayout.size) transition.updateFrame(node: self.titleNode, frame: titleFrame) if let image = self.titleIconNode.image { @@ -475,7 +475,7 @@ final class ChatMessageNotificationItemNode: NotificationItemNode { let textFrame = CGRect(origin: CGPoint(x: leftInset, y: titleFrame.maxY + textSpacing), size: textLayout.size) transition.updateFrame(node: self.textNode.textNode, frame: textFrame) - transition.updateFrame(node: self.imageNode, frame: CGRect(origin: CGPoint(x: width - 10.0 - imageSize.width, y: (panelHeight - imageSize.height) / 2.0), size: imageSize)) + transition.updateFrame(node: self.imageNode, frame: CGRect(origin: CGPoint(x: width - 12.0 - imageSize.width, y: (panelHeight - imageSize.height) / 2.0), size: imageSize)) if !textLayout.spoilers.isEmpty, let item = self.item { let presentationData = item.context.sharedContext.currentPresentationData.with({ $0 }) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessagePaymentAlertController/BUILD b/submodules/TelegramUI/Components/Chat/ChatMessagePaymentAlertController/BUILD index ffec32b8..d3f30ddc 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessagePaymentAlertController/BUILD +++ b/submodules/TelegramUI/Components/Chat/ChatMessagePaymentAlertController/BUILD @@ -25,6 +25,8 @@ swift_library( "//submodules/CheckNode", "//submodules/TelegramUIPreferences", "//submodules/TelegramUI/Components/Stars/StarsBalanceOverlayComponent", + "//submodules/TelegramUI/Components/AlertComponent", + "//submodules/TelegramUI/Components/AlertComponent/AlertCheckComponent", "//submodules/Markdown", ], visibility = [ diff --git a/submodules/TelegramUI/Components/Chat/ChatMessagePaymentAlertController/Sources/ChatMessagePaymentAlertController.swift b/submodules/TelegramUI/Components/Chat/ChatMessagePaymentAlertController/Sources/ChatMessagePaymentAlertController.swift index c7ccefad..5adf4309 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessagePaymentAlertController/Sources/ChatMessagePaymentAlertController.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessagePaymentAlertController/Sources/ChatMessagePaymentAlertController.swift @@ -16,309 +16,10 @@ import CheckNode import Markdown import TextFormat import StarsBalanceOverlayComponent +import AlertComponent +import AlertCheckComponent -private let textFont = Font.regular(13.0) -private let boldTextFont = Font.semibold(13.0) - -private func formattedText(_ text: String, fontSize: CGFloat, color: UIColor, linkColor: UIColor, textAlignment: NSTextAlignment = .natural) -> NSAttributedString { - return parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: Font.regular(fontSize), textColor: color), bold: MarkdownAttributeSet(font: Font.semibold(fontSize), textColor: color), link: MarkdownAttributeSet(font: Font.regular(fontSize), textColor: linkColor), linkAttribute: { _ in return (TelegramTextAttributes.URL, "") }), textAlignment: textAlignment) -} - -private final class ChatMessagePaymentAlertContentNode: AlertContentNode, ASGestureRecognizerDelegate { - private let strings: PresentationStrings - private let title: String - private let text: String - private let optionText: String? - private let alignment: TextAlertContentActionLayout - - private let titleNode: ImmediateTextNode - private let textNode: ImmediateTextNode - - private let checkNode: InteractiveCheckNode - private let checkLabelNode: ImmediateTextNode - - private let actionNodesSeparator: ASDisplayNode - private let actionNodes: [TextAlertContentActionNode] - private let actionVerticalSeparators: [ASDisplayNode] - - private var validLayout: CGSize? - - override var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled - } - - var dontAskAgain: Bool = false { - didSet { - self.checkNode.setSelected(self.dontAskAgain, animated: true) - - } - } - - var openTerms: () -> Void = {} - - init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, title: String, text: String, optionText: String?, actions: [TextAlertAction], alignment: TextAlertContentActionLayout) { - self.strings = strings - self.title = title - self.text = text - self.optionText = optionText - self.alignment = alignment - - self.titleNode = ImmediateTextNode() - self.titleNode.displaysAsynchronously = false - self.titleNode.maximumNumberOfLines = 1 - self.titleNode.textAlignment = .center - - self.textNode = ImmediateTextNode() - self.textNode.maximumNumberOfLines = 0 - self.textNode.displaysAsynchronously = false - self.textNode.lineSpacing = 0.1 - self.textNode.textAlignment = .center - - self.checkNode = InteractiveCheckNode(theme: CheckNodeTheme(backgroundColor: theme.accentColor, strokeColor: theme.contrastColor, borderColor: theme.controlBorderColor, overlayBorder: false, hasInset: false, hasShadow: false)) - self.checkLabelNode = ImmediateTextNode() - self.checkLabelNode.maximumNumberOfLines = 4 - - self.actionNodesSeparator = ASDisplayNode() - self.actionNodesSeparator.isLayerBacked = true - - self.actionNodes = actions.map { action -> TextAlertContentActionNode in - return TextAlertContentActionNode(theme: theme, action: action) - } - - var actionVerticalSeparators: [ASDisplayNode] = [] - if actions.count > 1 { - for _ in 0 ..< actions.count - 1 { - let separatorNode = ASDisplayNode() - separatorNode.isLayerBacked = true - actionVerticalSeparators.append(separatorNode) - } - } - self.actionVerticalSeparators = actionVerticalSeparators - - super.init() - - self.addSubnode(self.titleNode) - self.addSubnode(self.textNode) - - if let _ = optionText { - self.addSubnode(self.checkNode) - self.addSubnode(self.checkLabelNode) - } - - self.addSubnode(self.actionNodesSeparator) - - for actionNode in self.actionNodes { - self.addSubnode(actionNode) - } - - for separatorNode in self.actionVerticalSeparators { - self.addSubnode(separatorNode) - } - - self.checkNode.valueChanged = { [weak self] value in - if let strongSelf = self { - strongSelf.dontAskAgain = !strongSelf.dontAskAgain - } - } - - self.checkLabelNode.highlightAttributeAction = { attributes in - if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] { - return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL) - } else { - return nil - } - } - self.checkLabelNode.tapAttributeAction = { [weak self] attributes, _ in - if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] { - self?.openTerms() - } - } - - self.updateTheme(theme) - } - - override func didLoad() { - super.didLoad() - - let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.acceptTap(_:))) - tapGesture.delegate = self.wrappedGestureRecognizerDelegate - self.view.addGestureRecognizer(tapGesture) - } - - override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { - let location = gestureRecognizer.location(in: self.checkLabelNode.view) - if self.checkLabelNode.bounds.contains(location) { - return true - } - return false - } - - override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - if !self.bounds.contains(point) { - return nil - } - - if let (_, attributes) = self.checkLabelNode.attributesAtPoint(self.view.convert(point, to: self.checkLabelNode.view)) { - if attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] == nil { - return self.view - } - } - - return super.hitTest(point, with: event) - } - - @objc private func acceptTap(_ gestureRecognizer: UITapGestureRecognizer) { - self.dontAskAgain = !self.dontAskAgain - } - - override func updateTheme(_ theme: AlertControllerTheme) { - self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.semibold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center) - self.textNode.attributedText = formattedText(self.text, fontSize: 13.0, color: theme.primaryColor, linkColor: theme.accentColor, textAlignment: .center) - - self.checkLabelNode.attributedText = parseMarkdownIntoAttributedString( - self.optionText ?? "", - attributes: MarkdownAttributes( - body: MarkdownAttributeSet(font: textFont, textColor: theme.primaryColor), - bold: MarkdownAttributeSet(font: boldTextFont, textColor: theme.primaryColor), - link: MarkdownAttributeSet(font: textFont, textColor: theme.primaryColor), - linkAttribute: { _ in - return nil - } - ) - ) - self.actionNodesSeparator.backgroundColor = theme.separatorColor - for actionNode in self.actionNodes { - actionNode.updateTheme(theme) - } - for separatorNode in self.actionVerticalSeparators { - separatorNode.backgroundColor = theme.separatorColor - } - - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) - } - } - - override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - var size = size - size.width = min(size.width, 270.0) - - self.validLayout = size - - var origin: CGPoint = CGPoint(x: 0.0, y: 17.0) - - let titleSize = self.titleNode.updateLayout(CGSize(width: size.width - 32.0, height: size.height)) - transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize)) - origin.y += titleSize.height + 4.0 - - var entriesHeight: CGFloat = 0.0 - - let textSize = self.textNode.updateLayout(CGSize(width: size.width - 32.0, height: size.height)) - transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize)) - origin.y += textSize.height - - if self.checkLabelNode.supernode != nil { - origin.y += 21.0 - entriesHeight += 21.0 - - let checkSize = CGSize(width: 22.0, height: 22.0) - let condensedSize = CGSize(width: size.width - 76.0, height: size.height) - - let spacing: CGFloat = 12.0 - let acceptTermsSize = self.checkLabelNode.updateLayout(condensedSize) - let acceptTermsTotalWidth = checkSize.width + spacing + acceptTermsSize.width - let acceptTermsOriginX = floorToScreenPixels((size.width - acceptTermsTotalWidth) / 2.0) - - transition.updateFrame(node: self.checkNode, frame: CGRect(origin: CGPoint(x: acceptTermsOriginX, y: origin.y - 3.0), size: checkSize)) - transition.updateFrame(node: self.checkLabelNode, frame: CGRect(origin: CGPoint(x: acceptTermsOriginX + checkSize.width + spacing, y: origin.y), size: acceptTermsSize)) - origin.y += acceptTermsSize.height - entriesHeight += acceptTermsSize.height - origin.y += 21.0 - } - - let actionButtonHeight: CGFloat = 44.0 - var minActionsWidth: CGFloat = 0.0 - let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) - let actionTitleInsets: CGFloat = 8.0 - - var effectiveActionLayout = self.alignment - for actionNode in self.actionNodes { - let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) - if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 { - effectiveActionLayout = .vertical - } - switch effectiveActionLayout { - case .horizontal: - minActionsWidth += actionTitleSize.width + actionTitleInsets - case .vertical: - minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) - } - } - - let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0) - - let contentWidth = max(size.width, minActionsWidth) - - var actionsHeight: CGFloat = 0.0 - switch effectiveActionLayout { - case .horizontal: - actionsHeight = actionButtonHeight - case .vertical: - actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) - } - - let resultSize = CGSize(width: contentWidth, height: titleSize.height + textSize.height + entriesHeight + actionsHeight + 3.0 + insets.top + insets.bottom) - - transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - - var actionOffset: CGFloat = 0.0 - let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) - var separatorIndex = -1 - var nodeIndex = 0 - for actionNode in self.actionNodes { - if separatorIndex >= 0 { - let separatorNode = self.actionVerticalSeparators[separatorIndex] - switch effectiveActionLayout { - case .horizontal: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) - case .vertical: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - } - } - separatorIndex += 1 - - let currentActionWidth: CGFloat - switch effectiveActionLayout { - case .horizontal: - if nodeIndex == self.actionNodes.count - 1 { - currentActionWidth = resultSize.width - actionOffset - } else { - currentActionWidth = actionWidth - } - case .vertical: - currentActionWidth = resultSize.width - } - - let actionNodeFrame: CGRect - switch effectiveActionLayout { - case .horizontal: - actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += currentActionWidth - case .vertical: - actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += actionButtonHeight - } - - transition.updateFrame(node: actionNode, frame: actionNodeFrame) - - nodeIndex += 1 - } - - return resultSize - } -} - -public class ChatMessagePaymentAlertController: AlertController { +public class ChatMessagePaymentAlertController: AlertScreen { private let context: AccountContext? private let presentationData: PresentationData private weak var parentNavigationController: NavigationController? @@ -327,29 +28,26 @@ public class ChatMessagePaymentAlertController: AlertController { private let animateBalanceOverlay: Bool private var didUpdateCurrency = false - public var currency: CurrencyAmount.Currency { - didSet { - self.didUpdateCurrency = true - if let layout = self.validLayout { - self.containerLayoutUpdated(layout, transition: .animated(duration: 0.25, curve: .easeInOut)) - } - } - } - + + private var initialCurrency: CurrencyAmount.Currency? + public var currency: CurrencyAmount.Currency? + private var currencyDisposable: Disposable? + private let balance = ComponentView() private var didAppear = false - - private var validLayout: ContainerViewLayout? - + public init( context: AccountContext?, presentationData: PresentationData, - contentNode: AlertContentNode, + updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, + configuration: Configuration = AlertScreen.Configuration(), + contentSignal: Signal<[AnyComponentWithIdentity], NoError>, + actionsSignal: Signal<[AlertScreen.Action], NoError>, navigationController: NavigationController?, chatPeerId: EnginePeer.Id, showBalance: Bool = true, - currency: CurrencyAmount.Currency = .stars, + currencySignal: Signal = .single(.stars), animateBalanceOverlay: Bool = true ) { self.context = context @@ -357,31 +55,84 @@ public class ChatMessagePaymentAlertController: AlertController { self.parentNavigationController = navigationController self.chatPeerId = chatPeerId self.showBalance = showBalance - self.currency = currency self.animateBalanceOverlay = animateBalanceOverlay - super.init(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode) + var effectiveUpdatedPresentationData: (initial: PresentationData, signal: Signal) + if let updatedPresentationData { + effectiveUpdatedPresentationData = updatedPresentationData + } else { + effectiveUpdatedPresentationData = (initial: presentationData, signal: .single(presentationData)) + } + + super.init( + configuration: configuration, + contentSignal: contentSignal, + actionsSignal: actionsSignal, + updatedPresentationData: effectiveUpdatedPresentationData + ) - self.willDismiss = { [weak self] in + self.currencyDisposable = (currencySignal + |> distinctUntilChanged + |> deliverOnMainQueue).start(next: { [weak self] currency in guard let self else { return } - self.animateOut() - } + if self.currency == nil { + self.initialCurrency = currency + } + self.currency = currency + if let layout = self.validLayout { + self.containerLayoutUpdated(layout, transition: .animated(duration: 0.25, curve: .easeInOut)) + } + }) + } + + public convenience init( + context: AccountContext?, + presentationData: PresentationData, + updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, + configuration: Configuration = AlertScreen.Configuration(), + content: [AnyComponentWithIdentity], + actions: [AlertScreen.Action], + navigationController: NavigationController?, + chatPeerId: EnginePeer.Id, + showBalance: Bool = true, + currency: CurrencyAmount.Currency = .stars, + animateBalanceOverlay: Bool = true + ) { + self.init( + context: context, + presentationData: presentationData, + updatedPresentationData: updatedPresentationData, + configuration: configuration, + contentSignal: .single(content), + actionsSignal: .single(actions), + navigationController: navigationController, + chatPeerId: chatPeerId, + showBalance: showBalance, + currencySignal: .single(currency), + animateBalanceOverlay: animateBalanceOverlay + ) } required public init(coder aDecoder: NSCoder) { preconditionFailure() } + override public func dismiss(completion: (() -> Void)? = nil) { + super.dismiss(completion: completion) + + self.animateOut() + } + private func animateOut() { if !self.animateBalanceOverlay { - if self.currency == .ton && self.didUpdateCurrency { + if case .ton = self.currency, let initialCurrency, initialCurrency != self.currency { self.currency = .stars + if let layout = self.validLayout { + self.containerLayoutUpdated(layout, transition: .animated(duration: 0.25, curve: .easeInOut)) + } } - Queue.mainQueue().after(0.39, { - - }) } else { if let view = self.balance.view { view.layer.animateScale(from: 1.0, to: 0.8, duration: 0.4, removeOnCompletion: false) @@ -389,18 +140,10 @@ public class ChatMessagePaymentAlertController: AlertController { } } } - - public override func dismissAnimated() { - super.dismissAnimated() - self.animateOut() - } - public override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { super.containerLayoutUpdated(layout, transition: transition) - - self.validLayout = layout - + if !self.didAppear { self.didAppear = true if !layout.metrics.isTablet && layout.size.width > layout.size.height { @@ -410,7 +153,7 @@ public class ChatMessagePaymentAlertController: AlertController { } } - if let context = self.context, let _ = self.parentNavigationController, self.showBalance { + if let context = self.context, let _ = self.parentNavigationController, self.showBalance, let currency = self.currency { let insets = layout.insets(options: .statusBar) var balanceTransition = ComponentTransition(transition) if self.balance.view == nil { @@ -424,12 +167,12 @@ public class ChatMessagePaymentAlertController: AlertController { context: context, peerId: self.chatPeerId.namespace == Namespaces.Peer.CloudChannel ? self.chatPeerId : context.account.peerId, theme: self.presentationData.theme, - currency: self.currency, + currency: currency, action: { [weak self] in - guard let self, let starsContext = context.starsContext, let navigationController = self.parentNavigationController else { + guard let self, let starsContext = context.starsContext, let navigationController = self.parentNavigationController, let currency = self.currency else { return } - switch self.currency { + switch currency { case .stars: let _ = (context.engine.payments.starsTopUpOptions() |> take(1) @@ -452,7 +195,7 @@ public class ChatMessagePaymentAlertController: AlertController { } context.sharedContext.applicationBindings.openUrl(fragmentUrl) } - self.dismissAnimated() + self.dismiss(completion: nil) } ) ), @@ -486,57 +229,66 @@ public func chatMessagePaymentAlertController( hasCheck: Bool = true, navigationController: NavigationController?, completion: @escaping (Bool) -> Void -) -> AlertController { - let theme = defaultDarkColorPresentationTheme - let presentationData = updatedPresentationData?.initial ?? presentationData +) -> ViewController { let strings = presentationData.strings - var completionImpl: (() -> Void)? - var dismissImpl: (() -> Void)? - - let title = presentationData.strings.Chat_PaidMessage_Confirm_Title - let actionTitle = presentationData.strings.Chat_PaidMessage_Confirm_PayForMessage(count) - let messagesString = presentationData.strings.Chat_PaidMessage_Confirm_Text_Messages(count) - - let actions: [TextAlertAction] = [TextAlertAction(type: .defaultAction, title: actionTitle, action: { - completionImpl?() - dismissImpl?() - }), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - dismissImpl?() - })] - + let messagesString = strings.Chat_PaidMessage_Confirm_Text_Messages(count) let text: String if peers.count == 1, let peer = peers.first { - let amountString = presentationData.strings.Chat_PaidMessage_Confirm_Text_Stars(Int32(clamping: amount.value)) - let totalString = presentationData.strings.Chat_PaidMessage_Confirm_Text_Stars(Int32(clamping: amount.value * Int64(count))) + let amountString = strings.Chat_PaidMessage_Confirm_Text_Stars(Int32(clamping: amount.value)) + let totalString = strings.Chat_PaidMessage_Confirm_Text_Stars(Int32(clamping: amount.value * Int64(count))) if case let .channel(channel) = peer.chatOrMonoforumMainPeer, case .broadcast = channel.info { - text = presentationData.strings.Chat_PaidMessage_Confirm_SingleComment_Text(EnginePeer(channel).compactDisplayTitle, amountString, totalString, messagesString).string + text = strings.Chat_PaidMessage_Confirm_SingleComment_Text(EnginePeer(channel).compactDisplayTitle, amountString, totalString, messagesString).string } else { - text = presentationData.strings.Chat_PaidMessage_Confirm_Single_Text(peer.chatOrMonoforumMainPeer?.compactDisplayTitle ?? " ", amountString, totalString, messagesString).string + text = strings.Chat_PaidMessage_Confirm_Single_Text(peer.chatOrMonoforumMainPeer?.compactDisplayTitle ?? " ", amountString, totalString, messagesString).string } } else { let amount = totalAmount ?? amount - let usersString = presentationData.strings.Chat_PaidMessage_Confirm_Text_Users(Int32(peers.count)) - let totalString = presentationData.strings.Chat_PaidMessage_Confirm_Text_Stars(Int32(clamping: amount.value * Int64(count))) - text = presentationData.strings.Chat_PaidMessage_Confirm_Multiple_Text(usersString, totalString, messagesString).string + let usersString = strings.Chat_PaidMessage_Confirm_Text_Users(Int32(peers.count)) + let totalString = strings.Chat_PaidMessage_Confirm_Text_Stars(Int32(clamping: amount.value * Int64(count))) + text = strings.Chat_PaidMessage_Confirm_Multiple_Text(usersString, totalString, messagesString).string + } + + let checkState = AlertCheckComponent.ExternalState() + + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: strings.Chat_PaidMessage_Confirm_Title) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(text)) + ) + )) + if hasCheck { + content.append(AnyComponentWithIdentity( + id: "check", + component: AnyComponent( + AlertCheckComponent(title: strings.Chat_PaidMessage_Confirm_DontAskAgain, initialValue: false, externalState: checkState) + ) + )) } - let optionText = hasCheck ? presentationData.strings.Chat_PaidMessage_Confirm_DontAskAgain : nil - - let contentNode = ChatMessagePaymentAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: theme, strings: strings, title: title, text: text, optionText: optionText, actions: actions, alignment: .vertical) - - completionImpl = { [weak contentNode] in - guard let contentNode else { - return - } - completion(contentNode.dontAskAgain) - } - - let controller = ChatMessagePaymentAlertController(context: context, presentationData: presentationData, contentNode: contentNode, navigationController: navigationController, chatPeerId: context?.account.peerId ?? peers[0].peerId) - dismissImpl = { [weak controller] in - controller?.dismissAnimated() - } - return controller + let alertController = ChatMessagePaymentAlertController( + context: context, + presentationData: presentationData, + updatedPresentationData: updatedPresentationData, + configuration: AlertScreen.Configuration(actionAlignment: .vertical, allowInputInset: true), + content: content, + actions: [ + .init(title: strings.Chat_PaidMessage_Confirm_PayForMessage(count), type: .default, action: { + completion(checkState.value) + }), + .init(title: strings.Common_Cancel) + ], + navigationController: navigationController, + chatPeerId: context?.account.peerId ?? peers[0].peerId + ) + return alertController } public func chatMessageRemovePaymentAlertController( @@ -548,47 +300,55 @@ public func chatMessageRemovePaymentAlertController( amount: StarsAmount?, navigationController: NavigationController?, completion: @escaping (Bool) -> Void -) -> AlertController { - let theme = defaultDarkColorPresentationTheme - let presentationData = updatedPresentationData?.initial ?? presentationData +) -> ViewController { let strings = presentationData.strings - var completionImpl: (() -> Void)? - var dismissImpl: (() -> Void)? - - let actions: [TextAlertAction] = [ - TextAlertAction(type: .genericAction, title: strings.Common_Cancel, action: { - dismissImpl?() - }), - TextAlertAction(type: .defaultAction, title: strings.Chat_PaidMessage_RemoveFee_Yes, action: { - completionImpl?() - dismissImpl?() - }) - ] - - let title = strings.Chat_PaidMessage_RemoveFee_Title - let text: String - if let context, chatPeer.id != context.account.peerId { + if case .user = chatPeer { + text = strings.Chat_PaidMessage_RemoveFee_Text(peer.compactDisplayTitle).string + } else if let context, chatPeer.id != context.account.peerId { text = strings.Channel_RemoveFeeAlert_Text(peer.compactDisplayTitle).string } else { text = strings.Chat_PaidMessage_RemoveFee_Text(peer.compactDisplayTitle).string } + + let checkState = AlertCheckComponent.ExternalState() - let optionText = amount.flatMap { strings.Chat_PaidMessage_RemoveFee_Refund(strings.Chat_PaidMessage_RemoveFee_Refund_Stars(Int32(clamping: $0.value))).string } - - let contentNode = ChatMessagePaymentAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: theme, strings: strings, title: title, text: text, optionText: optionText, actions: actions, alignment: .horizontal) - - completionImpl = { [weak contentNode] in - guard let contentNode else { - return - } - completion(contentNode.dontAskAgain) + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: strings.Chat_PaidMessage_RemoveFee_Title) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(text)) + ) + )) + if let amount { + content.append(AnyComponentWithIdentity( + id: "check", + component: AnyComponent( + AlertCheckComponent(title: strings.Chat_PaidMessage_RemoveFee_Refund(strings.Chat_PaidMessage_RemoveFee_Refund_Stars(Int32(clamping: amount.value))).string, initialValue: false, externalState: checkState) + ) + )) } - let controller = ChatMessagePaymentAlertController(context: context, presentationData: presentationData, contentNode: contentNode, navigationController: navigationController, chatPeerId: chatPeer.id) - dismissImpl = { [weak controller] in - controller?.dismissAnimated() - } - return controller + let alertController = ChatMessagePaymentAlertController( + context: context, + presentationData: presentationData, + updatedPresentationData: updatedPresentationData, + content: content, + actions: [ + .init(title: strings.Common_Cancel), + .init(title: strings.Chat_PaidMessage_RemoveFee_Yes, type: .default, action: { + completion(checkState.value) + }) + ], + navigationController: navigationController, + chatPeerId: chatPeer.id + ) + return alertController } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift index d1dfa76b..11ea5ed6 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift @@ -1678,8 +1678,15 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { if isTranslating, !rects.isEmpty { if self.shimmeringNodes.isEmpty { + let color: UIColor + let isIncoming = item.message.effectivelyIncoming(item.context.account.peerId) + if item.presentationData.theme.theme.overallDarkAppearance { + color = isIncoming ? item.presentationData.theme.theme.chat.message.incoming.primaryTextColor.withAlphaComponent(0.1) : item.presentationData.theme.theme.chat.message.outgoing.primaryTextColor.withAlphaComponent(0.1) + } else { + color = isIncoming ? item.presentationData.theme.theme.chat.message.incoming.accentTextColor.withAlphaComponent(0.1) : item.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor.withAlphaComponent(0.1) + } for rects in rects { - let shimmeringNode = ShimmeringLinkNode(color: item.message.effectivelyIncoming(item.context.account.peerId) ? item.presentationData.theme.theme.chat.message.incoming.secondaryTextColor.withAlphaComponent(0.1) : item.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor.withAlphaComponent(0.1)) + let shimmeringNode = ShimmeringLinkNode(color: color) shimmeringNode.updateRects(rects) shimmeringNode.frame = self.bounds shimmeringNode.updateLayout(self.bounds.size) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageReplyInfoNode/Sources/ChatMessageReplyInfoNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageReplyInfoNode/Sources/ChatMessageReplyInfoNode.swift index 9ebeccca..cde7b258 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageReplyInfoNode/Sources/ChatMessageReplyInfoNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageReplyInfoNode/Sources/ChatMessageReplyInfoNode.swift @@ -85,6 +85,7 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { public let quote: (quote: EngineMessageReplyQuote, isQuote: Bool)? public let todoItemId: Int32? public let story: StoryId? + public let isSummarized: Bool public let parentMessage: Message public let constrainedSize: CGSize public let animationCache: AnimationCache? @@ -101,6 +102,7 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { quote: (quote: EngineMessageReplyQuote, isQuote: Bool)?, todoItemId: Int32?, story: StoryId?, + isSummarized: Bool, parentMessage: Message, constrainedSize: CGSize, animationCache: AnimationCache?, @@ -116,6 +118,7 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { self.quote = quote self.todoItemId = todoItemId self.story = story + self.isSummarized = isSummarized self.parentMessage = parentMessage self.constrainedSize = constrainedSize self.animationCache = animationCache @@ -133,6 +136,7 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { } private let backgroundView: MessageInlineBlockBackgroundView + private var starsView: StarsView? private var quoteIconView: UIImageView? private let contentNode: ASDisplayNode private var titleNode: TextNode? @@ -206,7 +210,6 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { var secondaryColor: UIColor? var tertiaryColor: UIColor? - var authorNameColor: UIColor? var dashSecondaryColor: UIColor? var dashTertiaryColor: UIColor? @@ -239,6 +242,10 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { break } + if arguments.isSummarized { + authorNameColor = nil + } + switch arguments.type { case let .bubble(incoming): titleColor = incoming ? (authorNameColor ?? arguments.presentationData.theme.theme.chat.message.incoming.accentTextColor) : arguments.presentationData.theme.theme.chat.message.outgoing.accentTextColor @@ -437,7 +444,7 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { } var textLeftInset: CGFloat = 0.0 - let messageText: NSAttributedString + var messageText: NSAttributedString var todoItemCompleted: Bool? if let todoItemId = arguments.todoItemId, let todo = arguments.message?.media.first(where: { $0 is TelegramMediaTodo }) as? TelegramMediaTodo, let todoItem = todo.items.first(where: { $0.id == todoItemId }) { messageText = stringWithAppliedEntities(todoItem.text, entities: todoItem.entities, baseColor: textColor, linkColor: textColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false, message: nil) @@ -607,6 +614,11 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { } adjustedConstrainedTextSize.width -= textLeftInset + if arguments.isSummarized { + titleString = NSAttributedString(string: arguments.presentationData.strings.Conversation_Summary_Title, font: titleFont, textColor: titleColor) + messageText = NSAttributedString(string: arguments.presentationData.strings.Conversation_Summary_Text, font: textFont, textColor: titleColor) + } + let (titleLayout, titleApply) = titleNodeLayout(TextNodeLayoutArguments(attributedString: titleString, backgroundColor: nil, maximumNumberOfLines: maxTitleNumberOfLines, truncationType: .end, constrainedSize: CGSize(width: contrainedTextSize.width - additionalTitleWidth, height: contrainedTextSize.height), alignment: .natural, cutout: nil, insets: textInsets)) if isExpiredStory || isStory { contrainedTextSize.width -= 26.0 @@ -687,6 +699,11 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { node = ChatMessageReplyInfoNode() } + var animation = animation + if node.titleNode == nil { + animation = .None + } + node.previousMediaReference = updatedMediaReference //node.textNode?.textNode.displaysAsynchronously = !arguments.presentationData.isPreview @@ -925,6 +942,22 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { giftEmojiLayer.removeFromSuperlayer() } + if arguments.isSummarized { + let starsView: StarsView + if let current = node.starsView { + starsView = current + } else { + starsView = StarsView() + node.starsView = starsView + node.contentNode.view.insertSubview(starsView, at: 1) + } + starsView.frame = CGRect(origin: CGPoint(), size: backgroundFrame.size) + starsView.update(size: backgroundFrame.size, color: mainColor) + } else if let starsView = node.starsView { + node.starsView = nil + starsView.removeFromSuperview() + } + node.contentNode.frame = CGRect(origin: CGPoint(), size: size) return node @@ -1065,3 +1098,96 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { return nil } } + +private final class StarsView: UIView { + private let staticEmitterLayer = CAEmitterLayer() + + private var currentColor: UIColor? + + override init(frame: CGRect) { + super.init(frame: frame) + + self.clipsToBounds = true + + self.layer.addSublayer(self.staticEmitterLayer) + } + + required init(coder: NSCoder) { + preconditionFailure() + } + + private func setupEmitter(size: CGSize) { + guard let currentColor = self.currentColor else { + return + } + let color = currentColor + + self.staticEmitterLayer.emitterShape = .rectangle + self.staticEmitterLayer.emitterSize = size + self.staticEmitterLayer.emitterMode = .surface + self.layer.addSublayer(self.staticEmitterLayer) + + let staticEmitter = CAEmitterCell() + staticEmitter.name = "emitter" + staticEmitter.contents = UIImage(bundleImageName: "Premium/Stars/Particle")?.cgImage + staticEmitter.birthRate = 20.0 + staticEmitter.lifetime = 3.2 + staticEmitter.velocity = 18.0 + staticEmitter.velocityRange = 3 + staticEmitter.scale = 0.1 + staticEmitter.scaleRange = 0.08 + staticEmitter.emissionRange = .pi * 2.0 + staticEmitter.setValue(3.0, forKey: "mass") + staticEmitter.setValue(2.0, forKey: "massRange") + + let staticColors: [Any] = [ + color.withAlphaComponent(0.0).cgColor, + color.cgColor, + color.withAlphaComponent(0.0).cgColor, + color.withAlphaComponent(0.0).cgColor, + color.cgColor, + color.withAlphaComponent(0.0).cgColor + ] + let staticColorBehavior = CAEmitterCell.createEmitterBehavior(type: "colorOverLife") + staticColorBehavior.setValue(staticColors, forKey: "colors") + staticEmitter.setValue([staticColorBehavior], forKey: "emitterBehaviors") + + let attractor = CAEmitterCell.createEmitterBehavior(type: "simpleAttractor") + attractor.setValue("attractor", forKey: "name") + attractor.setValue(20, forKey: "falloff") + attractor.setValue(35, forKey: "radius") + self.staticEmitterLayer.setValue([attractor], forKey: "emitterBehaviors") + self.staticEmitterLayer.setValue(4.0, forKeyPath: "emitterBehaviors.attractor.stiffness") + self.staticEmitterLayer.setValue(false, forKeyPath: "emitterBehaviors.attractor.enabled") + + self.staticEmitterLayer.emitterCells = [staticEmitter] + } + + func update(size: CGSize, color: UIColor) { + if self.staticEmitterLayer.emitterCells == nil { + self.currentColor = color + self.setupEmitter(size: size) + } else if self.currentColor != color { + self.currentColor = color + + let staticColors: [Any] = [ + UIColor.white.withAlphaComponent(0.0).cgColor, + UIColor.white.withAlphaComponent(0.35).cgColor, + color.cgColor, + color.cgColor, + color.withAlphaComponent(0.0).cgColor + ] + let staticColorBehavior = CAEmitterCell.createEmitterBehavior(type: "colorOverLife") + staticColorBehavior.setValue(staticColors, forKey: "colors") + + for cell in self.staticEmitterLayer.emitterCells ?? [] { + cell.setValue([staticColorBehavior], forKey: "emitterBehaviors") + } + } + + let emitterPosition = CGPoint(x: size.width * 0.5, y: size.height * 0.5) + self.staticEmitterLayer.frame = CGRect(origin: .zero, size: size) + self.staticEmitterLayer.emitterPosition = emitterPosition + self.staticEmitterLayer.setValue(emitterPosition, forKeyPath: "emitterBehaviors.attractor.position") + } +} diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageSelectionInputPanelNode/Sources/ChatMessageSelectionInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageSelectionInputPanelNode/Sources/ChatMessageSelectionInputPanelNode.swift index 61fa256f..bf7b210c 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageSelectionInputPanelNode/Sources/ChatMessageSelectionInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageSelectionInputPanelNode/Sources/ChatMessageSelectionInputPanelNode.swift @@ -121,7 +121,6 @@ private final class GlassButtonView: UIView { override init(frame: CGRect) { self.backgroundView = GlassBackgroundView() - self.backgroundView.isUserInteractionEnabled = false self.iconView = GlassBackgroundView.ContentImageView() self.backgroundView.contentView.addSubview(self.iconView) @@ -460,11 +459,13 @@ public final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode { override public func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, maxOverlayHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, isMediaInputExpanded: Bool) -> CGFloat { self.validLayout = (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, maxOverlayHeight, metrics, isSecondary, isMediaInputExpanded) - var leftInset = leftInset - leftInset += 16.0 + var leftInset = leftInset + 8.0 + var rightInset = rightInset + 8.0 - var rightInset = rightInset - rightInset += 16.0 + if bottomInset <= 32.0 { + leftInset += 18.0 + rightInset += 18.0 + } let panelHeight = defaultHeight(metrics: metrics) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageShareButton/BUILD b/submodules/TelegramUI/Components/Chat/ChatMessageShareButton/BUILD index 6d03e682..70a8cb94 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageShareButton/BUILD +++ b/submodules/TelegramUI/Components/Chat/ChatMessageShareButton/BUILD @@ -20,6 +20,7 @@ swift_library( "//submodules/WallpaperBackgroundNode", "//submodules/TelegramUI/Components/Chat/ChatMessageItemCommon", "//submodules/ContextUI", + "//submodules/Components/HierarchyTrackingLayer", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageShareButton/Sources/ChatMessageShareButton.swift b/submodules/TelegramUI/Components/Chat/ChatMessageShareButton/Sources/ChatMessageShareButton.swift index 502bd18b..7e38d33e 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageShareButton/Sources/ChatMessageShareButton.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageShareButton/Sources/ChatMessageShareButton.swift @@ -10,6 +10,7 @@ import Postbox import WallpaperBackgroundNode import ChatMessageItemCommon import ContextUI +import HierarchyTrackingLayer public class ChatMessageShareButton: ASDisplayNode { private let referenceNode: ContextReferenceContentNode @@ -21,15 +22,18 @@ public class ChatMessageShareButton: ASDisplayNode { private let topButton: HighlightTrackingButtonNode private let topIconNode: ASImageNode private var topIconOffset = CGPoint() - + private var bottomButton: HighlightTrackingButtonNode? private var bottomIconNode: ASImageNode? + private var starsView: StarsView? + private var separatorNode: ASDisplayNode? private var theme: PresentationTheme? private var isReplies: Bool = false private var hasMore: Bool = false + private var isExpand: Bool = false private var textNode: ImmediateTextNode? @@ -103,7 +107,7 @@ public class ChatMessageShareButton: ASDisplayNode { self.morePressed?() } - public func update(presentationData: ChatPresentationData, controllerInteraction: ChatControllerInteraction, chatLocation: ChatLocation, subject: ChatControllerSubject?, message: Message, account: Account, disableComments: Bool = false) -> CGSize { + public func update(presentationData: ChatPresentationData, controllerInteraction: ChatControllerInteraction, chatLocation: ChatLocation, subject: ChatControllerSubject?, message: Message, account: Account, disableComments: Bool = false, isSummarize: Bool = false) -> CGSize { var isReplies = false var isNavigate = false var replyCount = 0 @@ -134,15 +138,27 @@ public class ChatMessageShareButton: ASDisplayNode { hasMore = true } - if self.theme !== presentationData.theme.theme || self.isReplies != isReplies || self.hasMore != hasMore { + var isExpand = false + if controllerInteraction.summarizedMessageIds.contains(message.id) { + isExpand = true + } + + if self.theme !== presentationData.theme.theme || self.isReplies != isReplies || self.hasMore != hasMore || self.isExpand != isExpand { self.theme = presentationData.theme.theme self.isReplies = isReplies self.hasMore = hasMore + self.isExpand = isExpand var updatedIconImage: UIImage? var updatedBottomIconImage: UIImage? var updatedIconOffset = CGPoint() - if let _ = message.adAttribute { + if isSummarize { + if isExpand { + updatedIconImage = PresentationResourcesChat.chatFreeExpandButtonIcon(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) + } else { + updatedIconImage = PresentationResourcesChat.chatFreeCollapseButtonIcon(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) + } + } else if let _ = message.adAttribute { updatedIconImage = PresentationResourcesChat.chatFreeCloseButtonIcon(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) updatedIconOffset = CGPoint(x: UIScreenPixel, y: UIScreenPixel) @@ -167,6 +183,17 @@ public class ChatMessageShareButton: ASDisplayNode { updatedIconImage = PresentationResourcesChat.chatFreeShareButtonIcon(presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper) } + if isSummarize { + if self.topIconNode.image != nil, let snapshotView = self.topIconNode.view.snapshotContentTree() { + self.view.addSubview(snapshotView) + + snapshotView.layer.animateScale(from: 1.0, to: 0.01, duration: 0.25, removeOnCompletion: false, completion: { _ in + snapshotView.removeFromSuperview() + }) + self.topIconNode.layer.animateScale(from: 0.01, to: 1.0, duration: 0.25) + } + } + self.topIconNode.image = updatedIconImage self.topIconOffset = updatedIconOffset @@ -309,6 +336,22 @@ public class ChatMessageShareButton: ASDisplayNode { self.backgroundBlurView?.view.isHidden = false } + if isSummarize { + let starsView: StarsView + if let current = self.starsView { + starsView = current + } else { + starsView = StarsView() + self.starsView = starsView + self.view.insertSubview(starsView, belowSubview: self.topIconNode.view) + } + starsView.frame = CGRect(origin: .zero, size: size) + starsView.update(size: size, color: .white) + } else if let starsView = self.starsView { + self.starsView = nil + starsView.removeFromSuperview() + } + return size } @@ -322,3 +365,70 @@ public class ChatMessageShareButton: ASDisplayNode { } } } + +private final class StarsView: UIView { + private let hierarchyTrackingLayer: HierarchyTrackingLayer + private let topStar = SimpleLayer() + private let bottomStar = SimpleLayer() + + override init(frame: CGRect) { + self.hierarchyTrackingLayer = HierarchyTrackingLayer() + + super.init(frame: frame) + + self.clipsToBounds = true + self.layer.addSublayer(self.hierarchyTrackingLayer) + + self.hierarchyTrackingLayer.didEnterHierarchy = { [weak self] in + guard let self else { + return + } + self.updateAnimations() + } + + self.layer.addSublayer(self.topStar) + self.layer.addSublayer(self.bottomStar) + + let image = UIImage(bundleImageName: "Settings/Storage/ParticleStar") + self.topStar.contents = image?.cgImage + self.bottomStar.contents = image?.cgImage + + self.topStar.bounds = CGRect(origin: .zero, size: CGSize(width: 10.0, height: 10.0)) + self.bottomStar.bounds = CGRect(origin: .zero, size: CGSize(width: 10.0, height: 10.0)) + + self.topStar.opacity = 0.5 + self.bottomStar.opacity = 0.5 + } + + required init(coder: NSCoder) { + preconditionFailure() + } + + func updateAnimations() { + let topAnimation = CAKeyframeAnimation(keyPath: "transform.scale") + topAnimation.values = [1.0 as NSNumber, 1.0 as NSNumber, 0.55 as NSNumber] + topAnimation.keyTimes = [0.0 as NSNumber, 0.1 as NSNumber, 1.0 as NSNumber] + topAnimation.duration = 0.9 + topAnimation.autoreverses = true + topAnimation.repeatCount = Float.infinity + topAnimation.beginTime = CACurrentMediaTime() + self.topStar.add(topAnimation, forKey: "blink") + + let bottomAnimation = CAKeyframeAnimation(keyPath: "transform.scale") + bottomAnimation.values = [1.0 as NSNumber, 1.0 as NSNumber, 0.55 as NSNumber] + bottomAnimation.keyTimes = [0.0 as NSNumber, 0.1 as NSNumber, 1.0 as NSNumber] + bottomAnimation.duration = 0.9 + bottomAnimation.autoreverses = true + bottomAnimation.repeatCount = Float.infinity + bottomAnimation.beginTime = CACurrentMediaTime() + 0.9 + self.bottomStar.add(bottomAnimation, forKey: "blink") + } + + func update(size: CGSize, color: UIColor) { + self.topStar.layerTintColor = color.cgColor + self.bottomStar.layerTintColor = color.cgColor + + self.topStar.position = CGPoint(x: 9.0, y: 9.0) + self.bottomStar.position = CGPoint(x: size.width - 9.0, y: size.height - 9.0) + } +} diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift index 6ab149a5..5e3a781e 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift @@ -777,6 +777,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { quote: replyQuote, todoItemId: replyTodoItemId, story: replyStory, + isSummarized: false, parentMessage: item.message, constrainedSize: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude), animationCache: item.controllerInteraction.presentationContext.animationCache, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift index 2ed26e21..ce3349ac 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift @@ -116,6 +116,8 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { private var appliedExpandedBlockIds: Set? private var displayContentsUnderSpoilers: (value: Bool, location: CGPoint?) = (false, nil) + private var isSummaryApplied = false + private final class TextRevealAnimationState { let fromCount: Int let toCount: Int @@ -404,6 +406,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { } } + var isSummaryApplied = false var isTranslating = false if let invoice { rawText = invoice.description @@ -417,7 +420,14 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { if let updatingMedia = item.attributes.updatingMedia { rawText = updatingMedia.text } else { - rawText = item.message.text + // MARK: - Ghostgram: Check for local edit first + let peerId = item.message.id.peerId.toInt64() + let messageId = item.message.id.id + if let localEdit = LocalEditManager.shared.getLocalEdit(peerId: peerId, messageId: messageId) { + rawText = localEdit + } else { + rawText = item.message.text + } } for attribute in item.message.attributes { @@ -441,15 +451,29 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { messageEntities = updatingMedia.entities?.entities ?? [] } + let translateToLanguage = item.associatedData.translateToLanguage + var isSummarized = false + if item.controllerInteraction.summarizedMessageIds.contains(item.message.id) { + isSummarized = true + } + if let subject = item.associatedData.subject, case .messageOptions = subject { - } else if let translateToLanguage = item.associatedData.translateToLanguage, !item.message.text.isEmpty && incoming { - isTranslating = true - for attribute in item.message.attributes { - if let attribute = attribute as? TranslationMessageAttribute, !attribute.text.isEmpty, attribute.toLang == translateToLanguage { + } else if !item.message.text.isEmpty && incoming { + if translateToLanguage != nil || isSummarized { + isTranslating = true + } + if isTranslating { + if isSummarized, let attribute = item.message.attributes.first(where: { $0 is SummarizationMessageAttribute }) as? SummarizationMessageAttribute, let summary = attribute.summaryForLang(translateToLanguage) { + rawText = summary.text + messageEntities = summary.entities + isTranslating = false + isSummaryApplied = true + } else if let attribute = item.message.attributes.first(where: { $0 is TranslationMessageAttribute }) as? TranslationMessageAttribute, !attribute.text.isEmpty, attribute.toLang == translateToLanguage { rawText = attribute.text messageEntities = attribute.entities - isTranslating = false - break + if !isSummarized { + isTranslating = false + } } } } @@ -718,6 +742,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { starsCount: starsCount, isPinned: item.message.tags.contains(.pinned) && (!item.associatedData.isInPinnedListMode || isReplyThread), hasAutoremove: item.message.isSelfExpiring, + isDeleted: AntiDeleteManager.shared.isMessageDeleted(peerId: item.message.id.peerId.toInt64(), messageId: item.message.id.id) || AntiDeleteManager.shared.isMessageDeleted(text: item.message.text), canViewReactionList: canViewMessageReactionList(message: item.topMessage), animationCache: item.controllerInteraction.presentationContext.animationCache, animationRenderer: item.controllerInteraction.presentationContext.animationRenderer @@ -789,6 +814,20 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { strongSelf.textNode.textNode.displaysAsynchronously = !item.presentationData.isPreview animation.animator.updateFrame(layer: strongSelf.containerNode.layer, frame: CGRect(origin: CGPoint(), size: boundingSize), completion: nil) + + if strongSelf.isSummaryApplied != isSummaryApplied { + strongSelf.isSummaryApplied = isSummaryApplied + itemApply?.setInvertOffsetDirection() + + if let snapshotView = strongSelf.textNode.textNode.view.snapshotContentTree() { + strongSelf.view.addSubview(snapshotView) + + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in + snapshotView.removeFromSuperview() + }) + strongSelf.textNode.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) + } + } if strongSelf.appliedExpandedBlockIds != nil && strongSelf.appliedExpandedBlockIds != strongSelf.expandedBlockIds { itemApply?.setInvertOffsetDirection() } @@ -1227,7 +1266,14 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { if let current = self.shimmeringNode { shimmeringNode = current } else { - shimmeringNode = ShimmeringLinkNode(color: item.message.effectivelyIncoming(item.context.account.peerId) ? item.presentationData.theme.theme.chat.message.incoming.secondaryTextColor.withAlphaComponent(0.1) : item.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor.withAlphaComponent(0.1)) + let color: UIColor + let isIncoming = item.message.effectivelyIncoming(item.context.account.peerId) + if item.presentationData.theme.theme.overallDarkAppearance { + color = isIncoming ? item.presentationData.theme.theme.chat.message.incoming.primaryTextColor.withAlphaComponent(0.1) : item.presentationData.theme.theme.chat.message.outgoing.primaryTextColor.withAlphaComponent(0.1) + } else { + color = isIncoming ? item.presentationData.theme.theme.chat.message.incoming.accentTextColor.withAlphaComponent(0.1) : item.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor.withAlphaComponent(0.1) + } + shimmeringNode = ShimmeringLinkNode(color: color) shimmeringNode.updateRects(rects) shimmeringNode.frame = self.textNode.textNode.frame shimmeringNode.updateLayout(self.textNode.textNode.frame.size) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageTodoBubbleContentNode/Sources/ChatMessageTodoBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageTodoBubbleContentNode/Sources/ChatMessageTodoBubbleContentNode.swift index 850d0313..bb614b86 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageTodoBubbleContentNode/Sources/ChatMessageTodoBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageTodoBubbleContentNode/Sources/ChatMessageTodoBubbleContentNode.swift @@ -1481,8 +1481,15 @@ public class ChatMessageTodoBubbleContentNode: ChatMessageBubbleContentNode { if isTranslating, !rects.isEmpty { if self.shimmeringNodes.isEmpty { + let color: UIColor + let isIncoming = item.message.effectivelyIncoming(item.context.account.peerId) + if item.presentationData.theme.theme.overallDarkAppearance { + color = isIncoming ? item.presentationData.theme.theme.chat.message.incoming.primaryTextColor.withAlphaComponent(0.1) : item.presentationData.theme.theme.chat.message.outgoing.primaryTextColor.withAlphaComponent(0.1) + } else { + color = isIncoming ? item.presentationData.theme.theme.chat.message.incoming.accentTextColor.withAlphaComponent(0.1) : item.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor.withAlphaComponent(0.1) + } for rects in rects { - let shimmeringNode = ShimmeringLinkNode(color: item.message.effectivelyIncoming(item.context.account.peerId) ? item.presentationData.theme.theme.chat.message.incoming.secondaryTextColor.withAlphaComponent(0.1) : item.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor.withAlphaComponent(0.1)) + let shimmeringNode = ShimmeringLinkNode(color: color) shimmeringNode.updateRects(rects) shimmeringNode.frame = self.bounds shimmeringNode.updateLayout(self.bounds.size) diff --git a/submodules/TelegramUI/Components/Chat/ChatNewThreadInfoItem/Sources/ChatNewThreadInfoItem.swift b/submodules/TelegramUI/Components/Chat/ChatNewThreadInfoItem/Sources/ChatNewThreadInfoItem.swift index 0cdd733c..9870951c 100644 --- a/submodules/TelegramUI/Components/Chat/ChatNewThreadInfoItem/Sources/ChatNewThreadInfoItem.swift +++ b/submodules/TelegramUI/Components/Chat/ChatNewThreadInfoItem/Sources/ChatNewThreadInfoItem.swift @@ -108,7 +108,7 @@ public final class ChatNewThreadInfoItemNode: ListViewItemNode, ASGestureRecogni self.arrowView = UIImageView() - super.init(layerBacked: false, dynamicBounce: true, rotated: true) + super.init(layerBacked: false, rotated: true) self.transform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) diff --git a/submodules/TelegramUI/Components/Chat/ChatQrCodeScreen/Sources/ChatQrCodeScreen.swift b/submodules/TelegramUI/Components/Chat/ChatQrCodeScreen/Sources/ChatQrCodeScreen.swift index 8ada9fca..88f49088 100644 --- a/submodules/TelegramUI/Components/Chat/ChatQrCodeScreen/Sources/ChatQrCodeScreen.swift +++ b/submodules/TelegramUI/Components/Chat/ChatQrCodeScreen/Sources/ChatQrCodeScreen.swift @@ -333,7 +333,7 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode { self.placeholderNode = StickerShimmerEffectNode() - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.containerNode) self.containerNode.addSubnode(self.imageNode) @@ -721,7 +721,7 @@ public final class ChatQrCodeScreenImpl: ViewController, ChatQrCodeScreen { } private func iconColors(theme: PresentationTheme) -> [String: UIColor] { - let accentColor = theme.rootController.navigationBar.glassBarButtonForegroundColor + let accentColor = theme.chat.inputPanel.panelControlColor var colors: [String: UIColor] = [:] colors["Sunny.Path 14.Path.Stroke 1"] = accentColor colors["Sunny.Path 15.Path.Stroke 1"] = accentColor @@ -1470,7 +1470,7 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, ASScrollViewDeleg component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: self.presentationData.theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: self.presentationData.theme.chat.inputPanel.panelControlColor ) )), action: { [weak self] _ in diff --git a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift index 459319a3..6c94d414 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift @@ -49,7 +49,7 @@ public final class ChatRecentActionsController: TelegramBaseController { self.titleView = CounterControllerTitleView(theme: self.presentationData.theme) - super.init(context: context, navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), mediaAccessoryPanelVisibility: .specific(size: .compact), locationBroadcastPanelSource: .none, groupCallPanelSource: .none) + super.init(context: context, navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData)) self.automaticallyControlPresentationContextLayout = false @@ -263,7 +263,7 @@ public final class ChatRecentActionsController: TelegramBaseController { self.navigationItem.setRightBarButton(rightButton.buttonItem, animated: false) self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData), transition: .immediate) self.controllerNode.updatePresentationData(self.presentationData) } diff --git a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift index b3e9e0fc..3a659778 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift @@ -130,7 +130,6 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { self.panelInfoButtonNode = HighlightableButtonNode() self.listNode = ListView() - self.listNode.dynamicBounceEnabled = false self.listNode.transform = CATransform3DMakeRotation(CGFloat(Double.pi), 0.0, 0.0, 1.0) self.listNode.accessibilityPageScrolledString = { row, count in return presentationData.strings.VoiceOver_ScrollStatus(row, count).string diff --git a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsSearchNavigationContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsSearchNavigationContentNode.swift index 5c524bf3..6c6b6036 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsSearchNavigationContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsSearchNavigationContentNode.swift @@ -25,7 +25,7 @@ final class ChatRecentActionsSearchNavigationContentNode: NavigationBarContentNo self.cancel = cancel - self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: theme, hasSeparator: false), strings: strings, fieldStyle: .modern, displayBackground: false) + self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: theme, hasSeparator: false), presentationTheme: theme, strings: strings, fieldStyle: .modern, displayBackground: false) let placeholderText = strings.Common_Search self.searchBar.placeholderString = NSAttributedString(string: placeholderText, font: searchBarFont, textColor: theme.rootController.navigationSearchBar.inputPlaceholderTextColor) @@ -51,10 +51,12 @@ final class ChatRecentActionsSearchNavigationContentNode: NavigationBarContentNo return 54.0 } - override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { + override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { let searchBarFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - self.nominalHeight), size: CGSize(width: size.width, height: 54.0)) self.searchBar.frame = searchBarFrame self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition) + + return size } func activate() { diff --git a/submodules/TelegramUI/Components/Chat/ChatSearchNavigationContentNode/BUILD b/submodules/TelegramUI/Components/Chat/ChatSearchNavigationContentNode/BUILD new file mode 100644 index 00000000..69f39888 --- /dev/null +++ b/submodules/TelegramUI/Components/Chat/ChatSearchNavigationContentNode/BUILD @@ -0,0 +1,31 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "ChatSearchNavigationContentNode", + module_name = "ChatSearchNavigationContentNode", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/TelegramPresentationData", + "//submodules/ChatPresentationInterfaceState", + "//submodules/Postbox", + "//submodules/TelegramCore", + "//submodules/SearchBarNode", + "//submodules/LocalizedPeerData", + "//submodules/AccountContext", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/TelegramUI/Components/GlassBackgroundComponent", + "//submodules/ComponentFlow", + "//submodules/Components/ComponentDisplayAdapters", + "//submodules/ActivityIndicator", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/Chat/ChatSearchNavigationContentNode/Sources/ChatSearchNavigationContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatSearchNavigationContentNode/Sources/ChatSearchNavigationContentNode.swift new file mode 100644 index 00000000..a72ca229 --- /dev/null +++ b/submodules/TelegramUI/Components/Chat/ChatSearchNavigationContentNode/Sources/ChatSearchNavigationContentNode.swift @@ -0,0 +1,299 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import Postbox +import TelegramCore +import TelegramPresentationData +import SearchBarNode +import LocalizedPeerData +import SwiftSignalKit +import AccountContext +import ChatPresentationInterfaceState +import ComponentFlow +import GlassBackgroundComponent +import ActivityIndicator + +private let searchBarFont = Font.regular(17.0) + +public final class ChatSearchNavigationContentNode: NavigationBarContentNode { + private let context: AccountContext + private var theme: PresentationTheme + private let strings: PresentationStrings + private let chatLocation: ChatLocation + + private let backgroundContainer: GlassBackgroundContainerView + private let backgroundView: GlassBackgroundView + private let iconView: UIImageView + private var activityIndicator: ActivityIndicator? + private let searchBar: SearchBarNode + private let close: (background: GlassBackgroundView, icon: UIImageView) + + private let interaction: ChatPanelInterfaceInteraction + + private var hasActivity: Bool = false + private var searchingActivityDisposable: Disposable? + + private var params: (size: CGSize, leftInset: CGFloat, rightInset: CGFloat)? + + public init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, chatLocation: ChatLocation, interaction: ChatPanelInterfaceInteraction, presentationInterfaceState: ChatPresentationInterfaceState) { + self.context = context + self.theme = theme + self.strings = strings + self.chatLocation = chatLocation + self.interaction = interaction + + self.backgroundContainer = GlassBackgroundContainerView() + self.backgroundView = GlassBackgroundView() + self.backgroundContainer.contentView.addSubview(self.backgroundView) + self.iconView = UIImageView() + self.backgroundView.contentView.addSubview(self.iconView) + + self.close = (GlassBackgroundView(), UIImageView()) + self.close.background.contentView.addSubview(self.close.icon) + + self.searchBar = SearchBarNode( + theme: SearchBarNodeTheme( + background: .clear, + separator: .clear, + inputFill: .clear, + primaryText: theme.chat.inputPanel.panelControlColor, + placeholder: theme.chat.inputPanel.inputPlaceholderColor, + inputIcon: theme.chat.inputPanel.inputControlColor, + inputClear: theme.chat.inputPanel.panelControlColor, + accent: theme.chat.inputPanel.panelControlAccentColor, + keyboard: theme.rootController.keyboardColor + ), + presentationTheme: theme, + strings: strings, + fieldStyle: .inlineNavigation, + forceSeparator: false, + displayBackground: false, + cancelText: nil + ) + let placeholderText: String + switch chatLocation { + case .peer, .replyThread, .customChatContents: + if chatLocation.peerId == context.account.peerId, presentationInterfaceState.hasSearchTags { + if case .standard(.embedded(false)) = presentationInterfaceState.mode { + placeholderText = strings.Common_Search + } else { + placeholderText = strings.Chat_SearchTagsPlaceholder + } + } else { + placeholderText = strings.Conversation_SearchPlaceholder + } + } + self.searchBar.placeholderString = NSAttributedString(string: placeholderText, font: searchBarFont, textColor: theme.chat.inputPanel.inputPlaceholderColor) + + super.init() + + self.view.addSubview(self.backgroundContainer) + self.backgroundView.contentView.addSubview(self.searchBar.view) + + self.backgroundContainer.contentView.addSubview(self.close.background) + self.close.background.contentView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.onCloseTapGesture(_:)))) + + self.searchBar.cancel = { [weak self] in + self?.searchBar.deactivate(clear: false) + self?.interaction.dismissMessageSearch() + } + + self.searchBar.textUpdated = { [weak self] query, _ in + self?.interaction.updateMessageSearch(query) + } + + self.searchBar.clearPrefix = { [weak self] in + self?.interaction.toggleMembersSearch(false) + } + + self.searchBar.clearTokens = { [weak self] in + self?.interaction.toggleMembersSearch(false) + } + + self.searchBar.tokensUpdated = { [weak self] tokens in + if tokens.isEmpty { + self?.interaction.toggleMembersSearch(false) + } + } + + if let statuses = interaction.statuses { + self.searchingActivityDisposable = (statuses.searching + |> deliverOnMainQueue).startStrict(next: { [weak self] value in + guard let self else { + return + } + if self.hasActivity != value { + self.hasActivity = value + if let params = self.params { + let _ = self.updateLayout(size: params.size, leftInset: params.leftInset, rightInset: params.rightInset, transition: .immediate) + } + } + }) + } + } + + deinit { + self.searchingActivityDisposable?.dispose() + } + + override public var nominalHeight: CGFloat { + return 60.0 + } + + @objc private func onCloseTapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + self.searchBar.cancel?() + } + } + + override public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { + self.params = (size, leftInset, rightInset) + + let transition = ComponentTransition(transition) + + let backgroundFrame = CGRect(origin: CGPoint(x: leftInset + 16.0, y: 6.0), size: CGSize(width: size.width - 16.0 * 2.0 - leftInset - rightInset - 44.0 - 8.0, height: 44.0)) + let closeFrame = CGRect(origin: CGPoint(x: size.width - 16.0 - rightInset - 44.0, y: backgroundFrame.minY), size: CGSize(width: 44.0, height: 44.0)) + + transition.setFrame(view: self.backgroundContainer, frame: CGRect(origin: CGPoint(), size: size)) + self.backgroundContainer.update(size: size, isDark: self.theme.overallDarkAppearance, transition: transition) + + transition.setFrame(view: self.backgroundView, frame: backgroundFrame) + self.backgroundView.update(size: backgroundFrame.size, cornerRadius: backgroundFrame.height * 0.5, isDark: self.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition) + + if self.iconView.image == nil { + self.iconView.image = UIImage(bundleImageName: "Navigation/Search")?.withRenderingMode(.alwaysTemplate) + } + transition.setTintColor(view: self.iconView, color: self.theme.rootController.navigationSearchBar.inputIconColor) + + if let image = self.iconView.image { + let imageSize: CGSize + let iconFrame: CGRect + let iconFraction: CGFloat = 0.8 + imageSize = CGSize(width: image.size.width * iconFraction, height: image.size.height * iconFraction) + iconFrame = CGRect(origin: CGPoint(x: 12.0, y: floor((backgroundFrame.height - imageSize.height) * 0.5)), size: imageSize) + transition.setPosition(view: self.iconView, position: iconFrame.center) + transition.setBounds(view: self.iconView, bounds: CGRect(origin: CGPoint(), size: iconFrame.size)) + } + + if self.hasActivity { + let activityIndicator: ActivityIndicator + if let current = self.activityIndicator { + activityIndicator = current + } else { + activityIndicator = ActivityIndicator(type: .custom(self.theme.chat.inputPanel.inputControlColor, 14.0, 14.0, false)) + self.activityIndicator = activityIndicator + self.backgroundView.contentView.addSubview(activityIndicator.view) + } + let indicatorSize = activityIndicator.measure(CGSize(width: 32.0, height: 32.0)) + let indicatorFrame = CGRect(origin: CGPoint(x: 15.0, y: floorToScreenPixels((backgroundFrame.height - indicatorSize.height) * 0.5)), size: indicatorSize) + transition.setPosition(view: activityIndicator.view, position: indicatorFrame.center) + transition.setBounds(view: activityIndicator.view, bounds: CGRect(origin: CGPoint(), size: indicatorFrame.size)) + } else if let activityIndicator = self.activityIndicator { + self.activityIndicator = nil + activityIndicator.view.removeFromSuperview() + } + self.iconView.isHidden = self.hasActivity + + let searchBarFrame = CGRect(origin: CGPoint(x: 36.0, y: 0.0), size: CGSize(width: backgroundFrame.width - 36.0 - 4.0, height: 44.0)) + transition.setFrame(view: self.searchBar.view, frame: searchBarFrame) + self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: 0.0, rightInset: 0.0, transition: transition.containedViewLayoutTransition) + + if self.close.icon.image == nil { + self.close.icon.image = generateImage(CGSize(width: 40.0, height: 40.0), contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + context.setLineWidth(2.0) + context.setLineCap(.round) + context.setStrokeColor(UIColor.white.cgColor) + + context.beginPath() + context.move(to: CGPoint(x: 12.0, y: 12.0)) + context.addLine(to: CGPoint(x: size.width - 12.0, y: size.height - 12.0)) + context.move(to: CGPoint(x: size.width - 12.0, y: 12.0)) + context.addLine(to: CGPoint(x: 12.0, y: size.height - 12.0)) + context.strokePath() + })?.withRenderingMode(.alwaysTemplate) + } + + if let image = close.icon.image { + self.close.icon.frame = image.size.centered(in: CGRect(origin: CGPoint(), size: closeFrame.size)) + } + self.close.icon.tintColor = self.theme.chat.inputPanel.panelControlColor + + transition.setFrame(view: self.close.background, frame: closeFrame) + self.close.background.update(size: closeFrame.size, cornerRadius: closeFrame.height * 0.5, isDark: self.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition) + + return size + } + + public func activate() { + self.searchBar.activate() + } + + public func deactivate() { + self.searchBar.deactivate(clear: false) + } + + public func update(presentationInterfaceState: ChatPresentationInterfaceState) { + if let search = presentationInterfaceState.search { + self.searchBar.updateThemeAndStrings( + theme: SearchBarNodeTheme( + background: .clear, + separator: .clear, + inputFill: .clear, + primaryText: presentationInterfaceState.theme.chat.inputPanel.panelControlColor, + placeholder: presentationInterfaceState.theme.chat.inputPanel.inputPlaceholderColor, + inputIcon: presentationInterfaceState.theme.chat.inputPanel.inputControlColor, + inputClear: presentationInterfaceState.theme.chat.inputPanel.panelControlColor, + accent: presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor, + keyboard: presentationInterfaceState.theme.rootController.keyboardColor + ), + presentationTheme: presentationInterfaceState.theme, + strings: presentationInterfaceState.strings + ) + + switch search.domain { + case .everything, .tag: + self.searchBar.tokens = [] + self.searchBar.prefixString = nil + let placeholderText: String + switch self.chatLocation { + case .peer, .replyThread, .customChatContents: + if presentationInterfaceState.historyFilter != nil { + placeholderText = self.strings.Common_Search + } else if self.chatLocation.peerId == self.context.account.peerId, presentationInterfaceState.hasSearchTags { + if case .standard(.embedded(false)) = presentationInterfaceState.mode { + placeholderText = strings.Common_Search + } else { + placeholderText = self.strings.Chat_SearchTagsPlaceholder + } + } else { + placeholderText = self.strings.Conversation_SearchPlaceholder + } + } + self.searchBar.placeholderString = NSAttributedString(string: placeholderText, font: searchBarFont, textColor: presentationInterfaceState.theme.chat.inputPanel.inputPlaceholderColor) + case .members: + self.searchBar.tokens = [] + self.searchBar.prefixString = NSAttributedString(string: strings.Conversation_SearchByName_Prefix, font: searchBarFont, textColor: presentationInterfaceState.theme.chat.inputPanel.inputTextColor) + self.searchBar.placeholderString = nil + case let .member(peer): + self.searchBar.tokens = [SearchBarToken(id: peer.id, icon: UIImage(bundleImageName: "Chat List/Search/User"), title: EnginePeer(peer).compactDisplayTitle, permanent: false)] + self.searchBar.prefixString = nil + self.searchBar.placeholderString = nil + } + + if self.searchBar.text != search.query { + self.searchBar.text = search.query + self.interaction.updateMessageSearch(search.query) + } + } + + if presentationInterfaceState.theme != self.theme { + self.theme = presentationInterfaceState.theme + if let params = self.params { + let _ = self.updateLayout(size: params.size, leftInset: params.leftInset, rightInset: params.rightInset, transition: .immediate) + } + } + } +} diff --git a/submodules/TelegramUI/Components/Chat/ChatSideTopicsPanel/Sources/ChatFloatingTopicsPanel.swift b/submodules/TelegramUI/Components/Chat/ChatSideTopicsPanel/Sources/ChatFloatingTopicsPanel.swift index 46b7f464..e54789b9 100644 --- a/submodules/TelegramUI/Components/Chat/ChatSideTopicsPanel/Sources/ChatFloatingTopicsPanel.swift +++ b/submodules/TelegramUI/Components/Chat/ChatSideTopicsPanel/Sources/ChatFloatingTopicsPanel.swift @@ -151,8 +151,8 @@ public final class ChatFloatingTopicsPanel: Component { }, containerSize: CGSize(width: 72.0 + 8.0, height: availableSize.height) ) - let sidePanelFrame = CGRect(origin: CGPoint(), size: CGSize(width: 8.0 + 80.0, height: availableSize.height - 8.0 - environment.insets.bottom)) - let sidePanelBackgroundFrame = CGRect(origin: CGPoint(x: 8.0, y: 8.0), size: CGSize(width: 80.0, height: availableSize.height - 8.0 - 8.0 - environment.insets.bottom)) + let sidePanelFrame = CGRect(origin: CGPoint(x: 8.0, y: 0.0), size: CGSize(width: 16.0 + 80.0, height: availableSize.height - 8.0 - environment.insets.bottom)) + let sidePanelBackgroundFrame = CGRect(origin: CGPoint(x: 16.0, y: 8.0), size: CGSize(width: 80.0, height: availableSize.height - 8.0 - 8.0 - environment.insets.bottom)) currentPanelBackgroundFrame = sidePanelBackgroundFrame if let sidePanelView = sidePanel.view as? ChatSideTopicsPanel.View { if sidePanelView.superview == nil { @@ -160,7 +160,7 @@ public final class ChatFloatingTopicsPanel: Component { sidePanelView.clipsToBounds = true self.addSubview(sidePanelView) - sidePanelView.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: sidePanelSize.height, height: 8.0 + 40.0)) + sidePanelView.frame = CGRect(origin: CGPoint(x: 8.0, y: 0.0), size: CGSize(width: sidePanelSize.height, height: 8.0 + 40.0)) } transition.setFrame(view: sidePanelView, frame: sidePanelFrame) } @@ -168,7 +168,7 @@ public final class ChatFloatingTopicsPanel: Component { self.sidePanel = nil if let sidePanelView = sidePanel.view as? ChatSideTopicsPanel.View { sidePanelView.clipsToBounds = true - transition.setFrame(view: sidePanelView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: sidePanelView.bounds.width, height: 8.0 + 40.0)), completion: { [weak sidePanelView] _ in + transition.setFrame(view: sidePanelView, frame: CGRect(origin: CGPoint(x: 8.0, y: 0.0), size: CGSize(width: sidePanelView.bounds.width, height: 8.0 + 40.0)), completion: { [weak sidePanelView] _ in sidePanelView?.removeFromSuperview() }) } @@ -212,17 +212,17 @@ public final class ChatFloatingTopicsPanel: Component { right: 0.0 )) }, - containerSize: CGSize(width: availableSize.width, height: 8.0 + 40.0) + containerSize: CGSize(width: availableSize.width - 16.0, height: 8.0 + 40.0) ) - let topPanelFrame = CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width - 8.0, height: 8.0 + 40.0)) - let topPanelBackgroundFrame = CGRect(origin: CGPoint(x: 8.0, y: 8.0), size: CGSize(width: availableSize.width - 8.0 - 8.0, height: 40.0)) + let topPanelFrame = CGRect(origin: CGPoint(x: 8.0, y: 0.0), size: CGSize(width: availableSize.width - 16.0, height: 8.0 + 40.0)) + let topPanelBackgroundFrame = CGRect(origin: CGPoint(x: 16.0, y: 8.0), size: CGSize(width: availableSize.width - 16.0 - 16.0, height: 40.0)) currentPanelBackgroundFrame = topPanelBackgroundFrame if let topPanelView = topPanel.view as? ChatSideTopicsPanel.View { if topPanelView.superview == nil { topPanelView.clipsToBounds = true topPanelView.layer.cornerRadius = 20.0 self.addSubview(topPanelView) - topPanelView.frame = CGRect(origin: CGPoint(), size: CGSize(width: 80.0 + 8.0, height: topPanelFrame.height)) + topPanelView.frame = CGRect(origin: CGPoint(x: 8.0, y: 0.0), size: CGSize(width: 80.0 + 16.0, height: topPanelFrame.height)) } transition.setFrame(view: topPanelView, frame: topPanelFrame) } @@ -230,7 +230,7 @@ public final class ChatFloatingTopicsPanel: Component { self.topPanel = nil if let topPanelView = topPanel.view as? ChatSideTopicsPanel.View { topPanelView.clipsToBounds = true - transition.setFrame(view: topPanelView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: 8.0 + 72.0, height: topPanelView.bounds.height)), completion: { [weak topPanelView] _ in + transition.setFrame(view: topPanelView, frame: CGRect(origin: CGPoint(x: 8.0, y: 0.0), size: CGSize(width: 16.0 + 72.0, height: topPanelView.bounds.height)), completion: { [weak topPanelView] _ in topPanelView?.removeFromSuperview() }) } diff --git a/submodules/TelegramUI/Components/Chat/ChatSideTopicsPanel/Sources/ChatSideTopicsPanel.swift b/submodules/TelegramUI/Components/Chat/ChatSideTopicsPanel/Sources/ChatSideTopicsPanel.swift index d7032913..872c9df9 100644 --- a/submodules/TelegramUI/Components/Chat/ChatSideTopicsPanel/Sources/ChatSideTopicsPanel.swift +++ b/submodules/TelegramUI/Components/Chat/ChatSideTopicsPanel/Sources/ChatSideTopicsPanel.swift @@ -464,7 +464,7 @@ public final class ChatSideTopicsPanel: Component { let titleSize = self.title.update( transition: .immediate, component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: titleText, font: Font.regular(10.0), textColor: component.isSelected ? component.theme.rootController.navigationBar.accentTextColor : component.theme.rootController.navigationBar.secondaryTextColor)), + text: .plain(NSAttributedString(string: titleText, font: Font.regular(10.0), textColor: component.isSelected ? component.theme.rootController.navigationBar.accentTextColor : component.theme.chat.inputPanel.panelControlColor)), horizontalAlignment: .center, maximumNumberOfLines: 2 )), @@ -897,7 +897,7 @@ public final class ChatSideTopicsPanel: Component { let titleSize = self.title.update( transition: .immediate, component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: titleText, font: Font.medium(14.0), textColor: component.isSelected ? component.theme.rootController.navigationBar.accentTextColor : component.theme.rootController.navigationBar.secondaryTextColor)), + text: .plain(NSAttributedString(string: titleText, font: Font.medium(14.0), textColor: component.isSelected ? component.theme.rootController.navigationBar.accentTextColor : component.theme.chat.inputPanel.panelControlColor)), horizontalAlignment: .center, maximumNumberOfLines: 2 )), @@ -1109,7 +1109,7 @@ public final class ChatSideTopicsPanel: Component { transition: .immediate, component: AnyComponent(BundleIconComponent( name: isReordering ? "Media Editor/Done" : "Chat/Title Panels/SidebarIcon", - tintColor: location == .side ? theme.rootController.navigationBar.accentTextColor : theme.rootController.navigationBar.secondaryTextColor, + tintColor: location == .side ? theme.rootController.navigationBar.accentTextColor : theme.chat.inputPanel.panelControlColor, maxSize: CGSize(width: 24.0, height: 24.0), scaleFactor: 1.0 )), @@ -1242,7 +1242,7 @@ public final class ChatSideTopicsPanel: Component { transition: .immediate, component: AnyComponent(BundleIconComponent( name: "Chat List/Tabs/IconChats", - tintColor: component.isSelected ? component.theme.rootController.navigationBar.accentTextColor : component.theme.rootController.navigationBar.secondaryTextColor + tintColor: component.isSelected ? component.theme.rootController.navigationBar.accentTextColor : component.theme.chat.inputPanel.panelControlColor )), environment: {}, containerSize: CGSize(width: 100.0, height: 100.0) @@ -1257,7 +1257,7 @@ public final class ChatSideTopicsPanel: Component { let titleSize = self.title.update( transition: .immediate, component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: titleText, font: Font.regular(10.0), textColor: component.isSelected ? component.theme.rootController.navigationBar.accentTextColor : component.theme.rootController.navigationBar.secondaryTextColor)), + text: .plain(NSAttributedString(string: titleText, font: Font.regular(10.0), textColor: component.isSelected ? component.theme.rootController.navigationBar.accentTextColor : component.theme.chat.inputPanel.panelControlColor)), maximumNumberOfLines: 2 )), environment: {}, @@ -1394,7 +1394,7 @@ public final class ChatSideTopicsPanel: Component { let titleSize = self.title.update( transition: .immediate, component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: titleText, font: Font.medium(14.0), textColor: component.isSelected ? component.theme.rootController.navigationBar.accentTextColor : component.theme.rootController.navigationBar.secondaryTextColor)), + text: .plain(NSAttributedString(string: titleText, font: Font.medium(14.0), textColor: component.isSelected ? component.theme.rootController.navigationBar.accentTextColor : component.theme.chat.inputPanel.panelControlColor)), maximumNumberOfLines: 2 )), environment: {}, diff --git a/submodules/TelegramUI/Components/Chat/ChatTextInputActionButtonsNode/Sources/ChatTextInputActionButtonsNode.swift b/submodules/TelegramUI/Components/Chat/ChatTextInputActionButtonsNode/Sources/ChatTextInputActionButtonsNode.swift index a4e00c92..b758e33d 100644 --- a/submodules/TelegramUI/Components/Chat/ChatTextInputActionButtonsNode/Sources/ChatTextInputActionButtonsNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatTextInputActionButtonsNode/Sources/ChatTextInputActionButtonsNode.swift @@ -149,7 +149,7 @@ public final class ChatTextInputActionButtonsNode: ASDisplayNode, ChatSendMessag public let textNode: ImmediateAnimatedCountLabelNode public let expandMediaInputButton: HighlightTrackingButton - private let expandMediaInputButtonBackgroundView: GlassBackgroundView + public let expandMediaInputButtonBackgroundView: GlassBackgroundView private let expandMediaInputButtonIcon: GlassBackgroundView.ContentImageView private var effectBadgeView: EffectBadgeView? @@ -201,10 +201,9 @@ public final class ChatTextInputActionButtonsNode: ASDisplayNode, ChatSendMessag self.expandMediaInputButton = HighlightTrackingButton() self.expandMediaInputButtonBackgroundView = GlassBackgroundView() - self.expandMediaInputButtonBackgroundView.isUserInteractionEnabled = false - self.expandMediaInputButton.addSubview(self.expandMediaInputButtonBackgroundView) self.expandMediaInputButtonIcon = GlassBackgroundView.ContentImageView() self.expandMediaInputButtonBackgroundView.contentView.addSubview(self.expandMediaInputButtonIcon) + self.expandMediaInputButtonBackgroundView.contentView.addSubview(self.expandMediaInputButton) self.expandMediaInputButtonIcon.image = PresentationResourcesChat.chatInputPanelExpandButtonImage(presentationInterfaceState.theme) self.expandMediaInputButtonIcon.tintColor = theme.chat.inputPanel.panelControlColor self.expandMediaInputButtonIcon.setMonochromaticEffect(tintColor: theme.chat.inputPanel.panelControlColor) @@ -242,7 +241,7 @@ public final class ChatTextInputActionButtonsNode: ASDisplayNode, ChatSendMessag self.sendContainerNode.view.addSubview(self.sendButtonBackgroundView) self.sendContainerNode.addSubnode(self.sendButton) self.sendContainerNode.addSubnode(self.textNode) - self.view.addSubview(self.expandMediaInputButton) + self.view.addSubview(self.expandMediaInputButtonBackgroundView) self.expandMediaInputButton.highligthedChanged = { [weak self] highlighted in guard let self else { @@ -402,7 +401,7 @@ public final class ChatTextInputActionButtonsNode: ASDisplayNode, ChatSendMessag transition.updateFrame(view: self.expandMediaInputButton, frame: CGRect(origin: CGPoint(), size: size)) transition.updateFrame(view: self.expandMediaInputButtonBackgroundView, frame: CGRect(origin: CGPoint(), size: size)) - self.expandMediaInputButtonBackgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), transition: ComponentTransition(transition)) + self.expandMediaInputButtonBackgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), isInteractive: true, transition: ComponentTransition(transition)) if let image = self.expandMediaInputButtonIcon.image { let expandIconFrame = CGRect(origin: CGPoint(x: floor((size.width - image.size.width) * 0.5), y: floor((size.height - image.size.height) * 0.5)), size: image.size) self.expandMediaInputButtonIcon.center = expandIconFrame.center diff --git a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelComponent.swift b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelComponent.swift index 88c90e22..1a0b15a8 100644 --- a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelComponent.swift +++ b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelComponent.swift @@ -491,7 +491,6 @@ public final class ChatTextInputPanelComponent: Component { pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, - importState: nil, threadData: nil, isGeneralThreadClosed: false, replyMessage: nil, @@ -793,7 +792,6 @@ public final class ChatTextInputPanelComponent: Component { pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, - importState: nil, threadData: nil, isGeneralThreadClosed: false, replyMessage: nil, diff --git a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift index 90366396..1f78c9fd 100644 --- a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift @@ -703,7 +703,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg self.sendActionButtons.micButtonBackgroundView.alpha = 0.0 self.sendActionButtons.micButton.alpha = 0.0 self.sendActionButtons.micButtonTintMaskView.alpha = 0.0 - self.sendActionButtons.expandMediaInputButton.alpha = 0.0 + self.sendActionButtons.expandMediaInputButtonBackgroundView.alpha = 0.0 self.mediaActionButtons = ChatTextInputActionButtonsNode(context: context, presentationInterfaceState: presentationInterfaceState, presentationContext: presentationContext, presentController: presentController) self.mediaActionButtons.sendContainerNode.alpha = 0.0 @@ -811,11 +811,9 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg if highlighted { self.attachmentButtonIcon.layer.removeAnimation(forKey: "opacity") self.attachmentButtonIcon.alpha = 0.4 - self.attachmentButtonIcon.layer.allowsGroupOpacity = true } else { self.attachmentButtonIcon.alpha = 1.0 self.attachmentButtonIcon.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) - self.attachmentButtonIcon.layer.allowsGroupOpacity = false } } } @@ -901,7 +899,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg self.mediaActionButtons.updateAccessibility() self.mediaActionButtons.expandMediaInputButton.addTarget(self, action: #selector(self.expandButtonPressed), for: .touchUpInside) - self.mediaActionButtons.expandMediaInputButton.alpha = 0.0 + self.mediaActionButtons.expandMediaInputButtonBackgroundView.alpha = 0.0 self.searchLayoutClearButton.highligthedChanged = { [weak self] highlighted in guard let self else { @@ -1174,6 +1172,9 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg self.touchDownGestureRecognizer = recognizer textInputNode.textView.accessibilityHint = self.textPlaceholderNode.attributedText?.string + + self.isAccessibilityContainer = true + self.accessibilityElements = [textInputNode.textView] } private func textFieldMaxHeight(_ maxHeight: CGFloat, metrics: LayoutMetrics, bottomInset: CGFloat) -> CGFloat { @@ -2437,8 +2438,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg self.attachmentButtonBackground.contentView.addSubview(dotAnimationView) dotAnimationView.frame = dotAnimationSize.centered(in: self.attachmentButtonBackground.contentView.bounds) - self.attachmentButtonIcon.layer.opacity = 0.0 - self.attachmentButtonIcon.layer.transform = CATransform3DMakeScale(0.001, 0.001, 1.0) + self.attachmentButtonIcon.isHidden = true dotAnimationView.playOnce(completion: { [weak self, weak dotAnimationView] in guard let self else { return @@ -2453,8 +2453,9 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg transition.setScale(view: dotAnimationView, scale: 0.001) } - transition.setAlpha(view: self.attachmentButtonIcon, alpha: 1.0) - transition.setScale(view: self.attachmentButtonIcon, scale: 1.0) + self.attachmentButtonIcon.isHidden = false + transition.animateAlpha(view: self.attachmentButtonIcon, from: 0.0, to: 1.0) + transition.animateScale(view: self.attachmentButtonIcon, from: 0.001, to: 1.0) }) } } @@ -2692,8 +2693,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg transition.updatePosition(layer: dotAnimationView.layer, position: self.attachmentButtonBackground.contentView.bounds.center) - self.attachmentButtonIcon.layer.opacity = 0.0 - self.attachmentButtonIcon.layer.transform = CATransform3DMakeScale(0.001, 0.001, 1.0) + self.attachmentButtonIcon.isHidden = true dotAnimationView.playOnce(completion: { [weak self, weak dotAnimationView] in guard let self else { return @@ -2708,8 +2708,9 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg transition.setScale(view: dotAnimationView, scale: 0.001) } - transition.setAlpha(view: self.attachmentButtonIcon, alpha: 1.0) - transition.setScale(view: self.attachmentButtonIcon, scale: 1.0) + self.attachmentButtonIcon.isHidden = false + transition.animateAlpha(view: self.attachmentButtonIcon, from: 0.0, to: 1.0) + transition.animateScale(view: self.attachmentButtonIcon, from: 0.001, to: 1.0) }) } } else { @@ -4463,15 +4464,15 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg } if mediaInputIsActive && !hideExpandMediaInput { - if self.mediaActionButtons.expandMediaInputButton.alpha.isZero { - self.mediaActionButtons.expandMediaInputButton.alpha = 1.0 + if self.mediaActionButtons.expandMediaInputButtonBackgroundView.alpha.isZero { + self.mediaActionButtons.expandMediaInputButtonBackgroundView.alpha = 1.0 if alphaTransition.isAnimated { - self.mediaActionButtons.expandMediaInputButton.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + self.mediaActionButtons.expandMediaInputButtonBackgroundView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) } } } else { - if !self.mediaActionButtons.expandMediaInputButton.alpha.isZero { - alphaTransition.updateAlpha(layer: self.mediaActionButtons.expandMediaInputButton.layer, alpha: 0.0) + if !self.mediaActionButtons.expandMediaInputButtonBackgroundView.alpha.isZero { + alphaTransition.updateAlpha(layer: self.mediaActionButtons.expandMediaInputButtonBackgroundView.layer, alpha: 0.0) } } diff --git a/submodules/TelegramUI/Components/Chat/ChatUserInfoItem/Sources/ChatUserInfoItem.swift b/submodules/TelegramUI/Components/Chat/ChatUserInfoItem/Sources/ChatUserInfoItem.swift index c9bab92d..3b80c51c 100644 --- a/submodules/TelegramUI/Components/Chat/ChatUserInfoItem/Sources/ChatUserInfoItem.swift +++ b/submodules/TelegramUI/Components/Chat/ChatUserInfoItem/Sources/ChatUserInfoItem.swift @@ -170,7 +170,7 @@ public final class ChatUserInfoItemNode: ListViewItemNode, ASGestureRecognizerDe self.disclaimerTextNode.textNode.isUserInteractionEnabled = false self.disclaimerTextNode.textNode.displaysAsynchronously = false - super.init(layerBacked: false, dynamicBounce: true, rotated: true) + super.init(layerBacked: false, rotated: true) self.transform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) diff --git a/submodules/TelegramUI/Components/Chat/EditableTokenListNode/BUILD b/submodules/TelegramUI/Components/Chat/EditableTokenListNode/BUILD index cb19c19e..4b23fb12 100644 --- a/submodules/TelegramUI/Components/Chat/EditableTokenListNode/BUILD +++ b/submodules/TelegramUI/Components/Chat/EditableTokenListNode/BUILD @@ -16,6 +16,9 @@ swift_library( "//submodules/TelegramPresentationData", "//submodules/AvatarNode", "//submodules/AccountContext", + "//submodules/TelegramUI/Components/GlassBackgroundComponent", + "//submodules/ComponentFlow", + "//submodules/Components/ComponentDisplayAdapters", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Chat/EditableTokenListNode/Sources/EditableTokenListNode.swift b/submodules/TelegramUI/Components/Chat/EditableTokenListNode/Sources/EditableTokenListNode.swift index a2ec801b..20782373 100644 --- a/submodules/TelegramUI/Components/Chat/EditableTokenListNode/Sources/EditableTokenListNode.swift +++ b/submodules/TelegramUI/Components/Chat/EditableTokenListNode/Sources/EditableTokenListNode.swift @@ -6,6 +6,9 @@ import TelegramCore import TelegramPresentationData import AvatarNode import AccountContext +import GlassBackgroundComponent +import ComponentFlow +import ComponentDisplayAdapters public struct EditableTokenListToken { public enum Subject { @@ -26,23 +29,6 @@ public struct EditableTokenListToken { } } -private let caretIndicatorImage = generateVerticallyStretchableFilledCircleImage(radius: 1.0, color: UIColor(rgb: 0x3350ee)) - -private func caretAnimation() -> CAAnimation { - let animation = CAKeyframeAnimation(keyPath: "opacity") - animation.values = [1.0 as NSNumber, 0.0 as NSNumber, 1.0 as NSNumber, 1.0 as NSNumber] - let firstDuration = 0.3 - let secondDuration = 0.25 - let restDuration = 0.35 - let duration = firstDuration + secondDuration + restDuration - let keyTimes: [NSNumber] = [0.0 as NSNumber, (firstDuration / duration) as NSNumber, ((firstDuration + secondDuration) / duration) as NSNumber, ((firstDuration + secondDuration + restDuration) / duration) as NSNumber] - - animation.keyTimes = keyTimes - animation.duration = duration - animation.repeatCount = Float.greatestFiniteMagnitude - return animation -} - private func generateRemoveIcon(_ color: UIColor) -> UIImage? { return generateImage(CGSize(width: 22.0, height: 22.0), rotatedContext: { size, context in context.clear(CGRect(origin: .zero, size: size)) @@ -61,35 +47,10 @@ private func generateRemoveIcon(_ color: UIColor) -> UIImage? { }) } -public final class EditableTokenListNodeTheme { - public let backgroundColor: UIColor - public let separatorColor: UIColor - public let placeholderTextColor: UIColor - public let primaryTextColor: UIColor - public let tokenBackgroundColor: UIColor - public let selectedTextColor: UIColor - public let selectedBackgroundColor: UIColor - public let accentColor: UIColor - public let keyboardColor: PresentationThemeKeyboardColor - - public init(backgroundColor: UIColor, separatorColor: UIColor, placeholderTextColor: UIColor, primaryTextColor: UIColor, tokenBackgroundColor: UIColor, selectedTextColor: UIColor, selectedBackgroundColor: UIColor, accentColor: UIColor, keyboardColor: PresentationThemeKeyboardColor) { - self.backgroundColor = backgroundColor - self.separatorColor = separatorColor - self.placeholderTextColor = placeholderTextColor - self.primaryTextColor = primaryTextColor - self.tokenBackgroundColor = tokenBackgroundColor - self.selectedTextColor = selectedTextColor - self.selectedBackgroundColor = selectedBackgroundColor - self.accentColor = accentColor - self.keyboardColor = keyboardColor - } -} - private final class TokenNode: ASDisplayNode { private let context: AccountContext - private let presentationTheme: PresentationTheme + private let theme: PresentationTheme - let theme: EditableTokenListNodeTheme let token: EditableTokenListToken let avatarNode: AvatarNode let categoryAvatarNode: ASImageNode @@ -98,23 +59,9 @@ private final class TokenNode: ASDisplayNode { let backgroundNode: ASImageNode let selectedBackgroundNode: ASImageNode var isSelected: Bool = false - // didSet { - // if self.isSelected != oldValue { - // self.titleNode.attributedText = NSAttributedString(string: token.title, font: Font.regular(14.0), textColor: self.isSelected ? self.theme.selectedTextColor : self.theme.primaryTextColor) - // self.titleNode.redrawIfPossible() - // self.backgroundNode.isHidden = self.isSelected - // self.selectedBackgroundNode.isHidden = !self.isSelected - // - // self.avatarNode.isHidden = self.isSelected - // self.categoryAvatarNode.isHidden = self.isSelected - // self.removeIconNode.isHidden = !self.isSelected - // } - // } - // } - init(context: AccountContext, presentationTheme: PresentationTheme, theme: EditableTokenListNodeTheme, token: EditableTokenListToken, isSelected: Bool) { + init(context: AccountContext, theme: PresentationTheme, token: EditableTokenListToken, isSelected: Bool) { self.context = context - self.presentationTheme = presentationTheme self.theme = theme self.token = token self.titleNode = ASTextNode() @@ -131,39 +78,39 @@ private final class TokenNode: ASDisplayNode { self.removeIconNode.alpha = 0.0 self.removeIconNode.displaysAsynchronously = false self.removeIconNode.displayWithoutProcessing = true - self.removeIconNode.image = generateRemoveIcon(theme.selectedTextColor) + self.removeIconNode.image = generateRemoveIcon(theme.list.itemCheckColors.foregroundColor) - let cornerRadius: CGFloat + let cornerDiameter: CGFloat switch token.subject { case .peer: - cornerRadius = 24.0 + cornerDiameter = 28.0 case .category: - cornerRadius = 14.0 + cornerDiameter = 14.0 } self.backgroundNode = ASImageNode() self.backgroundNode.displaysAsynchronously = false self.backgroundNode.displayWithoutProcessing = true - self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: cornerRadius, color: theme.tokenBackgroundColor) + self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: cornerDiameter, color: theme.list.itemCheckColors.strokeColor.withAlphaComponent(0.25)) self.selectedBackgroundNode = ASImageNode() self.selectedBackgroundNode.alpha = 0.0 self.selectedBackgroundNode.displaysAsynchronously = false self.selectedBackgroundNode.displayWithoutProcessing = true - self.selectedBackgroundNode.image = generateStretchableFilledCircleImage(diameter: cornerRadius, color: theme.selectedBackgroundColor) + self.selectedBackgroundNode.image = generateStretchableFilledCircleImage(diameter: cornerDiameter, color: theme.list.itemCheckColors.fillColor) super.init() self.addSubnode(self.backgroundNode) self.addSubnode(self.selectedBackgroundNode) - self.titleNode.attributedText = NSAttributedString(string: token.title, font: Font.regular(14.0), textColor: self.isSelected ? self.theme.selectedTextColor : self.theme.primaryTextColor) + self.titleNode.attributedText = NSAttributedString(string: token.title, font: Font.regular(15.0), textColor: self.isSelected ? self.theme.list.itemCheckColors.foregroundColor : self.theme.list.itemPrimaryTextColor) self.addSubnode(self.titleNode) self.addSubnode(self.removeIconNode) switch token.subject { case let .peer(peer): self.addSubnode(self.avatarNode) - self.avatarNode.setPeer(context: context, theme: presentationTheme, peer: peer) + self.avatarNode.setPeer(context: context, theme: theme, peer: peer) case let .category(image): self.addSubnode(self.categoryAvatarNode) self.categoryAvatarNode.image = image @@ -182,9 +129,9 @@ private final class TokenNode: ASDisplayNode { if titleSize.width.isZero { return } - self.backgroundNode.frame = self.bounds.insetBy(dx: 2.0, dy: 2.0) - self.selectedBackgroundNode.frame = self.bounds.insetBy(dx: 2.0, dy: 2.0) - self.avatarNode.frame = CGRect(origin: CGPoint(x: 3.0, y: 3.0), size: CGSize(width: 22.0, height: 22.0)) + self.backgroundNode.frame = self.bounds + self.selectedBackgroundNode.frame = self.bounds + self.avatarNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: self.bounds.height, height: self.bounds.height)) self.categoryAvatarNode.frame = self.avatarNode.frame self.removeIconNode.frame = self.avatarNode.frame @@ -243,91 +190,70 @@ private final class TokenNode: ASDisplayNode { } } - self.titleNode.attributedText = NSAttributedString(string: token.title, font: Font.regular(14.0), textColor: self.isSelected ? self.theme.selectedTextColor : self.theme.primaryTextColor) + self.titleNode.attributedText = NSAttributedString(string: token.title, font: Font.regular(15.0), textColor: self.isSelected ? self.theme.list.itemCheckColors.foregroundColor : self.theme.list.itemPrimaryTextColor) self.titleNode.redrawIfPossible() } } -private final class CaretIndicatorNode: ASImageNode { - override func willEnterHierarchy() { - super.willEnterHierarchy() - - if self.layer.animation(forKey: "blink") == nil { - self.layer.add(caretAnimation(), forKey: "blink") - } - } -} - public final class EditableTokenListNode: ASDisplayNode, UITextFieldDelegate { private let context: AccountContext - private let presentationTheme: PresentationTheme + private let theme: PresentationTheme private let placeholder: String private let shortPlaceholder: String? - private let theme: EditableTokenListNodeTheme - private let backgroundNode: NavigationBackgroundNode + private let backgroundContainer: GlassBackgroundContainerView + private let backgroundView: GlassBackgroundView private let scrollNode: ASScrollNode private let placeholderNode: ASTextNode private var tokenNodes: [TokenNode] = [] - private let separatorNode: ASDisplayNode private let textFieldScrollNode: ASScrollNode private let textFieldNode: TextFieldNode - private let caretIndicatorNode: CaretIndicatorNode private var selectedTokenId: AnyHashable? public var textUpdated: ((String) -> Void)? public var deleteToken: ((AnyHashable) -> Void)? public var textReturned: (() -> Void)? - public init(context: AccountContext, presentationTheme: PresentationTheme, theme: EditableTokenListNodeTheme, placeholder: String, shortPlaceholder: String? = nil) { + public init(context: AccountContext, theme: PresentationTheme, placeholder: String, shortPlaceholder: String? = nil) { self.context = context - self.presentationTheme = presentationTheme self.theme = theme self.placeholder = placeholder self.shortPlaceholder = shortPlaceholder - self.backgroundNode = NavigationBackgroundNode(color: theme.backgroundColor) + self.backgroundContainer = GlassBackgroundContainerView() + self.backgroundView = GlassBackgroundView() + self.backgroundContainer.contentView.addSubview(self.backgroundView) self.scrollNode = ASScrollNode() - self.scrollNode.view.alwaysBounceVertical = true + self.scrollNode.view.alwaysBounceVertical = false + self.scrollNode.clipsToBounds = true self.placeholderNode = ASTextNode() self.placeholderNode.isUserInteractionEnabled = false self.placeholderNode.maximumNumberOfLines = 1 - self.placeholderNode.attributedText = NSAttributedString(string: placeholder, font: Font.regular(15.0), textColor: theme.placeholderTextColor) + self.placeholderNode.attributedText = NSAttributedString(string: placeholder, font: Font.regular(15.0), textColor: theme.list.itemPlaceholderTextColor) self.textFieldScrollNode = ASScrollNode() self.textFieldNode = TextFieldNode() self.textFieldNode.textField.font = Font.regular(15.0) - self.textFieldNode.textField.textColor = theme.primaryTextColor + self.textFieldNode.textField.textColor = theme.list.itemPrimaryTextColor self.textFieldNode.textField.autocorrectionType = .no self.textFieldNode.textField.returnKeyType = .done - self.textFieldNode.textField.keyboardAppearance = theme.keyboardColor.keyboardAppearance - self.textFieldNode.textField.tintColor = theme.accentColor - - self.caretIndicatorNode = CaretIndicatorNode() - self.caretIndicatorNode.isLayerBacked = true - self.caretIndicatorNode.displayWithoutProcessing = true - self.caretIndicatorNode.displaysAsynchronously = false - self.caretIndicatorNode.image = caretIndicatorImage - - self.separatorNode = ASDisplayNode() - self.separatorNode.isLayerBacked = true - self.separatorNode.backgroundColor = theme.separatorColor + self.textFieldNode.textField.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance + self.textFieldNode.textField.tintColor = theme.list.itemAccentColor super.init() - self.addSubnode(self.backgroundNode) + + self.view.addSubview(self.backgroundContainer) + self.addSubnode(self.scrollNode) - self.addSubnode(self.separatorNode) self.scrollNode.addSubnode(self.placeholderNode) self.scrollNode.addSubnode(self.textFieldScrollNode) self.textFieldScrollNode.addSubnode(self.textFieldNode) - //self.scrollNode.addSubnode(self.caretIndicatorNode) - self.clipsToBounds = true self.textFieldNode.textField.delegate = self self.textFieldNode.textField.addTarget(self, action: #selector(self.textFieldChanged(_:)), for: .editingChanged) @@ -357,7 +283,7 @@ public final class EditableTokenListNode: ASDisplayNode, UITextFieldDelegate { placeholderSnapshot = self.placeholderNode.layer.snapshotContentTreeAsView() placeholderSnapshot?.frame = self.placeholderNode.frame } - self.placeholderNode.attributedText = NSAttributedString(string: placeholder, font: Font.regular(15.0), textColor: self.theme.placeholderTextColor) + self.placeholderNode.attributedText = NSAttributedString(string: placeholder, font: Font.regular(15.0), textColor: self.theme.list.itemPlaceholderTextColor) } for i in (0 ..< self.tokenNodes.count).reversed() { @@ -380,8 +306,7 @@ public final class EditableTokenListNode: ASDisplayNode, UITextFieldDelegate { } let sideInset: CGFloat = 12.0 + leftInset - let verticalInset: CGFloat = 6.0 - + let verticalInset: CGFloat = 8.0 var animationDelay = 0.0 var currentOffset = CGPoint(x: sideInset, y: verticalInset) @@ -398,7 +323,7 @@ public final class EditableTokenListNode: ASDisplayNode, UITextFieldDelegate { if let currentNode = currentNode { tokenNode = currentNode } else { - tokenNode = TokenNode(context: self.context, presentationTheme: self.presentationTheme, theme: self.theme, token: token, isSelected: self.selectedTokenId != nil && token.id == self.selectedTokenId!) + tokenNode = TokenNode(context: self.context, theme: self.theme, token: token, isSelected: self.selectedTokenId != nil && token.id == self.selectedTokenId!) self.tokenNodes.append(tokenNode) self.scrollNode.addSubnode(tokenNode) animateIn = true @@ -407,10 +332,10 @@ public final class EditableTokenListNode: ASDisplayNode, UITextFieldDelegate { let tokenSize = tokenNode.measure(CGSize(width: max(1.0, width - sideInset - sideInset), height: CGFloat.greatestFiniteMagnitude)) if tokenSize.width + currentOffset.x >= width - sideInset && !currentOffset.x.isEqual(to: sideInset) { currentOffset.x = sideInset - currentOffset.y += tokenSize.height + currentOffset.y += tokenSize.height + 6.0 } let tokenFrame = CGRect(origin: CGPoint(x: currentOffset.x, y: currentOffset.y), size: tokenSize) - currentOffset.x += ceil(tokenSize.width) + currentOffset.x += ceil(tokenSize.width) + 6.0 if animateIn { tokenNode.frame = tokenFrame @@ -451,7 +376,7 @@ public final class EditableTokenListNode: ASDisplayNode, UITextFieldDelegate { let placeholderSize = self.placeholderNode.measure(CGSize(width: max(1.0, width - sideInset - sideInset), height: CGFloat.greatestFiniteMagnitude)) if width - currentOffset.x < placeholderSize.width { - currentOffset.y += 28.0 + currentOffset.y += 28.0 + 6.0 currentOffset.x = sideInset } @@ -472,42 +397,45 @@ public final class EditableTokenListNode: ASDisplayNode, UITextFieldDelegate { } let textNodeFrame = CGRect(origin: CGPoint(x: currentOffset.x + 4.0, y: currentOffset.y + UIScreenPixel), size: CGSize(width: width - currentOffset.x - sideInset - 8.0, height: 28.0)) - let caretNodeFrame = CGRect(origin: CGPoint(x: textNodeFrame.minX, y: textNodeFrame.minY + 4.0 - UIScreenPixel), size: CGSize(width: 2.0, height: 19.0 + UIScreenPixel)) if case .immediate = transition { transition.updateFrame(node: self.textFieldScrollNode, frame: textNodeFrame) transition.updateFrame(node: self.textFieldNode, frame: CGRect(origin: CGPoint(), size: textNodeFrame.size)) - transition.updateFrame(node: self.caretIndicatorNode, frame: caretNodeFrame) } else { let previousFrame = self.textFieldScrollNode.frame self.textFieldScrollNode.frame = textNodeFrame self.textFieldScrollNode.layer.animateFrame(from: previousFrame, to: textNodeFrame, duration: 0.2 + animationDelay, timingFunction: kCAMediaTimingFunctionSpring) transition.updateFrame(node: self.textFieldNode, frame: CGRect(origin: CGPoint(), size: textNodeFrame.size)) - - let previousCaretFrame = self.caretIndicatorNode.frame - self.caretIndicatorNode.frame = caretNodeFrame - self.caretIndicatorNode.layer.animateFrame(from: previousCaretFrame, to: caretNodeFrame, duration: 0.2 + animationDelay, timingFunction: kCAMediaTimingFunctionSpring) } let previousContentHeight = self.scrollNode.view.contentSize.height - let contentHeight = currentOffset.y + 29.0 + verticalInset + let contentHeight = currentOffset.y + 28.0 + verticalInset let nodeHeight = min(contentHeight, 110.0) - transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: UIScreenPixel))) transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: nodeHeight))) + transition.updateCornerRadius(node: self.scrollNode, cornerRadius: min(44.0, nodeHeight) * 0.5) + self.scrollNode.view.scrollIndicatorInsets = UIEdgeInsets(top: 16.0, left: 0.0, bottom: 16.0, right: 0.0) if !abs(previousContentHeight - contentHeight).isLess(than: CGFloat.ulpOfOne) { let contentOffset = CGPoint(x: 0.0, y: max(0.0, contentHeight - nodeHeight)) - if case .immediate = transition { - self.scrollNode.view.contentOffset = contentOffset - } else { - transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(x: 0.0, y: contentOffset.y), size: self.scrollNode.bounds.size)) + if self.scrollNode.view.contentOffset != contentOffset { + if case .immediate = transition { + self.scrollNode.view.contentOffset = contentOffset + } else { + //transition.animateOffsetAdditive(node: self.scrollNode, offset: self.scrollNode.view.contentOffset.y - contentOffset.y) + } } } - self.scrollNode.view.contentSize = CGSize(width: width, height: contentHeight) + if self.scrollNode.view.contentSize != CGSize(width: width, height: contentHeight) { + self.scrollNode.view.contentSize = CGSize(width: width, height: contentHeight) + } - transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: nodeHeight))) - self.backgroundNode.update(size: self.backgroundNode.bounds.size, transition: transition) + let backgroundFrame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: nodeHeight)) + self.backgroundContainer.update(size: backgroundFrame.size, isDark: self.theme.overallDarkAppearance, transition: ComponentTransition(transition)) + transition.updateFrame(view: self.backgroundContainer, frame: backgroundFrame) + + self.backgroundView.update(size: backgroundFrame.size, cornerRadius: min(44.0, backgroundFrame.height) * 0.5, isDark: self.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: ComponentTransition(transition)) + transition.updateFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size)) return nodeHeight } @@ -528,15 +456,9 @@ public final class EditableTokenListNode: ASDisplayNode, UITextFieldDelegate { } public func textFieldDidBeginEditing(_ textField: UITextField) { - /*if self.caretIndicatorNode.supernode == self { - self.caretIndicatorNode.removeFromSupernode() - }*/ } public func textFieldDidEndEditing(_ textField: UITextField) { - /*if self.caretIndicatorNode.supernode != self.scrollNode { - self.scrollNode.addSubnode(self.caretIndicatorNode) - }*/ } public func setText(_ text: String) { diff --git a/submodules/TelegramUI/Components/Chat/FactCheckAlertController/BUILD b/submodules/TelegramUI/Components/Chat/FactCheckAlertController/BUILD index 15cf263a..09c44b10 100644 --- a/submodules/TelegramUI/Components/Chat/FactCheckAlertController/BUILD +++ b/submodules/TelegramUI/Components/Chat/FactCheckAlertController/BUILD @@ -21,8 +21,9 @@ swift_library( "//submodules/Components/MultilineTextComponent", "//submodules/Components/BalancedTextComponent", "//submodules/Components/ComponentDisplayAdapters", - "//submodules/TelegramUI/Components/TextFieldComponent", "//submodules/TextFormat", + "//submodules/TelegramUI/Components/AlertComponent", + "//submodules/TelegramUI/Components/AlertComponent/AlertMultilineInputFieldComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Chat/FactCheckAlertController/Sources/FactCheckAlertController.swift b/submodules/TelegramUI/Components/Chat/FactCheckAlertController/Sources/FactCheckAlertController.swift index 0df9fdc4..fb4c4a6c 100644 --- a/submodules/TelegramUI/Components/Chat/FactCheckAlertController/Sources/FactCheckAlertController.swift +++ b/submodules/TelegramUI/Components/Chat/FactCheckAlertController/Sources/FactCheckAlertController.swift @@ -13,399 +13,109 @@ import BalancedTextComponent import TextFieldComponent import ComponentDisplayAdapters import TextFormat +import ComponentFlow +import AlertComponent +import AlertMultilineInputFieldComponent -private final class FactCheckAlertContentNode: AlertContentNode { - private let context: AccountContext - private var theme: AlertControllerTheme - private var presentationTheme: PresentationTheme - private let strings: PresentationStrings - private let text: String - private let initialValue: String - - private let titleView = ComponentView() +public func factCheckAlertController( + context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, + value: String, + entities: [MessageTextEntity], + apply: @escaping (String, [MessageTextEntity]) -> Void +) -> ViewController { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let strings = presentationData.strings + + let inputState = AlertMultilineInputFieldComponent.ExternalState() - private let state = ComponentState() - - private let inputBackgroundNode = ASImageNode() - private let inputField = ComponentView() - private let inputFieldExternalState = TextFieldComponent.ExternalState() - private let inputPlaceholderView = ComponentView() - - private let actionNodesSeparator: ASDisplayNode - private let actionNodes: [TextAlertContentActionNode] - private let actionVerticalSeparators: [ASDisplayNode] - - private let disposable = MetaDisposable() - - private var validLayout: CGSize? - - private let hapticFeedback = HapticFeedback() - - var present: (ViewController) -> () = { _ in } - - var complete: (() -> Void)? { - didSet { -// self.inputFieldNode.complete = self.complete + let doneIsEnabled: Signal + if !value.isEmpty { + doneIsEnabled = .single(true) + } else { + doneIsEnabled = inputState.valueSignal + |> map { value in + return !value.string.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty } } - override var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled + var characterLimit: Int = 1024 + if let data = context.currentAppConfiguration.with({ $0 }).data, let value = data["factcheck_length_limit"] as? Double { + characterLimit = Int(value) } - init(context: AccountContext, theme: AlertControllerTheme, presentationTheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction], text: String, value: String, entities: [MessageTextEntity]) { - self.context = context - self.theme = theme - self.presentationTheme = presentationTheme - self.strings = strings - self.text = text - self.initialValue = value - - if !value.isEmpty { - self.inputFieldExternalState.initialText = chatInputStateStringWithAppliedEntities(value, entities: entities) - } - - self.actionNodesSeparator = ASDisplayNode() - self.actionNodesSeparator.isLayerBacked = true - - self.actionNodes = actions.map { action -> TextAlertContentActionNode in - return TextAlertContentActionNode(theme: theme, action: action) - } - - var actionVerticalSeparators: [ASDisplayNode] = [] - if actions.count > 1 { - for _ in 0 ..< actions.count - 1 { - let separatorNode = ASDisplayNode() - separatorNode.isLayerBacked = true - actionVerticalSeparators.append(separatorNode) - } - } - self.actionVerticalSeparators = actionVerticalSeparators - - super.init() - - self.inputBackgroundNode.displaysAsynchronously = false - self.inputBackgroundNode.image = generateStretchableFilledCircleImage(diameter: 16.0, color: presentationTheme.actionSheet.inputHollowBackgroundColor, strokeColor: presentationTheme.actionSheet.inputBorderColor, strokeWidth: UIScreenPixel) - - self.addSubnode(self.actionNodesSeparator) - self.addSubnode(self.inputBackgroundNode) - - for actionNode in self.actionNodes { - self.addSubnode(actionNode) - } - self.actionNodes.last?.actionEnabled = true - - for separatorNode in self.actionVerticalSeparators { - self.addSubnode(separatorNode) - } - - self.updateTheme(theme) - - self.state._updated = { [weak self] transition, _ in - guard let self, let _ = self.validLayout else { - return - } - self.requestLayout?(transition.containedViewLayoutTransition) - } - } + let initialValue = chatInputStateStringWithAppliedEntities(value, entities: entities) - deinit { - self.disposable.dispose() - } - - var textAndEntities: (String, [MessageTextEntity]) { - let text = self.inputFieldExternalState.text.string - let entities = generateChatInputTextEntities(self.inputFieldExternalState.text) - return (text, entities) - } - - override func updateTheme(_ theme: AlertControllerTheme) { - self.theme = theme - - self.actionNodesSeparator.backgroundColor = theme.separatorColor - for actionNode in self.actionNodes { - actionNode.updateTheme(theme) - } - for separatorNode in self.actionVerticalSeparators { - separatorNode.backgroundColor = theme.separatorColor - } - - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) - } - } - - override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - var size = size - size.width = min(size.width, 270.0) - let measureSize = CGSize(width: size.width - 16.0 * 2.0, height: CGFloat.greatestFiniteMagnitude) - - let hadValidLayout = self.validLayout != nil - - self.validLayout = size - - var origin: CGPoint = CGPoint(x: 0.0, y: 16.0) - let spacing: CGFloat = 5.0 - - - let titleSize = self.titleView.update( - transition: .immediate, - component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: self.text, font: Font.semibold(17.0), textColor: self.theme.primaryColor)), - horizontalAlignment: .center, - maximumNumberOfLines: 0 - )), - environment: {}, - containerSize: CGSize(width: measureSize.width, height: 1000.0) + var presentImpl: ((ViewController) -> Void)? + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: strings.FactCheck_Title) ) - let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) * 0.5), y: origin.y), size: titleSize) - if let titleComponentView = self.titleView.view { - if titleComponentView.superview == nil { - self.view.addSubview(titleComponentView) - } - titleComponentView.frame = titleFrame - } - origin.y += titleSize.height + 17.0 - - let actionButtonHeight: CGFloat = 44.0 - var minActionsWidth: CGFloat = 0.0 - let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) - let actionTitleInsets: CGFloat = 8.0 - - var effectiveActionLayout = TextAlertContentActionLayout.horizontal - for actionNode in self.actionNodes { - let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) - if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 { - effectiveActionLayout = .vertical - } - switch effectiveActionLayout { - case .horizontal: - minActionsWidth += actionTitleSize.width + actionTitleInsets - case .vertical: - minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) - } - } - - let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 9.0, right: 18.0) - - var contentWidth = max(titleSize.width, minActionsWidth) - contentWidth = max(contentWidth, 234.0) - - var actionsHeight: CGFloat = 0.0 - switch effectiveActionLayout { - case .horizontal: - actionsHeight = actionButtonHeight - case .vertical: - actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) - } - - let resultWidth = contentWidth + insets.left + insets.right - - let inputInset: CGFloat = 16.0 - let inputWidth = resultWidth - inputInset * 2.0 - - var characterLimit: Int = 1024 - if let data = self.context.currentAppConfiguration.with({ $0 }).data, let value = data["factcheck_length_limit"] as? Double { - characterLimit = Int(value) - } - - let inputFieldSize = self.inputField.update( - transition: .immediate, - component: AnyComponent(TextFieldComponent( - context: self.context, - theme: self.presentationTheme, - strings: self.strings, - externalState: self.inputFieldExternalState, - fontSize: 14.0, - textColor: self.presentationTheme.actionSheet.inputTextColor, - accentColor: self.presentationTheme.actionSheet.controlAccentColor, - insets: UIEdgeInsets(top: 8.0, left: 2.0, bottom: 8.0, right: 2.0), - hideKeyboard: false, - customInputView: nil, - resetText: nil, - isOneLineWhenUnfocused: false, + )) + content.append(AnyComponentWithIdentity( + id: "input", + component: AnyComponent( + AlertMultilineInputFieldComponent( + context: context, + initialValue: initialValue, + placeholder: strings.FactCheck_Placeholder, characterLimit: characterLimit, + formatMenuAvailability: .available([.bold, .italic]), emptyLineHandling: .oneConsecutive, - formatMenuAvailability: .available([.bold, .italic, .link]), - returnKeyType: .default, - lockedFormatAction: { - }, - present: { [weak self] c in - self?.present(c) - }, - paste: { _ in - }, - returnKeyAction: nil, - backspaceKeyAction: nil - )), - environment: {}, - containerSize: CGSize(width: inputWidth, height: 270.0) - ) - self.inputField.parentState = self.state - let inputFieldFrame = CGRect(origin: CGPoint(x: inputInset, y: origin.y), size: inputFieldSize) - if let inputFieldView = self.inputField.view as? TextFieldComponent.View { - if inputFieldView.superview == nil { - self.view.addSubview(inputFieldView) - } - transition.updateFrame(view: inputFieldView, frame: inputFieldFrame) - transition.updateFrame(node: self.inputBackgroundNode, frame: inputFieldFrame) - - if !hadValidLayout { - inputFieldView.activateInput() - } - } - - let inputPlaceholderSize = self.inputPlaceholderView.update( - transition: .immediate, - component: AnyComponent( - MultilineTextComponent(text: .plain(NSAttributedString( - string: self.strings.FactCheck_Placeholder, - font: Font.regular(14.0), - textColor: self.presentationTheme.actionSheet.inputPlaceholderColor - ))) - ), - environment: {}, - containerSize: CGSize(width: inputWidth, height: 240.0) - ) - let inputPlaceholderFrame = CGRect(origin: CGPoint(x: inputInset + 10.0, y: floorToScreenPixels(inputFieldFrame.midY - inputPlaceholderSize.height / 2.0)), size: inputPlaceholderSize) - if let inputPlaceholderView = self.inputPlaceholderView.view { - if inputPlaceholderView.superview == nil { - inputPlaceholderView.isUserInteractionEnabled = false - self.view.addSubview(inputPlaceholderView) - } - inputPlaceholderView.frame = inputPlaceholderFrame - inputPlaceholderView.isHidden = self.inputFieldExternalState.hasText - } - - if let lastActionNode = self.actionNodes.last { - if self.initialValue.isEmpty { - lastActionNode.actionEnabled = self.inputFieldExternalState.hasText - } else { - if self.inputFieldExternalState.hasText { - lastActionNode.action = TextAlertAction( - type: .defaultAction, - title: self.strings.Common_Done, - action: lastActionNode.action.action - ) - } else { - lastActionNode.action = TextAlertAction( - type: .defaultDestructiveAction, - title: self.strings.FactCheck_Remove, - action: lastActionNode.action.action - ) + isInitiallyFocused: true, + externalState: inputState, + present: { c in + presentImpl?(c) } - } + ) + ) + )) + + let doneIsRemove: Signal + if !value.isEmpty { + doneIsRemove = inputState.valueSignal + |> map { value in + return value.string.isEmpty } - - let resultSize = CGSize(width: resultWidth, height: titleSize.height + spacing + inputFieldSize.height + 17.0 + actionsHeight + insets.top + insets.bottom) - - transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - - var actionOffset: CGFloat = 0.0 - let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) - var separatorIndex = -1 - var nodeIndex = 0 - for actionNode in self.actionNodes { - if separatorIndex >= 0 { - let separatorNode = self.actionVerticalSeparators[separatorIndex] - switch effectiveActionLayout { - case .horizontal: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) - case .vertical: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - } - } - separatorIndex += 1 - - let currentActionWidth: CGFloat - switch effectiveActionLayout { - case .horizontal: - if nodeIndex == self.actionNodes.count - 1 { - currentActionWidth = resultSize.width - actionOffset - } else { - currentActionWidth = actionWidth - } - case .vertical: - currentActionWidth = resultSize.width - } - - let actionNodeFrame: CGRect - switch effectiveActionLayout { - case .horizontal: - actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += currentActionWidth - case .vertical: - actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += actionButtonHeight - } - - transition.updateFrame(node: actionNode, frame: actionNodeFrame) - - nodeIndex += 1 - } - - return resultSize + |> distinctUntilChanged + } else { + doneIsRemove = .single(false) } - func deactivateInput() { - if let inputFieldView = self.inputField.view as? TextFieldComponent.View { - inputFieldView.deactivateInput() - } + let actionsSignal: Signal<[AlertScreen.Action], NoError> = doneIsRemove + |> map { doneIsRemove in + var actions: [AlertScreen.Action] = [] + actions.append(.init(title: strings.Common_Cancel)) + + let doneTitle: String = doneIsRemove ? strings.FactCheck_Remove : strings.Common_Done + let doneType: AlertScreen.Action.ActionType = doneIsRemove ? .defaultDestructive : .default + actions.append( + .init(id: "done", title: doneTitle, type: doneType, action: { + let (text, entities) = inputState.textAndEntities + apply(text, entities) + }, isEnabled: doneIsEnabled) + ) + + return actions } - func animateError() { - if let inputFieldView = self.inputField.view as? TextFieldComponent.View { - inputFieldView.layer.addShakeAnimation() - } + var effectiveUpdatedPresentationData: (PresentationData, Signal) + if let updatedPresentationData { + effectiveUpdatedPresentationData = updatedPresentationData + } else { + effectiveUpdatedPresentationData = (presentationData, context.sharedContext.presentationData) + } - self.hapticFeedback.error() + let alertController = AlertScreen( + configuration: AlertScreen.Configuration(allowInputInset: true), + contentSignal: .single(content), + actionsSignal: actionsSignal, + updatedPresentationData: effectiveUpdatedPresentationData + ) + presentImpl = { [weak alertController] c in + alertController?.present(c, in: .window(.root)) } -} - -public func factCheckAlertController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, value: String, entities: [MessageTextEntity], apply: @escaping (String, [MessageTextEntity]) -> Void) -> AlertController { - let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } - - var dismissImpl: ((Bool) -> Void)? - var applyImpl: (() -> Void)? - - let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - dismissImpl?(true) - }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Done, action: { - dismissImpl?(true) - applyImpl?() - })] - - let contentNode = FactCheckAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), presentationTheme: presentationData.theme, strings: presentationData.strings, actions: actions, text: presentationData.strings.FactCheck_Title, value: value, entities: entities) - contentNode.complete = { - applyImpl?() - } - applyImpl = { [weak contentNode] in - guard let contentNode = contentNode else { - return - } - let (text, entities) = contentNode.textAndEntities - apply(text, entities) - } - - let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode) - let presentationDataDisposable = (updatedPresentationData?.signal ?? context.sharedContext.presentationData).start(next: { [weak controller] presentationData in - controller?.theme = AlertControllerTheme(presentationData: presentationData) - }) - controller.dismissed = { _ in - presentationDataDisposable.dispose() - } - dismissImpl = { [weak controller] animated in - contentNode.deactivateInput() - if animated { - controller?.dismissAnimated() - } else { - controller?.dismiss() - } - } - - contentNode.present = { [weak controller] c in - controller?.present(c, in: .window(.root)) - } - - return controller + return alertController } diff --git a/submodules/TelegramUI/Components/Chat/ForwardAccessoryPanelNode/BUILD b/submodules/TelegramUI/Components/Chat/ForwardAccessoryPanelNode/BUILD index 0faa5484..97ddeb8a 100644 --- a/submodules/TelegramUI/Components/Chat/ForwardAccessoryPanelNode/BUILD +++ b/submodules/TelegramUI/Components/Chat/ForwardAccessoryPanelNode/BUILD @@ -29,6 +29,8 @@ swift_library( "//submodules/TelegramUI/Components/AnimationCache", "//submodules/TelegramUI/Components/MultiAnimationRenderer", "//submodules/TelegramUI/Components/Chat/AccessoryPanelNode", + "//submodules/ComponentFlow", + "//submodules/TelegramUI/Components/AlertComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Chat/ForwardAccessoryPanelNode/Sources/ForwardAccessoryPanelNode.swift b/submodules/TelegramUI/Components/Chat/ForwardAccessoryPanelNode/Sources/ForwardAccessoryPanelNode.swift index a63706f2..21ed1416 100644 --- a/submodules/TelegramUI/Components/Chat/ForwardAccessoryPanelNode/Sources/ForwardAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ForwardAccessoryPanelNode/Sources/ForwardAccessoryPanelNode.swift @@ -20,6 +20,8 @@ import AnimationCache import MultiAnimationRenderer import AccessoryPanelNode import AppBundle +import ComponentFlow +import AlertComponent func textStringForForwardedMessage(_ message: Message, strings: PresentationStrings) -> (text: String, entities: [MessageTextEntity], isMedia: Bool) { for media in message.media { @@ -378,26 +380,48 @@ public final class ForwardAccessoryPanelNode: AccessoryPanelNode { string = self.strings.Conversation_ForwardOptions_Text(messages, peerDisplayTitle) } - let font = Font.regular(floor(self.fontSize.baseDisplaySize * 15.0 / 17.0)) - let boldFont = Font.semibold(floor(self.fontSize.baseDisplaySize * 15.0 / 17.0)) - let body = MarkdownAttributeSet(font: font, textColor: self.theme.actionSheet.secondaryTextColor) - let bold = MarkdownAttributeSet(font: boldFont, textColor: self.theme.actionSheet.secondaryTextColor) + let font = Font.regular(15.0) + let boldFont = Font.semibold(15.0) + let body = MarkdownAttributeSet(font: font, textColor: self.theme.actionSheet.primaryTextColor) + let bold = MarkdownAttributeSet(font: boldFont, textColor: self.theme.actionSheet.primaryTextColor) + let text = addAttributesToStringWithRanges(string._tuple, body: body, argumentAttributes: [0: bold, 1: bold], textAlignment: .natural) - let title = NSAttributedString(string: self.strings.Conversation_ForwardOptions_Title(messageCount), font: Font.semibold(floor(self.fontSize.baseDisplaySize)), textColor: self.theme.actionSheet.primaryTextColor, paragraphAlignment: .center) - let text = addAttributesToStringWithRanges(string._tuple, body: body, argumentAttributes: [0: bold, 1: bold], textAlignment: .center) + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent( + title: strings.Conversation_ForwardOptions_Title(messageCount) + ) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .attributed(text)) + ) + )) - let alertController = richTextAlertController(context: self.context, title: title, text: text, actions: [TextAlertAction(type: .genericAction, title: self.strings.Conversation_ForwardOptions_ShowOptions, action: { [weak self] in - if let strongSelf = self { - strongSelf.interfaceInteraction?.presentForwardOptions(strongSelf.view) - Queue.mainQueue().after(0.5) { - strongSelf.updateThemeAndStrings(theme: strongSelf.theme, strings: strongSelf.strings, forwardOptionsState: strongSelf.forwardOptionsState, force: true) - } - - let _ = ApplicationSpecificNotice.incrementChatForwardOptionsTip(accountManager: strongSelf.context.sharedContext.accountManager, count: 3).start() - } - }), TextAlertAction(type: .destructiveAction, title: self.strings.Conversation_ForwardOptions_CancelForwarding, action: { [weak self] in - self?.dismiss?() - })], actionLayout: .vertical) + let alertController = AlertScreen( + context: context, + configuration: AlertScreen.Configuration(actionAlignment: .vertical), + content: content, + actions: [ + .init(title: strings.Conversation_ForwardOptions_ShowOptions, action: { [weak self] in + guard let self else { + return + } + self.interfaceInteraction?.presentForwardOptions(self.view) + Queue.mainQueue().after(0.5) { + self.updateThemeAndStrings(theme: self.theme, strings: self.strings, forwardOptionsState: self.forwardOptionsState, force: true) + } + let _ = ApplicationSpecificNotice.incrementChatForwardOptionsTip(accountManager: self.context.sharedContext.accountManager, count: 3).start() + }), + .init(title: strings.Conversation_ForwardOptions_CancelForwarding, type: .destructive, action: { [weak self] in + self?.dismiss?() + }) + ] + ) self.interfaceInteraction?.presentController(alertController, nil) } diff --git a/submodules/TelegramUI/Components/Chat/ManagedDiceAnimationNode/Sources/ManagedDiceAnimationNode.swift b/submodules/TelegramUI/Components/Chat/ManagedDiceAnimationNode/Sources/ManagedDiceAnimationNode.swift index 7f38fa1e..d6e4efc8 100644 --- a/submodules/TelegramUI/Components/Chat/ManagedDiceAnimationNode/Sources/ManagedDiceAnimationNode.swift +++ b/submodules/TelegramUI/Components/Chat/ManagedDiceAnimationNode/Sources/ManagedDiceAnimationNode.swift @@ -100,7 +100,7 @@ public struct InteractiveEmojiConfiguration { } public static func with(appConfiguration: AppConfiguration) -> InteractiveEmojiConfiguration { - if let data = appConfiguration.data, let emojis = data["emojies_send_dice"] as? [String] { + if let data = appConfiguration.data, var emojis = data["emojies_send_dice"] as? [String] { var successParameters: [String: InteractiveEmojiSuccessParameters] = [:] if let success = data["emojies_send_dice_success"] as? [String: [String: Double]] { for (key, dict) in success { @@ -109,6 +109,11 @@ public struct InteractiveEmojiConfiguration { } } } + #if DEBUG + if !emojis.contains("🎲") { + emojis.append("🎲") + } + #endif return InteractiveEmojiConfiguration(emojis: emojis, successParameters: successParameters) } else { return .defaultValue @@ -126,6 +131,10 @@ public final class ManagedDiceAnimationNode: ManagedAnimationNode { private let configuration = Promise() private let emojis = Promise<[TelegramMediaFile]>() + public var isRolling: Bool { + return self.diceState == .rolling + } + public var success: (() -> Void)? public init(context: AccountContext, emoji: String) { diff --git a/submodules/TelegramUI/Components/Chat/ShimmeringLinkNode/Sources/ShimmeringLinkNode.swift b/submodules/TelegramUI/Components/Chat/ShimmeringLinkNode/Sources/ShimmeringLinkNode.swift index c70a1c79..efdc5c13 100644 --- a/submodules/TelegramUI/Components/Chat/ShimmeringLinkNode/Sources/ShimmeringLinkNode.swift +++ b/submodules/TelegramUI/Components/Chat/ShimmeringLinkNode/Sources/ShimmeringLinkNode.swift @@ -93,7 +93,7 @@ public final class ShimmeringLinkNode: ASDisplayNode { self.shimmerEffectNode.updateAbsoluteRect(CGRect(origin: .zero, size: size), within: size) self.borderShimmerEffectNode.updateAbsoluteRect(CGRect(origin: .zero, size: size), within: size) - self.shimmerEffectNode.update(backgroundColor: .clear, foregroundColor: self.color.withAlphaComponent(min(1.0, self.color.alpha * 1.2)), horizontal: true, effectSize: nil, globalTimeOffset: false, duration: nil) - self.borderShimmerEffectNode.update(backgroundColor: .clear, foregroundColor: self.color.withAlphaComponent(min(1.0, self.color.alpha * 1.5)), horizontal: true, effectSize: nil, globalTimeOffset: false, duration: nil) + self.shimmerEffectNode.update(backgroundColor: .clear, foregroundColor: self.color.withMultipliedAlpha(1.75), horizontal: true, effectSize: nil, globalTimeOffset: false, duration: nil) + self.borderShimmerEffectNode.update(backgroundColor: .clear, foregroundColor: self.color.withMultipliedAlpha(2.0), horizontal: true, effectSize: nil, globalTimeOffset: false, duration: nil) } } diff --git a/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift b/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift index 68555594..894a9d0f 100644 --- a/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift +++ b/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift @@ -313,6 +313,8 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol public var enableFullTranslucency: Bool = true public var chatIsRotated: Bool = true public var canReadHistory: Bool = false + public var summarizedMessageIds: Set = Set() + private var isOpeningMediaValue: Bool = false public var isOpeningMedia: Bool { diff --git a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift index 06933901..efac3288 100644 --- a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift +++ b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift @@ -33,6 +33,8 @@ import LegacyMessageInputPanelInputView import AttachmentTextInputPanelNode import GlassBackgroundComponent +private let keyboardCornerRadius: CGFloat = 30.0 + public final class EmptyInputView: UIView, UIInputViewAudioFeedback { public var enableInputClicksWhenVisible: Bool { return true @@ -490,7 +492,7 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { self.clippingView = UIView() self.clippingView.clipsToBounds = true - self.clippingView.layer.cornerRadius = 20.0 + self.clippingView.layer.cornerRadius = keyboardCornerRadius self.entityKeyboardView = ComponentHostView() @@ -1904,8 +1906,8 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { theme: interfaceState.theme, strings: interfaceState.strings, isContentInFocus: isVisible, - containerInsets: UIEdgeInsets(top: self.isEmojiSearchActive ? -34.0 : 0.0, left: leftInset, bottom: keyboardBottomInset, right: rightInset), - topPanelInsets: UIEdgeInsets(), + containerInsets: UIEdgeInsets(top: self.isEmojiSearchActive ? -42.0 : 0.0, left: leftInset, bottom: keyboardBottomInset, right: rightInset), + topPanelInsets: UIEdgeInsets(top: 0.0, left: 5.0, bottom: 0.0, right: 5.0), emojiContent: emojiContent, stickerContent: stickerContent, maskContent: nil, @@ -2030,16 +2032,16 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { backgroundFrame.size.height += 32.0 if backgroundChromeView.image == nil { - backgroundChromeView.image = GlassBackgroundView.generateForegroundImage(size: CGSize(width: 20.0 * 2.0, height: 20.0 * 2.0), isDark: interfaceState.theme.overallDarkAppearance, fillColor: .clear) + backgroundChromeView.image = GlassBackgroundView.generateForegroundImage(size: CGSize(width: keyboardCornerRadius * 2.0, height: keyboardCornerRadius * 2.0), isDark: interfaceState.theme.overallDarkAppearance, fillColor: .clear) } if backgroundTintView.image == nil { - backgroundTintView.image = generateStretchableFilledCircleImage(diameter: 20.0 * 2.0, color: .white)?.withRenderingMode(.alwaysTemplate) + backgroundTintView.image = generateStretchableFilledCircleImage(diameter: keyboardCornerRadius * 2.0, color: .white)?.withRenderingMode(.alwaysTemplate) } backgroundTintView.tintColor = interfaceState.theme.chat.inputMediaPanel.backgroundColor transition.updateFrame(view: backgroundView, frame: backgroundFrame) backgroundView.updateColor(color: .clear, forceKeepBlur: true, transition: .immediate) - backgroundView.update(size: backgroundFrame.size, cornerRadius: 20.0, maskedCorners: [.layerMinXMinYCorner, .layerMaxXMinYCorner], transition: transition) + backgroundView.update(size: backgroundFrame.size, cornerRadius: keyboardCornerRadius, maskedCorners: [.layerMinXMinYCorner, .layerMaxXMinYCorner], transition: transition) transition.updateFrame(view: backgroundChromeView, frame: backgroundFrame.insetBy(dx: -1.0, dy: 0.0)) @@ -2666,7 +2668,6 @@ public final class EntityInputView: UIInputView, AttachmentTextInputPanelInputVi pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, - importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, diff --git a/submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen/Sources/ChatFolderLinkPreviewScreen.swift b/submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen/Sources/ChatFolderLinkPreviewScreen.swift index 7b623f34..201a5677 100644 --- a/submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen/Sources/ChatFolderLinkPreviewScreen.swift +++ b/submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen/Sources/ChatFolderLinkPreviewScreen.swift @@ -1269,7 +1269,10 @@ private final class ChatFolderLinkPreviewScreenComponent: Component { if let controller = environment.controller() { let subLayout = ContainerViewLayout( - size: availableSize, metrics: environment.metrics, deviceMetrics: environment.deviceMetrics, intrinsicInsets: UIEdgeInsets(top: 0.0, left: sideInset - 12.0, bottom: bottomPanelHeight, right: sideInset), + size: availableSize, + metrics: environment.metrics, + deviceMetrics: environment.deviceMetrics, + intrinsicInsets: UIEdgeInsets(top: 0.0, left: sideInset - 12.0, bottom: bottomPanelHeight, right: sideInset), safeInsets: UIEdgeInsets(), additionalInsets: UIEdgeInsets(), statusBarHeight: nil, @@ -1513,7 +1516,7 @@ private final class ChatFolderLinkPreviewScreenComponent: Component { case .someUserTooManyChannels: text = presentationData.strings.ChatListFilter_CreateLinkErrorSomeoneHasChannelLimit } - controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + controller.present(textAlertController(context: component.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) }) } }) diff --git a/submodules/TelegramUI/Components/ChatList/ChatListFilterTabContainerNode/BUILD b/submodules/TelegramUI/Components/ChatList/ChatListFilterTabContainerNode/BUILD new file mode 100644 index 00000000..d5a45624 --- /dev/null +++ b/submodules/TelegramUI/Components/ChatList/ChatListFilterTabContainerNode/BUILD @@ -0,0 +1,27 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "ChatListFilterTabContainerNode", + module_name = "ChatListFilterTabContainerNode", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/TelegramCore", + "//submodules/TelegramPresentationData", + "//submodules/TelegramUI/Components/TextNodeWithEntities", + "//submodules/AccountContext", + "//submodules/ComponentFlow", + "//submodules/Components/ComponentDisplayAdapters", + "//submodules/TelegramUI/Components/GlassBackgroundComponent", + "//submodules/TelegramUI/Components/LiquidLens", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/ChatList/ChatListFilterTabContainerNode/Sources/ChatListFilterTabContainerNode.swift b/submodules/TelegramUI/Components/ChatList/ChatListFilterTabContainerNode/Sources/ChatListFilterTabContainerNode.swift new file mode 100644 index 00000000..b393b8da --- /dev/null +++ b/submodules/TelegramUI/Components/ChatList/ChatListFilterTabContainerNode/Sources/ChatListFilterTabContainerNode.swift @@ -0,0 +1,1127 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import TelegramCore +import TelegramPresentationData +import TextNodeWithEntities +import AccountContext +import GlassBackgroundComponent +import ComponentFlow +import ComponentDisplayAdapters + +private final class ItemNodeDeleteButtonNode: HighlightableButtonNode { + private let pressed: () -> Void + + private let contentImageNode: ASImageNode + + private var theme: PresentationTheme? + + init(pressed: @escaping () -> Void) { + self.pressed = pressed + + self.contentImageNode = ASImageNode() + + super.init() + + self.addSubnode(self.contentImageNode) + + self.addTarget(self, action: #selector(self.pressedEvent), forControlEvents: .touchUpInside) + } + + @objc private func pressedEvent() { + self.pressed() + } + + func update(theme: PresentationTheme) -> CGSize { + let size = CGSize(width: 18.0, height: 18.0) + if self.theme !== theme { + self.theme = theme + self.contentImageNode.image = generateImage(size, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(theme.rootController.navigationBar.clearButtonBackgroundColor.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(), size: size)) + context.setStrokeColor(theme.rootController.navigationBar.clearButtonForegroundColor.cgColor) + context.setLineWidth(1.5) + context.setLineCap(.round) + context.move(to: CGPoint(x: 6.38, y: 6.38)) + context.addLine(to: CGPoint(x: 11.63, y: 11.63)) + context.strokePath() + context.move(to: CGPoint(x: 6.38, y: 11.63)) + context.addLine(to: CGPoint(x: 11.63, y: 6.38)) + context.strokePath() + }) + } + + self.contentImageNode.frame = CGRect(origin: CGPoint(), size: size) + + return size + } +} + +private final class ItemNode: ASDisplayNode { + private let context: AccountContext + private let pressed: (Bool) -> Void + private let requestedDeletion: () -> Void + + private let extractedContainerNode: ContextExtractedContentContainingNode + private let containerNode: ContextControllerSourceNode + + private let extractedBackgroundNode: ASImageNode + private let titleContainer: ASDisplayNode + private let titleNode: ImmediateTextNodeWithEntities + private let titleActiveNode: ImmediateTextNodeWithEntities + private let shortTitleContainer: ASDisplayNode + private let shortTitleNode: ImmediateTextNodeWithEntities + private let shortTitleActiveNode: ImmediateTextNodeWithEntities + private let badgeContainerNode: ASDisplayNode + private let badgeTextNode: ImmediateTextNode + private let badgeBackgroundActiveNode: ASImageNode + private let badgeBackgroundInactiveNode: ASImageNode + + private var deleteButtonNode: ItemNodeDeleteButtonNode? + private let buttonNode: HighlightTrackingButtonNode + + private var selectionFraction: CGFloat = 0.0 + private(set) var unreadCount: Int = 0 + + private var isReordering: Bool = false + private var isEditing: Bool = false + private var isDisabled: Bool = false + + private var theme: PresentationTheme? + private var currentTitle: (ChatFolderTitle, ChatFolderTitle)? + + private var pointerInteraction: PointerInteraction? + + init(context: AccountContext, pressed: @escaping (Bool) -> Void, requestedDeletion: @escaping () -> Void, contextGesture: @escaping (ContextExtractedContentContainingNode, ContextGesture, Bool) -> Void) { + self.context = context + self.pressed = pressed + self.requestedDeletion = requestedDeletion + + self.extractedContainerNode = ContextExtractedContentContainingNode() + self.containerNode = ContextControllerSourceNode() + + self.extractedBackgroundNode = ASImageNode() + self.extractedBackgroundNode.alpha = 0.0 + + let titleInset: CGFloat = 4.0 + + self.titleContainer = ASDisplayNode() + + self.titleNode = ImmediateTextNodeWithEntities() + self.titleNode.displaysAsynchronously = false + self.titleNode.insets = UIEdgeInsets(top: titleInset, left: 0.0, bottom: titleInset, right: 0.0) + self.titleNode.resetEmojiToFirstFrameAutomatically = true + + self.titleActiveNode = ImmediateTextNodeWithEntities() + self.titleActiveNode.displaysAsynchronously = false + self.titleActiveNode.insets = UIEdgeInsets(top: titleInset, left: 0.0, bottom: titleInset, right: 0.0) + self.titleActiveNode.alpha = 0.0 + self.titleActiveNode.resetEmojiToFirstFrameAutomatically = true + + self.shortTitleContainer = ASDisplayNode() + + self.shortTitleNode = ImmediateTextNodeWithEntities() + self.shortTitleNode.displaysAsynchronously = false + self.shortTitleNode.alpha = 0.0 + self.shortTitleNode.insets = UIEdgeInsets(top: titleInset, left: 0.0, bottom: titleInset, right: 0.0) + self.shortTitleNode.resetEmojiToFirstFrameAutomatically = true + + self.shortTitleActiveNode = ImmediateTextNodeWithEntities() + self.shortTitleActiveNode.displaysAsynchronously = false + self.shortTitleActiveNode.alpha = 0.0 + self.shortTitleActiveNode.insets = UIEdgeInsets(top: titleInset, left: 0.0, bottom: titleInset, right: 0.0) + self.shortTitleActiveNode.alpha = 0.0 + self.shortTitleActiveNode.resetEmojiToFirstFrameAutomatically = true + + self.badgeContainerNode = ASDisplayNode() + + self.badgeTextNode = ImmediateTextNode() + self.badgeTextNode.displaysAsynchronously = false + + self.badgeBackgroundActiveNode = ASImageNode() + self.badgeBackgroundActiveNode.displaysAsynchronously = false + self.badgeBackgroundActiveNode.displayWithoutProcessing = true + + self.badgeBackgroundInactiveNode = ASImageNode() + self.badgeBackgroundInactiveNode.displaysAsynchronously = false + self.badgeBackgroundInactiveNode.displayWithoutProcessing = true + + self.buttonNode = HighlightTrackingButtonNode() + + super.init() + + self.extractedContainerNode.contentNode.addSubnode(self.extractedBackgroundNode) + self.extractedContainerNode.contentNode.addSubnode(self.titleContainer) + self.titleContainer.addSubnode(self.titleNode) + self.titleContainer.addSubnode(self.titleActiveNode) + self.extractedContainerNode.contentNode.addSubnode(self.shortTitleContainer) + self.shortTitleContainer.addSubnode(self.shortTitleNode) + self.shortTitleContainer.addSubnode(self.shortTitleActiveNode) + self.badgeContainerNode.addSubnode(self.badgeBackgroundInactiveNode) + self.badgeContainerNode.addSubnode(self.badgeBackgroundActiveNode) + self.badgeContainerNode.addSubnode(self.badgeTextNode) + self.extractedContainerNode.contentNode.addSubnode(self.badgeContainerNode) + self.extractedContainerNode.contentNode.addSubnode(self.buttonNode) + + self.containerNode.addSubnode(self.extractedContainerNode) + self.containerNode.targetNodeForActivationProgress = self.extractedContainerNode.contentNode + self.addSubnode(self.containerNode) + + self.buttonNode.isExclusiveTouch = true + self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside) + + self.containerNode.activated = { [weak self] gesture, _ in + guard let strongSelf = self else { + return + } + contextGesture(strongSelf.extractedContainerNode, gesture, strongSelf.isDisabled) + } + + self.extractedContainerNode.willUpdateIsExtractedToContextPreview = { [weak self] isExtracted, transition in + guard let strongSelf = self else { + return + } + + if isExtracted, let theme = strongSelf.theme { + strongSelf.extractedBackgroundNode.image = generateStretchableFilledCircleImage(diameter: 28.0, color: theme.contextMenu.backgroundColor) + } + transition.updateAlpha(node: strongSelf.extractedBackgroundNode, alpha: isExtracted ? 1.0 : 0.0, completion: { _ in + if !isExtracted { + self?.extractedBackgroundNode.image = nil + } + }) + } + } + + override func didLoad() { + super.didLoad() + + self.pointerInteraction = PointerInteraction(view: self.containerNode.view, customInteractionView: nil, style: .insetRectangle(-10.0, 4.0)) + } + + @objc private func buttonPressed() { + self.pressed(self.isDisabled) + } + + func updateText(strings: PresentationStrings, title: ChatFolderTitle, shortTitle: ChatFolderTitle, unreadCount: Int, unreadHasUnmuted: Bool, isNoFilter: Bool, selectionFraction: CGFloat, isEditing: Bool, isReordering: Bool, canReorderAllChats: Bool, isDisabled: Bool, presentationData: PresentationData, transition: ContainedViewLayoutTransition) { + self.isEditing = isEditing + self.isDisabled = isDisabled + + var themeUpdated = false + if self.theme !== presentationData.theme { + self.theme = presentationData.theme + + self.badgeBackgroundActiveNode.image = generateStretchableFilledCircleImage(diameter: 18.0, color: presentationData.theme.list.itemCheckColors.fillColor) + self.badgeBackgroundInactiveNode.image = generateStretchableFilledCircleImage(diameter: 18.0, color: presentationData.theme.chatList.unreadBadgeInactiveBackgroundColor) + + themeUpdated = true + } + + var titleUpdated = false + if self.currentTitle?.0 != title || self.currentTitle?.1 != shortTitle { + self.currentTitle = (title, shortTitle) + + titleUpdated = true + } + + var unreadCountUpdated = false + if self.unreadCount != unreadCount { + unreadCountUpdated = true + self.unreadCount = unreadCount + } + + self.buttonNode.accessibilityLabel = title.text + if unreadCount > 0 { + if self.buttonNode.accessibilityValue == nil || unreadCountUpdated { + self.buttonNode.accessibilityValue = strings.VoiceOver_Chat_UnreadMessages(Int32(unreadCount)) + } + } else { + self.buttonNode.accessibilityValue = "" + } + if selectionFraction == 1.0 { + self.buttonNode.accessibilityTraits = [.button, .selected] + } else { + self.buttonNode.accessibilityTraits = [.button] + } + + self.containerNode.isGestureEnabled = !isEditing && !isReordering + self.buttonNode.isUserInteractionEnabled = !isEditing && !isReordering + + self.selectionFraction = selectionFraction + self.unreadCount = unreadCount + + if isReordering && !isNoFilter { + if self.deleteButtonNode == nil { + let deleteButtonNode = ItemNodeDeleteButtonNode(pressed: { [weak self] in + self?.requestedDeletion() + }) + self.extractedContainerNode.contentNode.addSubnode(deleteButtonNode) + self.deleteButtonNode = deleteButtonNode + if case .animated = transition { + deleteButtonNode.layer.animateScale(from: 0.1, to: 1.0, duration: 0.25) + deleteButtonNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) + } + } + } else if let deleteButtonNode = self.deleteButtonNode { + self.deleteButtonNode = nil + transition.updateTransformScale(node: deleteButtonNode, scale: 0.1) + transition.updateAlpha(node: deleteButtonNode, alpha: 0.0, completion: { [weak deleteButtonNode] _ in + deleteButtonNode?.removeFromSupernode() + }) + } + + transition.updateAlpha(node: self.badgeContainerNode, alpha: (isEditing || isDisabled || isReordering || unreadCount == 0) ? 0.0 : 1.0) + + let selectionAlpha: CGFloat = selectionFraction * selectionFraction + let deselectionAlpha: CGFloat = isDisabled ? 0.5 : 1.0// - selectionFraction + + transition.updateAlpha(node: self.titleNode, alpha: deselectionAlpha) + transition.updateAlpha(node: self.titleActiveNode, alpha: selectionAlpha) + transition.updateAlpha(node: self.shortTitleNode, alpha: deselectionAlpha) + transition.updateAlpha(node: self.shortTitleActiveNode, alpha: selectionAlpha) + + let titleArguments = TextNodeWithEntities.Arguments( + context: self.context, + cache: self.context.animationCache, + renderer: self.context.animationRenderer, + placeholderColor: presentationData.theme.list.mediaPlaceholderColor, + attemptSynchronous: false + ) + + self.titleNode.arguments = titleArguments + self.titleActiveNode.arguments = titleArguments + self.shortTitleNode.arguments = titleArguments + self.shortTitleActiveNode.arguments = titleArguments + + self.titleNode.visibility = title.enableAnimations + self.titleActiveNode.visibility = title.enableAnimations + self.shortTitleNode.visibility = title.enableAnimations + self.shortTitleActiveNode.visibility = title.enableAnimations + + if themeUpdated || titleUpdated { + self.titleNode.attributedText = title.attributedString(font: Font.medium(14.0), textColor: presentationData.theme.chat.inputPanel.panelControlColor) + self.titleActiveNode.attributedText = title.attributedString(font: Font.medium(14.0), textColor: presentationData.theme.chat.inputPanel.panelControlColor) + + self.shortTitleNode.attributedText = shortTitle.attributedString(font: Font.medium(14.0), textColor: presentationData.theme.chat.inputPanel.panelControlColor) + self.shortTitleActiveNode.attributedText = shortTitle.attributedString(font: Font.medium(14.0), textColor: presentationData.theme.chat.inputPanel.panelControlColor) + } + + if unreadCount != 0 { + if themeUpdated || unreadCountUpdated || self.badgeTextNode.attributedText == nil { + self.badgeTextNode.attributedText = NSAttributedString(string: "\(unreadCount)", font: Font.regular(14.0), textColor: presentationData.theme.list.itemCheckColors.foregroundColor) + } + + let badgeSelectionFraction: CGFloat = unreadHasUnmuted ? 1.0 : selectionFraction + let badgeSelectionAlpha: CGFloat = badgeSelectionFraction + //let badgeDeselectionAlpha: CGFloat = 1.0 - badgeSelectionFraction + + transition.updateAlpha(node: self.badgeBackgroundActiveNode, alpha: badgeSelectionAlpha * badgeSelectionAlpha) + //transition.updateAlpha(node: self.badgeBackgroundInactiveNode, alpha: badgeDeselectionAlpha) + self.badgeBackgroundInactiveNode.alpha = 1.0 + } + + if self.isReordering != isReordering { + self.isReordering = isReordering + if self.isReordering { + self.layer.addReorderingShaking() + } else { + self.layer.removeAnimation(forKey: "shaking_position") + self.layer.removeAnimation(forKey: "shaking_rotation") + } + } + } + + func updateLayout(height: CGFloat, transition: ContainedViewLayoutTransition) -> (width: CGFloat, shortWidth: CGFloat) { + let titleSize = self.titleNode.updateLayout(CGSize(width: 160.0, height: .greatestFiniteMagnitude)) + let _ = self.titleActiveNode.updateLayout(CGSize(width: 160.0, height: .greatestFiniteMagnitude)) + let titleFrame = CGRect(origin: CGPoint(x: -self.titleNode.insets.left, y: floor((height - titleSize.height) / 2.0)), size: titleSize) + self.titleContainer.frame = titleFrame + self.titleNode.frame = CGRect(origin: CGPoint(), size: titleFrame.size) + self.titleActiveNode.frame = CGRect(origin: CGPoint(), size: titleFrame.size) + + let shortTitleSize = self.shortTitleNode.updateLayout(CGSize(width: 160.0, height: .greatestFiniteMagnitude)) + let _ = self.shortTitleActiveNode.updateLayout(CGSize(width: 160.0, height: .greatestFiniteMagnitude)) + let shortTitleFrame = CGRect(origin: CGPoint(x: -self.shortTitleNode.insets.left, y: floor((height - shortTitleSize.height) / 2.0)), size: shortTitleSize) + self.shortTitleContainer.frame = shortTitleFrame + self.shortTitleNode.frame = CGRect(origin: CGPoint(), size: shortTitleFrame.size) + self.shortTitleActiveNode.frame = CGRect(origin: CGPoint(), size: shortTitleFrame.size) + + if let deleteButtonNode = self.deleteButtonNode { + if let theme = self.theme { + let deleteButtonSize = deleteButtonNode.update(theme: theme) + deleteButtonNode.frame = CGRect(origin: CGPoint(x: -deleteButtonSize.width + 3.0, y: 5.0), size: deleteButtonSize) + } + } + + let badgeSize = self.badgeTextNode.updateLayout(CGSize(width: 200.0, height: .greatestFiniteMagnitude)) + let badgeInset: CGFloat = 4.0 + let badgeBackgroundFrame = CGRect(origin: CGPoint(x: titleSize.width - self.titleNode.insets.left - self.titleNode.insets.right + 4.0, y: floor((height - 18.0) / 2.0)), size: CGSize(width: max(18.0, badgeSize.width + badgeInset * 2.0), height: 18.0)) + self.badgeContainerNode.frame = badgeBackgroundFrame + self.badgeBackgroundActiveNode.frame = CGRect(origin: CGPoint(), size: badgeBackgroundFrame.size) + self.badgeBackgroundInactiveNode.frame = CGRect(origin: CGPoint(), size: badgeBackgroundFrame.size) + self.badgeTextNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((badgeBackgroundFrame.width - badgeSize.width) / 2.0), y: floor((badgeBackgroundFrame.height - badgeSize.height) / 2.0)), size: badgeSize) + + let width: CGFloat + if self.unreadCount == 0 || self.isReordering || self.isEditing || self.isDisabled { + if !self.isReordering { + self.badgeContainerNode.alpha = 0.0 + } + width = titleSize.width - self.titleNode.insets.left - self.titleNode.insets.right + } else { + if !self.isReordering { + self.badgeContainerNode.alpha = 1.0 + } + width = badgeBackgroundFrame.maxX + } + + return (width, shortTitleSize.width - self.shortTitleNode.insets.left - self.shortTitleNode.insets.right) + } + + func updateArea(size: CGSize, sideInset: CGFloat, useShortTitle: Bool, transition: ContainedViewLayoutTransition) { + transition.updateAlpha(node: self.titleContainer, alpha: useShortTitle ? 0.0 : 1.0) + transition.updateAlpha(node: self.shortTitleContainer, alpha: useShortTitle ? 1.0 : 0.0) + + self.buttonNode.frame = CGRect(origin: CGPoint(x: -sideInset, y: 0.0), size: CGSize(width: size.width + sideInset * 2.0, height: size.height)) + + self.extractedContainerNode.frame = CGRect(origin: CGPoint(), size: size) + self.extractedContainerNode.contentNode.frame = CGRect(origin: CGPoint(), size: size) + self.extractedContainerNode.contentRect = CGRect(origin: CGPoint(x: self.extractedBackgroundNode.frame.minX, y: 0.0), size: CGSize(width:self.extractedBackgroundNode.frame.width, height: size.height)) + self.containerNode.frame = CGRect(origin: CGPoint(), size: size) + + self.hitTestSlop = UIEdgeInsets(top: 0.0, left: -sideInset, bottom: 0.0, right: -sideInset) + self.extractedContainerNode.hitTestSlop = self.hitTestSlop + self.extractedContainerNode.contentNode.hitTestSlop = self.hitTestSlop + self.containerNode.hitTestSlop = self.hitTestSlop + + let extractedBackgroundHeight: CGFloat = 36.0 + let extractedBackgroundInset: CGFloat = 14.0 + self.extractedBackgroundNode.frame = CGRect(origin: CGPoint(x: -extractedBackgroundInset, y: floor((size.height - extractedBackgroundHeight) / 2.0)), size: CGSize(width: size.width + extractedBackgroundInset * 2.0, height: extractedBackgroundHeight)) + } + + func animateBadgeIn() { + if !self.isReordering { + let transition: ContainedViewLayoutTransition = .animated(duration: 0.4, curve: .spring) + self.badgeContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) + ContainedViewLayoutTransition.immediate.updateSublayerTransformScale(node: self.badgeContainerNode, scale: 0.1) + transition.updateSublayerTransformScale(node: self.badgeContainerNode, scale: 1.0) + } + } + + func animateBadgeOut() { + if !self.isReordering { + let transition: ContainedViewLayoutTransition = .animated(duration: 0.4, curve: .spring) + self.badgeContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25) + ContainedViewLayoutTransition.immediate.updateSublayerTransformScale(node: self.badgeContainerNode, scale: 1.0) + transition.updateSublayerTransformScale(node: self.badgeContainerNode, scale: 0.1) + } + } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if let deleteButtonNode = self.deleteButtonNode { + if deleteButtonNode.frame.insetBy(dx: -4.0, dy: -4.0).contains(point) { + return deleteButtonNode.view + } + } + + if self.buttonNode.isUserInteractionEnabled { + if let result = self.buttonNode.view.hitTest(self.view.convert(point, to: self.buttonNode.view), with: event) { + return result + } + } + + return super.hitTest(point, with: event) + } +} + +public enum ChatListFilterTabEntryId: Hashable { + case all + case filter(Int32) +} + +public struct ChatListFilterTabEntryUnreadCount: Equatable { + public let value: Int + public let hasUnmuted: Bool + + public init(value: Int, hasUnmuted: Bool) { + self.value = value + self.hasUnmuted = hasUnmuted + } +} + +public enum ChatListFilterTabEntry: Equatable { + case all(unreadCount: Int) + case filter(id: Int32, text: ChatFolderTitle, unread: ChatListFilterTabEntryUnreadCount) + + public var id: ChatListFilterTabEntryId { + switch self { + case .all: + return .all + case let .filter(id, _, _): + return .filter(id) + } + } + + func title(strings: PresentationStrings) -> ChatFolderTitle { + switch self { + case .all: + return ChatFolderTitle(text: strings.ChatList_Tabs_AllChats, entities: [], enableAnimations: true) + case let .filter(_, text, _): + return text + } + } + + func shortTitle(strings: PresentationStrings) -> ChatFolderTitle { + switch self { + case .all: + return ChatFolderTitle(text: strings.ChatList_Tabs_All, entities: [], enableAnimations: true) + case let .filter(_, text, _): + return text + } + } +} + +public final class ChatListFilterTabContainerNode: ASDisplayNode { + private let context: AccountContext + private let backgroundContainerView: GlassBackgroundContainerView + private let backgroundView: GlassBackgroundView + private let scrollNode: ASScrollNode + private let selectedBackgroundNode: ASImageNode + private var itemNodes: [ChatListFilterTabEntryId: ItemNode] = [:] + + public var tabSelected: ((ChatListFilterTabEntryId, Bool) -> Void)? + public var tabRequestedDeletion: ((ChatListFilterTabEntryId) -> Void)? + public var addFilter: (() -> Void)? + public var contextGesture: ((Int32?, ContextExtractedContentContainingNode, ContextGesture, Bool) -> Void)? + public var presentPremiumTip: (() -> Void)? + + private var reorderingGesture: ReorderingGestureRecognizer? + private var reorderingItem: ChatListFilterTabEntryId? + private var reorderingItemPosition: (initial: CGFloat, offset: CGFloat)? + private var reorderingAutoScrollAnimator: ConstantDisplayLinkAnimator? + private var initialReorderedItemIds: [ChatListFilterTabEntryId]? + private var reorderedItemIds: [ChatListFilterTabEntryId]? + private lazy var hapticFeedback = { HapticFeedback() }() + + private var currentParams: (size: CGSize, sideInset: CGFloat, filters: [ChatListFilterTabEntry], selectedFilter: ChatListFilterTabEntryId?, isReordering: Bool, isEditing: Bool, canReorderAllChats: Bool, filtersLimit: Int32?, transitionFraction: CGFloat, presentationData: PresentationData)? + + public var reorderedFilterIds: [Int32]? { + return self.reorderedItemIds.flatMap { + $0.compactMap { + switch $0 { + case .all: + return 0 + case let .filter(id): + return id + } + } + } + } + + public var filtersCount: Int32 { + if let (_, _, filters, _, _, _, _, _, _, _) = self.currentParams { + let filters = filters.filter { filter in + if case .all = filter { + return false + } else { + return true + } + } + return Int32(filters.count) + } else { + return 0 + } + } + + public init(context: AccountContext) { + self.context = context + + self.backgroundContainerView = GlassBackgroundContainerView() + self.backgroundView = GlassBackgroundView() + self.backgroundContainerView.contentView.addSubview(self.backgroundView) + + self.scrollNode = ASScrollNode() + + self.selectedBackgroundNode = ASImageNode() + self.selectedBackgroundNode.displaysAsynchronously = false + self.selectedBackgroundNode.displayWithoutProcessing = true + + super.init() + + self.view.addSubview(self.backgroundContainerView) + + self.scrollNode.view.showsHorizontalScrollIndicator = false + self.scrollNode.view.showsVerticalScrollIndicator = false + self.scrollNode.view.scrollsToTop = false + self.scrollNode.view.delaysContentTouches = false + self.scrollNode.view.canCancelContentTouches = true + if #available(iOS 11.0, *) { + self.scrollNode.view.contentInsetAdjustmentBehavior = .never + } + + self.backgroundView.contentView.addSubview(self.scrollNode.view) + self.scrollNode.addSubnode(self.selectedBackgroundNode) + + let reorderingGesture = ReorderingGestureRecognizer(shouldBegin: { [weak self] point in + guard let strongSelf = self else { + return false + } + for (_, itemNode) in strongSelf.itemNodes { + if itemNode.view.convert(itemNode.bounds, to: strongSelf.view).contains(point) { + return true + } + } + return false + }, began: { [weak self] point in + guard let strongSelf = self, let _ = strongSelf.currentParams else { + return + } + strongSelf.initialReorderedItemIds = strongSelf.reorderedItemIds + for (id, itemNode) in strongSelf.itemNodes { + let itemFrame = itemNode.view.convert(itemNode.bounds, to: strongSelf.view) + if itemFrame.contains(point) { + strongSelf.hapticFeedback.impact() + + strongSelf.reorderingItem = id + itemNode.frame = itemFrame + strongSelf.reorderingAutoScrollAnimator = ConstantDisplayLinkAnimator(update: { + guard let strongSelf = self, let currentLocation = strongSelf.reorderingGesture?.currentLocation else { + return + } + let edgeWidth: CGFloat = 20.0 + if currentLocation.x <= edgeWidth { + var contentOffset = strongSelf.scrollNode.view.contentOffset + contentOffset.x = max(0.0, contentOffset.x - 3.0) + strongSelf.scrollNode.view.setContentOffset(contentOffset, animated: false) + } else if currentLocation.x >= strongSelf.bounds.width - edgeWidth { + var contentOffset = strongSelf.scrollNode.view.contentOffset + contentOffset.x = max(0.0, min(strongSelf.scrollNode.view.contentSize.width - strongSelf.scrollNode.bounds.width, contentOffset.x + 3.0)) + strongSelf.scrollNode.view.setContentOffset(contentOffset, animated: false) + } + }) + strongSelf.reorderingAutoScrollAnimator?.isPaused = false + strongSelf.backgroundView.contentView.addSubview(itemNode.view) + + strongSelf.reorderingItemPosition = (itemNode.frame.minX, 0.0) + if let (size, sideInset, filters, selectedFilter, isReordering, isEditing, canReorderAllChats, filtersLimit, transitionFraction, presentationData) = strongSelf.currentParams { + strongSelf.update(size: size, sideInset: sideInset, filters: filters, selectedFilter: selectedFilter, isReordering: isReordering, isEditing: isEditing, canReorderAllChats: canReorderAllChats, filtersLimit: filtersLimit, transitionFraction: transitionFraction, presentationData: presentationData, transition: .animated(duration: 0.25, curve: .easeInOut)) + } + return + } + } + }, ended: { [weak self] in + guard let strongSelf = self, let reorderingItem = strongSelf.reorderingItem else { + return + } + if let itemNode = strongSelf.itemNodes[reorderingItem] { + let projectedItemFrame = itemNode.view.convert(itemNode.bounds, to: strongSelf.scrollNode.view) + itemNode.frame = projectedItemFrame + strongSelf.scrollNode.addSubnode(itemNode) + } + + if strongSelf.currentParams?.canReorderAllChats == false, let firstItem = strongSelf.reorderedItemIds?.first, case .filter = firstItem { + strongSelf.reorderedItemIds = strongSelf.initialReorderedItemIds + strongSelf.presentPremiumTip?() + } + + strongSelf.reorderingItem = nil + strongSelf.reorderingItemPosition = nil + strongSelf.reorderingAutoScrollAnimator?.invalidate() + strongSelf.reorderingAutoScrollAnimator = nil + if let (size, sideInset, filters, selectedFilter, isReordering, isEditing, canReorderAllChats, filtersLimit, transitionFraction, presentationData) = strongSelf.currentParams { + strongSelf.update(size: size, sideInset: sideInset, filters: filters, selectedFilter: selectedFilter, isReordering: isReordering, isEditing: isEditing, canReorderAllChats: canReorderAllChats, filtersLimit: filtersLimit, transitionFraction: transitionFraction, presentationData: presentationData, transition: .animated(duration: 0.25, curve: .easeInOut)) + } + }, moved: { [weak self] offset in + guard let strongSelf = self, let reorderingItem = strongSelf.reorderingItem else { + return + } + + let minIndex = 0 + if let reorderingItemNode = strongSelf.itemNodes[reorderingItem], let (initial, _) = strongSelf.reorderingItemPosition, let reorderedItemIds = strongSelf.reorderedItemIds, let currentItemIndex = reorderedItemIds.firstIndex(of: reorderingItem) { + + for (id, itemNode) in strongSelf.itemNodes { + guard let itemIndex = reorderedItemIds.firstIndex(of: id) else { + continue + } + if id != reorderingItem { + let itemFrame = itemNode.view.convert(itemNode.bounds, to: strongSelf.view) + if reorderingItemNode.frame.intersects(itemFrame) { + let targetIndex: Int + if reorderingItemNode.frame.midX < itemFrame.midX { + targetIndex = max(minIndex, itemIndex - 1) + } else { + targetIndex = max(minIndex, min(reorderedItemIds.count - 1, itemIndex)) + } + if targetIndex != currentItemIndex { + strongSelf.hapticFeedback.tap() + + var updatedReorderedItemIds = reorderedItemIds + if targetIndex > currentItemIndex { + updatedReorderedItemIds.insert(reorderingItem, at: targetIndex + 1) + updatedReorderedItemIds.remove(at: currentItemIndex) + } else { + updatedReorderedItemIds.remove(at: currentItemIndex) + updatedReorderedItemIds.insert(reorderingItem, at: targetIndex) + } + strongSelf.reorderedItemIds = updatedReorderedItemIds + if let (size, sideInset, filters, selectedFilter, isReordering, isEditing, canReorderAllChats, filtersLimit, transitionFraction, presentationData) = strongSelf.currentParams { + strongSelf.update(size: size, sideInset: sideInset, filters: filters, selectedFilter: selectedFilter, isReordering: isReordering, isEditing: isEditing, canReorderAllChats: canReorderAllChats, filtersLimit: filtersLimit, transitionFraction: transitionFraction, presentationData: presentationData, transition: .animated(duration: 0.25, curve: .easeInOut)) + } + } + break + } + } + } + + strongSelf.reorderingItemPosition = (initial, offset) + } + if let (size, sideInset, filters, selectedFilter, isReordering, isEditing, canReorderAllChats, filtersLimit, transitionFraction, presentationData) = strongSelf.currentParams { + strongSelf.update(size: size, sideInset: sideInset, filters: filters, selectedFilter: selectedFilter, isReordering: isReordering, isEditing: isEditing, canReorderAllChats: canReorderAllChats, filtersLimit: filtersLimit, transitionFraction: transitionFraction, presentationData: presentationData, transition: .immediate) + } + }) + self.reorderingGesture = reorderingGesture + self.view.addGestureRecognizer(reorderingGesture) + reorderingGesture.isEnabled = false + } + + private var previousSelectedAbsFrame: CGRect? + private var previousSelectedFrame: CGRect? + + public func cancelAnimations() { + self.selectedBackgroundNode.layer.removeAllAnimations() + self.scrollNode.layer.removeAllAnimations() + } + + public func update(size containerSize: CGSize, sideInset containerSideInset: CGFloat, filters: [ChatListFilterTabEntry], selectedFilter: ChatListFilterTabEntryId?, isReordering: Bool, isEditing: Bool, canReorderAllChats: Bool, filtersLimit: Int32?, transitionFraction: CGFloat, presentationData: PresentationData, transition proposedTransition: ContainedViewLayoutTransition) { + let isFirstTime = self.currentParams == nil + let transition: ContainedViewLayoutTransition = isFirstTime ? .immediate : proposedTransition + + let backgroundSize = CGSize(width: containerSize.width - (16.0 - containerSideInset) * 2.0, height: 44.0) + + transition.updateFrame(view: self.backgroundContainerView, frame: CGRect(origin: CGPoint(x: containerSideInset + 16.0, y: 0.0), size: backgroundSize)) + self.backgroundContainerView.update(size: backgroundSize, isDark: presentationData.theme.overallDarkAppearance, transition: ComponentTransition(transition)) + + transition.updateFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: backgroundSize)) + self.backgroundView.update(size: backgroundSize, cornerRadius: backgroundSize.height * 0.5, isDark: presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: presentationData.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: ComponentTransition(transition)) + + var isEditing = isEditing + if isReordering { + isEditing = false + } + + var focusOnSelectedFilter = self.currentParams?.selectedFilter != selectedFilter + let previousScrollBounds = self.scrollNode.bounds + let previousContentWidth = self.scrollNode.view.contentSize.width + + if self.currentParams?.presentationData.theme !== presentationData.theme { + self.selectedBackgroundNode.image = generateStretchableFilledCircleImage(diameter: 36.0, color: presentationData.theme.chatList.itemHighlightedBackgroundColor) + } + + if isReordering { + if let reorderedItemIds = self.reorderedItemIds { + let currentIds = Set(reorderedItemIds) + if currentIds != Set(filters.map { $0.id }) { + var updatedReorderedItemIds = reorderedItemIds.filter { id in + return filters.contains(where: { $0.id == id }) + } + for filter in filters { + if !currentIds.contains(filter.id) { + updatedReorderedItemIds.append(filter.id) + } + } + self.reorderedItemIds = updatedReorderedItemIds + } + } else { + self.reorderedItemIds = filters.map { $0.id } + } + } else if self.reorderedItemIds != nil { + self.reorderedItemIds = nil + } + + self.currentParams = (size: containerSize, sideInset: containerSideInset, filters: filters, selectedFilter: selectedFilter, isReordering, isEditing, canReorderAllChats, filtersLimit, transitionFraction, presentationData: presentationData) + + self.reorderingGesture?.isEnabled = isReordering + + transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: backgroundSize)) + + enum BadgeAnimation { + case `in` + case out + } + + var badgeAnimations: [ChatListFilterTabEntryId: BadgeAnimation] = [:] + + var reorderedFilters: [ChatListFilterTabEntry] = filters + if let reorderedItemIds = self.reorderedItemIds { + reorderedFilters = reorderedItemIds.compactMap { id -> ChatListFilterTabEntry? in + if let index = filters.firstIndex(where: { $0.id == id }) { + return filters[index] + } else { + return nil + } + } + } + + var folderIndex = 0 + for i in 0 ..< reorderedFilters.count { + let filter = reorderedFilters[i] + + let itemNode: ItemNode + var itemNodeTransition = transition + var wasAdded = false + if let current = self.itemNodes[filter.id] { + itemNode = current + } else { + itemNodeTransition = .immediate + wasAdded = true + itemNode = ItemNode(context: self.context, pressed: { [weak self] disabled in + self?.tabSelected?(filter.id, disabled) + }, requestedDeletion: { [weak self] in + self?.tabRequestedDeletion?(filter.id) + }, contextGesture: { [weak self] sourceNode, gesture, isDisabled in + guard let strongSelf = self else { + return + } + strongSelf.scrollNode.view.panGestureRecognizer.isEnabled = false + strongSelf.scrollNode.view.panGestureRecognizer.isEnabled = true + strongSelf.scrollNode.view.setContentOffset(strongSelf.scrollNode.view.contentOffset, animated: false) + switch filter { + case let .filter(id, _, _): + strongSelf.contextGesture?(id, sourceNode, gesture, isDisabled) + default: + strongSelf.contextGesture?(nil, sourceNode, gesture, isDisabled) + } + }) + self.itemNodes[filter.id] = itemNode + } + let unreadCount: Int + let unreadHasUnmuted: Bool + var isNoFilter = false + var isDisabled = false + switch filter { + case let .all(count): + unreadCount = count + unreadHasUnmuted = true + isNoFilter = true + case let .filter(_, _, unread): + unreadCount = unread.value + unreadHasUnmuted = unread.hasUnmuted + + if let filtersLimit = filtersLimit { + isDisabled = !canReorderAllChats && folderIndex >= filtersLimit + } + folderIndex += 1 + } + if !wasAdded && (itemNode.unreadCount != 0) != (unreadCount != 0) { + badgeAnimations[filter.id] = (unreadCount != 0) ? .in : .out + } + + let selectionFraction: CGFloat + if selectedFilter == filter.id { + selectionFraction = 1.0 - abs(transitionFraction) + } else if i != 0 && selectedFilter == reorderedFilters[i - 1].id { + selectionFraction = max(0.0, -transitionFraction) + } else if i != reorderedFilters.count - 1 && selectedFilter == reorderedFilters[i + 1].id { + selectionFraction = max(0.0, transitionFraction) + } else { + selectionFraction = 0.0 + } + + itemNode.updateText(strings: presentationData.strings, title: filter.title(strings: presentationData.strings), shortTitle: i == 0 ? filter.shortTitle(strings: presentationData.strings) : filter.title(strings: presentationData.strings), unreadCount: unreadCount, unreadHasUnmuted: unreadHasUnmuted, isNoFilter: isNoFilter, selectionFraction: selectionFraction, isEditing: isEditing, isReordering: isReordering, canReorderAllChats: canReorderAllChats, isDisabled: isDisabled, presentationData: presentationData, transition: itemNodeTransition) + } + var removeKeys: [ChatListFilterTabEntryId] = [] + for (id, _) in self.itemNodes { + if !filters.contains(where: { $0.id == id }) { + removeKeys.append(id) + } + } + for id in removeKeys { + if let itemNode = self.itemNodes.removeValue(forKey: id) { + transition.updateAlpha(node: itemNode, alpha: 0.0, completion: { [weak itemNode] _ in + itemNode?.removeFromSupernode() + }) + transition.updateTransformScale(node: itemNode, scale: 0.1) + } + } + + var tabSizes: [(ChatListFilterTabEntryId, CGSize, CGSize, ItemNode, Bool)] = [] + var totalRawTabSize: CGFloat = 0.0 + var selectionFrames: [CGRect] = [] + + for filter in reorderedFilters { + guard let itemNode = self.itemNodes[filter.id] else { + continue + } + let wasAdded = itemNode.supernode == nil + var itemNodeTransition = transition + if wasAdded { + itemNodeTransition = .immediate + self.scrollNode.addSubnode(itemNode) + } + let (paneNodeWidth, paneNodeShortWidth) = itemNode.updateLayout(height: backgroundSize.height, transition: itemNodeTransition) + let paneNodeSize = CGSize(width: paneNodeWidth, height: backgroundSize.height) + let paneNodeShortSize = CGSize(width: paneNodeShortWidth, height: backgroundSize.height) + tabSizes.append((filter.id, paneNodeSize, paneNodeShortSize, itemNode, wasAdded)) + totalRawTabSize += paneNodeSize.width + + if case .animated = transition, let badgeAnimation = badgeAnimations[filter.id] { + switch badgeAnimation { + case .in: + itemNode.animateBadgeIn() + case .out: + itemNode.animateBadgeOut() + } + } + } + + let minSpacing: CGFloat = 26.0 + + let resolvedSideInset: CGFloat = 14.0 + var leftOffset: CGFloat = resolvedSideInset + + var longTitlesWidth: CGFloat = resolvedSideInset + for i in 0 ..< tabSizes.count { + let (_, paneNodeSize, _, _, _) = tabSizes[i] + longTitlesWidth += paneNodeSize.width + if i != tabSizes.count - 1 { + longTitlesWidth += minSpacing + } + } + longTitlesWidth += resolvedSideInset + let useShortTitles = longTitlesWidth > backgroundSize.width + + for i in 0 ..< tabSizes.count { + let (itemId, paneNodeLongSize, paneNodeShortSize, paneNode, wasAdded) = tabSizes[i] + var itemNodeTransition = transition + if wasAdded { + itemNodeTransition = .immediate + } + + let useShortTitle = itemId == .all && useShortTitles + let paneNodeSize = useShortTitle ? paneNodeShortSize : paneNodeLongSize + + let paneFrame = CGRect(origin: CGPoint(x: leftOffset, y: floor((backgroundSize.height - paneNodeSize.height) / 2.0)), size: paneNodeSize) + + if itemId == self.reorderingItem, let (initial, offset) = self.reorderingItemPosition { + itemNodeTransition.updateSublayerTransformScale(node: paneNode, scale: 1.2) + itemNodeTransition.updateAlpha(node: paneNode, alpha: 0.9) + itemNodeTransition.updateFrameAdditive(node: paneNode, frame: CGRect(origin: CGPoint(x: initial + offset, y: paneFrame.minY), size: paneFrame.size)) + } else { + itemNodeTransition.updateSublayerTransformScale(node: paneNode, scale: 1.0) + itemNodeTransition.updateAlpha(node: paneNode, alpha: 1.0) + if wasAdded { + paneNode.frame = paneFrame + paneNode.alpha = 0.0 + itemNodeTransition.updateAlpha(node: paneNode, alpha: 1.0) + } else { + itemNodeTransition.updateFrameAdditive(node: paneNode, frame: paneFrame) + } + } + paneNode.updateArea(size: paneFrame.size, sideInset: minSpacing / 2.0, useShortTitle: useShortTitle, transition: itemNodeTransition) + paneNode.hitTestSlop = UIEdgeInsets(top: 0.0, left: -minSpacing / 2.0, bottom: 0.0, right: -minSpacing / 2.0) + + selectionFrames.append(paneFrame) + + leftOffset += paneNodeSize.width + minSpacing + } + leftOffset -= minSpacing + leftOffset += resolvedSideInset + + self.scrollNode.view.contentSize = CGSize(width: leftOffset, height: backgroundSize.height) + + var selectedFrame: CGRect? + if let selectedFilter = selectedFilter, let currentIndex = reorderedFilters.firstIndex(where: { $0.id == selectedFilter }) { + func interpolateFrame(from fromValue: CGRect, to toValue: CGRect, t: CGFloat) -> CGRect { + return CGRect(x: floorToScreenPixels(toValue.origin.x * t + fromValue.origin.x * (1.0 - t)), y: floorToScreenPixels(toValue.origin.y * t + fromValue.origin.y * (1.0 - t)), width: floorToScreenPixels(toValue.size.width * t + fromValue.size.width * (1.0 - t)), height: floorToScreenPixels(toValue.size.height * t + fromValue.size.height * (1.0 - t))) + } + + if currentIndex != 0 && transitionFraction > 0.0 { + let currentFrame = selectionFrames[currentIndex] + let previousFrame = selectionFrames[currentIndex - 1] + selectedFrame = interpolateFrame(from: currentFrame, to: previousFrame, t: abs(transitionFraction)) + } else if currentIndex != filters.count - 1 && transitionFraction < 0.0 { + let currentFrame = selectionFrames[currentIndex] + let previousFrame = selectionFrames[currentIndex + 1] + selectedFrame = interpolateFrame(from: currentFrame, to: previousFrame, t: abs(transitionFraction)) + } else { + selectedFrame = selectionFrames[currentIndex] + } + } + + if let selectedFrame = selectedFrame { + let wasAdded = self.selectedBackgroundNode.isHidden + self.selectedBackgroundNode.isHidden = false + let selectedBackgroundFrame = CGRect(origin: CGPoint(x: selectedFrame.minX - 10.0, y: selectedFrame.minY - floor((36.0 - selectedFrame.height) * 0.5)), size: CGSize(width: selectedFrame.width + 10.0 * 2.0, height: 36.0)) + if wasAdded { + self.selectedBackgroundNode.frame = selectedBackgroundFrame + } else { + transition.updateFrame(node: self.selectedBackgroundNode, frame: selectedBackgroundFrame) + } + + if let previousSelectedFrame = self.previousSelectedFrame { + let previousContentOffsetX = max(0.0, min(previousContentWidth - previousScrollBounds.width, floor(previousSelectedFrame.midX - previousScrollBounds.width / 2.0))) + if abs(previousContentOffsetX - previousScrollBounds.minX) < 1.0 { + focusOnSelectedFilter = true + } + } + + if focusOnSelectedFilter && self.reorderingItem == nil { + let updatedBounds: CGRect + if transitionFraction.isZero && selectedFilter == reorderedFilters.first?.id { + updatedBounds = CGRect(origin: CGPoint(), size: self.scrollNode.bounds.size) + } else if transitionFraction.isZero && selectedFilter == reorderedFilters.last?.id { + updatedBounds = CGRect(origin: CGPoint(x: max(0.0, self.scrollNode.view.contentSize.width - self.scrollNode.bounds.width), y: 0.0), size: self.scrollNode.bounds.size) + } else { + let contentOffsetX = max(0.0, min(self.scrollNode.view.contentSize.width - self.scrollNode.bounds.width, floor(selectedFrame.midX - self.scrollNode.bounds.width / 2.0))) + updatedBounds = CGRect(origin: CGPoint(x: contentOffsetX, y: 0.0), size: self.scrollNode.bounds.size) + } + self.scrollNode.bounds = updatedBounds + } + if abs(previousScrollBounds.minX - self.scrollNode.bounds.minX) > .ulpOfOne { + transition.animateHorizontalOffsetAdditive(node: self.scrollNode, offset: previousScrollBounds.minX - self.scrollNode.bounds.minX) + } + + self.previousSelectedAbsFrame = selectedFrame.offsetBy(dx: -self.scrollNode.bounds.minX, dy: 0.0) + self.previousSelectedFrame = selectedFrame + } else { + self.selectedBackgroundNode.isHidden = true + self.previousSelectedAbsFrame = nil + self.previousSelectedFrame = nil + } + } +} + +private class ReorderingGestureRecognizerTimerTarget: NSObject { + private let f: () -> Void + + init(_ f: @escaping () -> Void) { + self.f = f + + super.init() + } + + @objc func timerEvent() { + self.f() + } +} + +private final class InternalGestureRecognizerDelegate: NSObject, UIGestureRecognizerDelegate { + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool { + if otherGestureRecognizer is UIPanGestureRecognizer { + return true + } else { + return false + } + } +} + +private final class ReorderingGestureRecognizer: UIGestureRecognizer, UIGestureRecognizerDelegate { + private let internalDelegate = InternalGestureRecognizerDelegate() + + private let shouldBegin: (CGPoint) -> Bool + private let began: (CGPoint) -> Void + private let ended: () -> Void + private let moved: (CGFloat) -> Void + + private var initialLocation: CGPoint? + private var delayTimer: Foundation.Timer? + + var currentLocation: CGPoint? + + init(shouldBegin: @escaping (CGPoint) -> Bool, began: @escaping (CGPoint) -> Void, ended: @escaping () -> Void, moved: @escaping (CGFloat) -> Void) { + self.shouldBegin = shouldBegin + self.began = began + self.ended = ended + self.moved = moved + + super.init(target: nil, action: nil) + + self.delegate = self.internalDelegate + } + + override func reset() { + super.reset() + + self.initialLocation = nil + self.delayTimer?.invalidate() + self.delayTimer = nil + self.currentLocation = nil + } + + override func touchesBegan(_ touches: Set, with event: UIEvent) { + super.touchesBegan(touches, with: event) + + guard let location = touches.first?.location(in: self.view) else { + self.state = .failed + return + } + + if self.state == .possible { + if self.delayTimer == nil { + if !self.shouldBegin(location) { + self.state = .failed + return + } + self.initialLocation = location + let timer = Foundation.Timer(timeInterval: 0.2, target: ReorderingGestureRecognizerTimerTarget { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.delayTimer = nil + strongSelf.state = .began + strongSelf.began(location) + }, selector: #selector(ReorderingGestureRecognizerTimerTarget.timerEvent), userInfo: nil, repeats: false) + self.delayTimer = timer + RunLoop.main.add(timer, forMode: .common) + } else { + self.state = .failed + } + } + } + + override func touchesEnded(_ touches: Set, with event: UIEvent) { + super.touchesEnded(touches, with: event) + + self.delayTimer?.invalidate() + + if self.state == .began || self.state == .changed { + self.ended() + } + + self.state = .failed + } + + override func touchesCancelled(_ touches: Set, with event: UIEvent) { + super.touchesCancelled(touches, with: event) + + if self.state == .began || self.state == .changed { + self.delayTimer?.invalidate() + self.ended() + self.state = .failed + } + } + + override func touchesMoved(_ touches: Set, with event: UIEvent) { + super.touchesMoved(touches, with: event) + + guard let initialLocation = self.initialLocation, let location = touches.first?.location(in: self.view) else { + return + } + let offset = location.x - initialLocation.x + self.currentLocation = location + + if self.delayTimer != nil { + if abs(offset) > 4.0 { + self.delayTimer?.invalidate() + self.state = .failed + return + } + } else { + if self.state == .began || self.state == .changed { + self.state = .changed + self.moved(offset) + } + } + } +} diff --git a/submodules/TelegramUI/Components/ChatList/ChatListHeaderNoticeComponent/BUILD b/submodules/TelegramUI/Components/ChatList/ChatListHeaderNoticeComponent/BUILD new file mode 100644 index 00000000..cd53921f --- /dev/null +++ b/submodules/TelegramUI/Components/ChatList/ChatListHeaderNoticeComponent/BUILD @@ -0,0 +1,33 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "ChatListHeaderNoticeComponent", + module_name = "ChatListHeaderNoticeComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/TelegramCore", + "//submodules/TelegramPresentationData", + "//submodules/AccountContext", + "//submodules/ComponentFlow", + "//submodules/Components/ComponentDisplayAdapters", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/AppBundle", + "//submodules/ItemListUI", + "//submodules/Markdown", + "//submodules/TelegramUI/Components/Chat/MergedAvatarsNode", + "//submodules/TelegramUI/Components/TextNodeWithEntities", + "//submodules/TextFormat", + "//submodules/AvatarNode", + "//submodules/TelegramUI/Components/GlobalControlPanelsContext", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/ChatList/ChatListHeaderNoticeComponent/Sources/ChatListHeaderNoticeComponent.swift b/submodules/TelegramUI/Components/ChatList/ChatListHeaderNoticeComponent/Sources/ChatListHeaderNoticeComponent.swift new file mode 100644 index 00000000..f3596727 --- /dev/null +++ b/submodules/TelegramUI/Components/ChatList/ChatListHeaderNoticeComponent/Sources/ChatListHeaderNoticeComponent.swift @@ -0,0 +1,139 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import TelegramCore +import TelegramPresentationData +import AccountContext +import GlobalControlPanelsContext +import ComponentFlow +import ComponentDisplayAdapters + +public final class ChatListHeaderNoticeComponent: Component { + public let context: AccountContext + public let theme: PresentationTheme + public let strings: PresentationStrings + public let data: GlobalControlPanelsContext.ChatListNotice + public let activateAction: (GlobalControlPanelsContext.ChatListNotice) -> Void + public let dismissAction: (GlobalControlPanelsContext.ChatListNotice) -> Void + public let selectAction: (GlobalControlPanelsContext.ChatListNotice, Bool) -> Void + + public init( + context: AccountContext, + theme: PresentationTheme, + strings: PresentationStrings, + data: GlobalControlPanelsContext.ChatListNotice, + activateAction: @escaping (GlobalControlPanelsContext.ChatListNotice) -> Void, + dismissAction: @escaping (GlobalControlPanelsContext.ChatListNotice) -> Void, + selectAction: @escaping (GlobalControlPanelsContext.ChatListNotice, Bool) -> Void + ) { + self.context = context + self.theme = theme + self.strings = strings + self.data = data + self.activateAction = activateAction + self.dismissAction = dismissAction + self.selectAction = selectAction + } + + public static func ==(lhs: ChatListHeaderNoticeComponent, rhs: ChatListHeaderNoticeComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.theme !== rhs.theme { + return false + } + if lhs.strings !== rhs.strings { + return false + } + if lhs.data != rhs.data { + return false + } + return true + } + + public final class View: UIView { + private var panel: ChatListNoticeItemNode? + + private var component: ChatListHeaderNoticeComponent? + private weak var state: EmptyComponentState? + + public override init(frame: CGRect) { + super.init(frame: frame) + + self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.onTapGesture(_:)))) + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + } + + @objc private func onTapGesture(_ recognizer: UITapGestureRecognizer) { + guard let component = self.component else { + return + } + if case .ended = recognizer.state { + component.activateAction(component.data) + } + } + + func update(component: ChatListHeaderNoticeComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + self.state = state + + let itemNode: ChatListNoticeItemNode + if let current = self.panel { + itemNode = current + } else { + itemNode = ChatListNoticeItemNode() + self.panel = itemNode + self.addSubview(itemNode.view) + } + + let item = ChatListNoticeItem( + context: component.context, + theme: component.theme, + strings: component.strings, + notice: component.data, + action: { [weak self] action in + guard let self, let component = self.component else { + return + } + switch action { + case .activate: + component.activateAction(component.data) + case .hide: + component.dismissAction(component.data) + case let .buttonChoice(isPositive): + component.selectAction(component.data, isPositive) + } + } + ) + let (nodeLayout, apply) = itemNode.asyncLayout()(item, ListViewItemLayoutParams( + width: availableSize.width, + leftInset: 0.0, + rightInset: 0.0, + availableHeight: 10000.0, + isStandalone: true + ), false) + + let size = CGSize(width: availableSize.width, height: nodeLayout.contentSize.height) + let panelFrame = CGRect(origin: CGPoint(), size: size) + transition.setFrame(view: itemNode.view, frame: panelFrame) + apply() + + return size + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/ChatList/ChatListHeaderNoticeComponent/Sources/ChatListNoticeItem.swift b/submodules/TelegramUI/Components/ChatList/ChatListHeaderNoticeComponent/Sources/ChatListNoticeItem.swift new file mode 100644 index 00000000..7c7cb283 --- /dev/null +++ b/submodules/TelegramUI/Components/ChatList/ChatListHeaderNoticeComponent/Sources/ChatListNoticeItem.swift @@ -0,0 +1,548 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import SwiftSignalKit +import TelegramCore +import TelegramPresentationData +import AppBundle +import ItemListUI +import Markdown +import AccountContext +import MergedAvatarsNode +import TextNodeWithEntities +import TextFormat +import AvatarNode +import GlobalControlPanelsContext + +class ChatListNoticeItem: ListViewItem { + enum Action { + case activate + case hide + case buttonChoice(isPositive: Bool) + } + + let context: AccountContext + let theme: PresentationTheme + let strings: PresentationStrings + let notice: GlobalControlPanelsContext.ChatListNotice + let action: (Action) -> Void + + let selectable: Bool = true + + init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, notice: GlobalControlPanelsContext.ChatListNotice, action: @escaping (Action) -> Void) { + self.context = context + self.theme = theme + self.strings = strings + self.notice = notice + self.action = action + } + + func selected(listView: ListView) { + listView.clearHighlightAnimated(true) + + self.action(.activate) + } + + func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { + async { + let node = ChatListNoticeItemNode() + + let (nodeLayout, apply) = node.asyncLayout()(self, params, false) + + node.insets = nodeLayout.insets + node.contentSize = nodeLayout.contentSize + + Queue.mainQueue().async { + completion(node, { + return (nil, { _ in + apply() + }) + }) + } + } + } + + func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) { + Queue.mainQueue().async { + assert(node() is ChatListNoticeItemNode) + if let nodeValue = node() as? ChatListNoticeItemNode { + + let layout = nodeValue.asyncLayout() + async { + let (nodeLayout, apply) = layout(self, params, nextItem == nil) + Queue.mainQueue().async { + completion(nodeLayout, { _ in + apply() + }) + } + } + } + } + } +} + +private let separatorHeight = 1.0 / UIScreen.main.scale + +private let titleFont = Font.semibold(15.0) +private let titleBoldFont = Font.bold(15.0) +private let titleItalicFont = Font.semiboldItalic(15.0) +private let titleBoldItalicFont = Font.semiboldItalic(15.0) + +private let textFont = Font.regular(15.0) +private let textBoldFont = Font.semibold(15.0) +private let textItalicFont = Font.italic(15.0) +private let textBoldItalicFont = Font.semiboldItalic(15.0) + +private let smallTextFont = Font.regular(14.0) + +final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode { + private let contentContainer: ASDisplayNode + private let titleNode: TextNodeWithEntities + private let textNode: TextNodeWithEntities + private let arrowNode: ASImageNode + private let separatorNode: ASDisplayNode + + private var avatarNode: AvatarNode? + private var avatarsNode: MergedAvatarsNode? + + private var closeButton: HighlightableButtonNode? + + private var okButtonText: TextNode? + private var cancelButtonText: TextNode? + private var okButton: HighlightableButtonNode? + private var cancelButton: HighlightableButtonNode? + + private var item: ChatListNoticeItem? + + override var apparentHeight: CGFloat { + didSet { + self.contentContainer.frame = CGRect(origin: CGPoint(), size: CGSize(width: self.bounds.width, height: self.apparentHeight)) + self.separatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: self.contentContainer.bounds.height - UIScreenPixel), size: CGSize(width: self.contentContainer.bounds.width, height: UIScreenPixel)) + } + } + + required init() { + self.contentContainer = ASDisplayNode() + + self.titleNode = TextNodeWithEntities() + self.textNode = TextNodeWithEntities() + self.arrowNode = ASImageNode() + self.separatorNode = ASDisplayNode() + + super.init(layerBacked: false, rotated: false, seeThrough: false) + + self.contentContainer.clipsToBounds = true + self.clipsToBounds = true + + self.contentContainer.addSubnode(self.titleNode.textNode) + self.contentContainer.addSubnode(self.textNode.textNode) + self.contentContainer.addSubnode(self.arrowNode) + + self.addSubnode(self.contentContainer) + } + + @objc private func closePressed() { + guard let item = self.item else { + return + } + item.action(.hide) + } + + override func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) { + let layout = self.asyncLayout() + let (_, apply) = layout(item as! ChatListNoticeItem, params, nextItem == nil) + apply() + } + + func asyncLayout() -> (_ item: ChatListNoticeItem, _ params: ListViewItemLayoutParams, _ isLast: Bool) -> (ListViewItemNodeLayout, () -> Void) { + let previousItem = self.item + + let makeTitleLayout = TextNodeWithEntities.asyncLayout(self.titleNode) + let makeTextLayout = TextNodeWithEntities.asyncLayout(self.textNode) + + let makeOkButtonTextLayout = TextNode.asyncLayout(self.okButtonText) + let makeCancelButtonTextLayout = TextNode.asyncLayout(self.cancelButtonText) + + return { item, params, last in + let baseWidth = params.width - params.leftInset - params.rightInset + let _ = baseWidth + + let sideInset: CGFloat = params.leftInset + 16.0 + let rightInset: CGFloat = sideInset + 24.0 + var titleRightInset = rightInset - 4.0 + let verticalInset: CGFloat = 9.0 + var spacing: CGFloat = 0.0 + + let themeUpdated = item.theme !== previousItem?.theme + + let titleString: NSAttributedString + let textString: NSAttributedString + var avatarPeer: EnginePeer? + var avatarPeers: [EnginePeer] = [] + + var okButtonLayout: (TextNodeLayout, () -> TextNode)? + var cancelButtonLayout: (TextNodeLayout, () -> TextNode)? + var alignment: NSTextAlignment = .left + + switch item.notice { + case let .clearStorage(sizeFraction): + let sizeString = dataSizeString(Int64(sizeFraction), formatting: DataSizeStringFormatting(strings: item.strings, decimalSeparator: ".")) + let rawTitleString = item.strings.ChatList_StorageHintTitle(sizeString) + let titleStringValue = NSMutableAttributedString(attributedString: NSAttributedString(string: rawTitleString.string, font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor)) + if let range = rawTitleString.ranges.first { + titleStringValue.addAttribute(.foregroundColor, value: item.theme.rootController.navigationBar.accentTextColor, range: range.range) + } + titleString = titleStringValue + + textString = NSAttributedString(string: item.strings.ChatList_StorageHintText, font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor) + case .setupPassword: + titleString = NSAttributedString(string: item.strings.Settings_SuggestSetupPasswordTitle, font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor) + textString = NSAttributedString(string: item.strings.Settings_SuggestSetupPasswordText, font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor) + case let .premiumUpgrade(discount): + let discountString = "\(discount)%" + let rawTitleString = item.strings.ChatList_PremiumAnnualUpgradeTitle(discountString) + let titleStringValue = NSMutableAttributedString(attributedString: NSAttributedString(string: rawTitleString.string, font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor)) + if let range = rawTitleString.ranges.first { + titleStringValue.addAttribute(.foregroundColor, value: item.theme.rootController.navigationBar.accentTextColor, range: range.range) + } + titleString = titleStringValue + + textString = NSAttributedString(string: item.strings.ChatList_PremiumAnnualUpgradeText, font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor) + case let .premiumAnnualDiscount(discount): + let discountString = "\(discount)%" + let rawTitleString = item.strings.ChatList_PremiumAnnualDiscountTitle(discountString) + let titleStringValue = NSMutableAttributedString(attributedString: NSAttributedString(string: rawTitleString.string, font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor)) + if let range = rawTitleString.ranges.first { + titleStringValue.addAttribute(.foregroundColor, value: item.theme.rootController.navigationBar.accentTextColor, range: range.range) + } + titleString = titleStringValue + + textString = NSAttributedString(string: item.strings.ChatList_PremiumAnnualDiscountText, font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor) + titleRightInset = sideInset + case let .premiumRestore(discount): + let discountString = "\(discount)%" + let rawTitleString = item.strings.ChatList_PremiumRestoreDiscountTitle(discountString) + let titleStringValue = NSMutableAttributedString(attributedString: NSAttributedString(string: rawTitleString.string, font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor)) + if let range = rawTitleString.ranges.first { + titleStringValue.addAttribute(.foregroundColor, value: item.theme.rootController.navigationBar.accentTextColor, range: range.range) + } + titleString = titleStringValue + + textString = NSAttributedString(string: item.strings.ChatList_PremiumRestoreDiscountText, font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor) + case .xmasPremiumGift: + titleString = parseMarkdownIntoAttributedString(item.strings.ChatList_PremiumXmasGiftTitle, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor), bold: MarkdownAttributeSet(font: titleFont, textColor: item.theme.rootController.navigationBar.accentTextColor), link: MarkdownAttributeSet(font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor), linkAttribute: { _ in return nil })) + textString = NSAttributedString(string: item.strings.ChatList_PremiumXmasGiftText, font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor) + case .premiumGrace: + titleString = parseMarkdownIntoAttributedString(item.strings.ChatList_PremiumGraceTitle, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor), bold: MarkdownAttributeSet(font: titleFont, textColor: item.theme.rootController.navigationBar.accentTextColor), link: MarkdownAttributeSet(font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor), linkAttribute: { _ in return nil })) + textString = NSAttributedString(string: item.strings.ChatList_PremiumGraceText, font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor) + case .setupBirthday: + titleString = NSAttributedString(string: item.strings.ChatList_AddBirthdayTitle, font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor) + textString = NSAttributedString(string: item.strings.ChatList_AddBirthdayText, font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor) + case let .birthdayPremiumGift(peers, _): + let title: String + let text: String + if peers.count == 1, let peer = peers.first { + var peerName = peer.compactDisplayTitle + if peerName.count > 20 { + peerName = peerName.prefix(20).trimmingCharacters(in: .whitespacesAndNewlines) + "\u{2026}" + } + title = item.strings.ChatList_BirthdaySingleTitle(peerName).string + text = item.strings.ChatList_BirthdaySingleText + } else { + title = item.strings.ChatList_BirthdayMultipleTitle(Int32(peers.count)) + text = item.strings.ChatList_BirthdayMultipleText + } + titleString = parseMarkdownIntoAttributedString(title, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor), bold: MarkdownAttributeSet(font: titleFont, textColor: item.theme.rootController.navigationBar.accentTextColor), link: MarkdownAttributeSet(font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor), linkAttribute: { _ in return nil })) + textString = NSAttributedString(string: text, font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor) + avatarPeers = Array(peers.prefix(3)) + case let .reviewLogin(newSessionReview, totalCount): + spacing = 2.0 + alignment = .center + + var rawTitleString = item.strings.ChatList_SessionReview_PanelTitle + if totalCount > 1 { + rawTitleString = "1/\(totalCount) \(rawTitleString)" + } + let titleStringValue = NSMutableAttributedString(attributedString: NSAttributedString(string: rawTitleString, font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor)) + titleString = titleStringValue + + textString = NSAttributedString(string: item.strings.ChatList_SessionReview_PanelText(newSessionReview.device, newSessionReview.location).string, font: textFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor) + + okButtonLayout = makeOkButtonTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.strings.ChatList_SessionReview_PanelConfirm, font: titleFont, textColor: item.theme.list.itemAccentColor), maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - sideInset - rightInset, height: 100.0))) + cancelButtonLayout = makeCancelButtonTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.strings.ChatList_SessionReview_PanelReject, font: titleFont, textColor: item.theme.list.itemDestructiveColor), maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - sideInset - rightInset, height: 100.0))) + case let .starsSubscriptionLowBalance(amount, peers): + let title: String + let text: String + let starsValue = item.strings.ChatList_SubscriptionsLowBalance_Stars(Int32(clamping: amount.value)) + if let peer = peers.first, peers.count == 1 { + title = item.strings.ChatList_SubscriptionsLowBalance_Single_Title(starsValue, peer.compactDisplayTitle).string + text = item.strings.ChatList_SubscriptionsLowBalance_Single_Text + } else { + title = item.strings.ChatList_SubscriptionsLowBalance_Multiple_Title(starsValue).string + text = item.strings.ChatList_SubscriptionsLowBalance_Multiple_Text + } + let attributedTitle = NSMutableAttributedString(string: "⭐️\(title)", font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor) + if let range = attributedTitle.string.range(of: "⭐️") { + attributedTitle.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: false)), range: NSRange(range, in: attributedTitle.string)) + attributedTitle.addAttribute(.baselineOffset, value: 2.0, range: NSRange(range, in: attributedTitle.string)) + } + titleString = attributedTitle + textString = NSAttributedString(string: text, font: smallTextFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor) + case let .setupPhoto(accountPeer): + titleString = NSAttributedString(string: item.strings.ChatList_AddPhoto_Title, font: titleFont, textColor: item.theme.rootController.navigationBar.primaryTextColor) + textString = NSAttributedString(string: item.strings.ChatList_AddPhoto_Text, font: smallTextFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor) + avatarPeer = accountPeer + case .accountFreeze: + titleString = NSAttributedString(string: item.strings.ChatList_FrozenAccount_Title, font: titleFont, textColor: item.theme.list.itemDestructiveColor) + textString = NSAttributedString(string: item.strings.ChatList_FrozenAccount_Text, font: smallTextFont, textColor: item.theme.rootController.navigationBar.secondaryTextColor) + case let .link(_, _, title, subtitle): + titleString = stringWithAppliedEntities(title.string, entities: title.entities, baseColor: item.theme.list.itemPrimaryTextColor, linkColor: item.theme.list.itemAccentColor, baseFont: titleFont, linkFont: titleFont, boldFont: titleBoldFont, italicFont: titleItalicFont, boldItalicFont: titleBoldItalicFont, fixedFont: titleFont, blockQuoteFont: titleFont, message: nil) + textString = stringWithAppliedEntities(subtitle.string, entities: subtitle.entities, baseColor: item.theme.list.itemPrimaryTextColor, linkColor: item.theme.list.itemAccentColor, baseFont: textFont, linkFont: textFont, boldFont: textBoldFont, italicFont: textItalicFont, boldItalicFont: textBoldItalicFont, fixedFont: textFont, blockQuoteFont: textFont, message: nil) + } + + var leftInset: CGFloat = sideInset + if !avatarPeers.isEmpty { + let avatarsWidth = 30.0 + CGFloat(avatarPeers.count - 1) * 16.0 + leftInset += avatarsWidth + 4.0 + } else if let _ = avatarPeer { + let avatarsWidth: CGFloat = 40.0 + leftInset += avatarsWidth + 6.0 + } + + let titleLayout = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleString, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - titleRightInset, height: 100.0), alignment: alignment, lineSpacing: 0.18)) + + let textLayout = makeTextLayout(TextNodeLayoutArguments(attributedString: textString, maximumNumberOfLines: 10, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset, height: 100.0), alignment: alignment, lineSpacing: 0.18)) + + var contentSize = CGSize(width: params.width, height: verticalInset * 2.0 + titleLayout.0.size.height + textLayout.0.size.height) + if let okButtonLayout { + contentSize.height += okButtonLayout.0.size.height + 20.0 + } + + let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: UIEdgeInsets()) + + return (layout, { [weak self] in + if let strongSelf = self { + strongSelf.item = item + + if themeUpdated { + strongSelf.arrowNode.image = PresentationResourcesItemList.disclosureArrowImage(item.theme) + } + + let _ = titleLayout.1(TextNodeWithEntities.Arguments(context: item.context, cache: item.context.animationCache, renderer: item.context.animationRenderer, placeholderColor: .white, attemptSynchronous: true)) + if case .center = alignment { + strongSelf.titleNode.textNode.frame = CGRect(origin: CGPoint(x: floor((params.width - titleLayout.0.size.width) * 0.5), y: verticalInset), size: titleLayout.0.size) + } else { + strongSelf.titleNode.textNode.frame = CGRect(origin: CGPoint(x: leftInset, y: verticalInset), size: titleLayout.0.size) + } + + let _ = textLayout.1(TextNodeWithEntities.Arguments(context: item.context, cache: item.context.animationCache, renderer: item.context.animationRenderer, placeholderColor: .white, attemptSynchronous: true)) + + strongSelf.titleNode.visibilityRect = CGRect(origin: CGPoint(), size: CGSize(width: 1000000.0, height: 1000000.0)) + strongSelf.textNode.visibilityRect = CGRect(origin: CGPoint(), size: CGSize(width: 1000000.0, height: 1000000.0)) + + if case .center = alignment { + strongSelf.textNode.textNode.frame = CGRect(origin: CGPoint(x: floor((params.width - textLayout.0.size.width) * 0.5), y: strongSelf.titleNode.textNode.frame.maxY + spacing), size: textLayout.0.size) + } else { + strongSelf.textNode.textNode.frame = CGRect(origin: CGPoint(x: leftInset, y: strongSelf.titleNode.textNode.frame.maxY + spacing), size: textLayout.0.size) + } + + if !avatarPeers.isEmpty { + let avatarsNode: MergedAvatarsNode + if let current = strongSelf.avatarsNode { + avatarsNode = current + } else { + avatarsNode = MergedAvatarsNode() + avatarsNode.isUserInteractionEnabled = false + strongSelf.addSubnode(avatarsNode) + strongSelf.avatarsNode = avatarsNode + } + let avatarSize = CGSize(width: 30.0, height: 30.0) + avatarsNode.update(context: item.context, peers: avatarPeers.map { $0._asPeer() }, synchronousLoad: false, imageSize: avatarSize.width, imageSpacing: 16.0, borderWidth: 2.0 - UIScreenPixel, avatarFontSize: 10.0) + let avatarsSize = CGSize(width: avatarSize.width + 16.0 * CGFloat(avatarPeers.count - 1), height: avatarSize.height) + avatarsNode.updateLayout(size: avatarsSize) + avatarsNode.frame = CGRect(origin: CGPoint(x: sideInset - 6.0, y: floor((layout.size.height - avatarsSize.height) / 2.0)), size: avatarsSize) + } else if let avatarsNode = strongSelf.avatarsNode { + avatarsNode.removeFromSupernode() + strongSelf.avatarsNode = nil + } + + if let avatarPeer { + let avatarNode: AvatarNode + if let current = strongSelf.avatarNode { + avatarNode = current + } else { + avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 13.0)) + avatarNode.isUserInteractionEnabled = false + strongSelf.addSubnode(avatarNode) + strongSelf.avatarNode = avatarNode + + avatarNode.setPeer(context: item.context, theme: item.theme, peer: avatarPeer, overrideImage: .cameraIcon) + } + let avatarSize = CGSize(width: 40.0, height: 40.0) + avatarNode.frame = CGRect(origin: CGPoint(x: sideInset - 6.0, y: floor((layout.size.height - avatarSize.height) / 2.0)), size: avatarSize) + } else if let avatarNode = strongSelf.avatarNode { + avatarNode.removeFromSupernode() + strongSelf.avatarNode = nil + } + + if let image = strongSelf.arrowNode.image { + strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: layout.size.width - sideInset - image.size.width + 8.0, y: floor((layout.size.height - image.size.height) / 2.0)), size: image.size) + } + + let hasCloseButton: Bool + switch item.notice { + case .xmasPremiumGift, .setupBirthday, .birthdayPremiumGift, .premiumGrace, .starsSubscriptionLowBalance, .setupPhoto, .link: + hasCloseButton = true + default: + hasCloseButton = false + } + + if let okButtonLayout, let cancelButtonLayout { + strongSelf.arrowNode.isHidden = true + strongSelf.closeButton?.isHidden = true + + let okButton: HighlightableButtonNode + if let current = strongSelf.okButton { + okButton = current + } else { + okButton = HighlightableButtonNode() + strongSelf.okButton = okButton + strongSelf.contentContainer.addSubnode(okButton) + okButton.addTarget(strongSelf, action: #selector(strongSelf.okButtonPressed), forControlEvents: .touchUpInside) + } + + let cancelButton: HighlightableButtonNode + if let current = strongSelf.cancelButton { + cancelButton = current + } else { + cancelButton = HighlightableButtonNode() + strongSelf.cancelButton = cancelButton + strongSelf.contentContainer.addSubnode(cancelButton) + cancelButton.addTarget(strongSelf, action: #selector(strongSelf.cancelButtonPressed), forControlEvents: .touchUpInside) + } + + let okButtonText = okButtonLayout.1() + if okButtonText !== strongSelf.okButtonText { + strongSelf.okButtonText?.removeFromSupernode() + strongSelf.okButtonText = okButtonText + okButton.addSubnode(okButtonText) + } + + let cancelButtonText = cancelButtonLayout.1() + if cancelButtonText !== strongSelf.okButtonText { + strongSelf.cancelButtonText?.removeFromSupernode() + strongSelf.cancelButtonText = cancelButtonText + cancelButton.addSubnode(cancelButtonText) + } + + let buttonsWidth: CGFloat = max(min(300.0, params.width), okButtonLayout.0.size.width + cancelButtonLayout.0.size.width + 32.0) + let buttonWidth: CGFloat = floor(buttonsWidth * 0.5) + let buttonHeight: CGFloat = 32.0 + + let okButtonFrame = CGRect(origin: CGPoint(x: floor((params.width - buttonsWidth) * 0.5), y: strongSelf.textNode.textNode.frame.maxY + 6.0), size: CGSize(width: buttonWidth, height: buttonHeight)) + let cancelButtonFrame = CGRect(origin: CGPoint(x: okButtonFrame.maxX, y: strongSelf.textNode.textNode.frame.maxY + 6.0), size: CGSize(width: buttonWidth, height: buttonHeight)) + + okButton.frame = okButtonFrame + cancelButton.frame = cancelButtonFrame + + okButtonText.frame = CGRect(origin: CGPoint(x: floor((okButtonFrame.width - okButtonLayout.0.size.width) * 0.5), y: floor((okButtonFrame.height - okButtonLayout.0.size.height) * 0.5)), size: okButtonLayout.0.size) + cancelButtonText.frame = CGRect(origin: CGPoint(x: floor((cancelButtonFrame.width - cancelButtonLayout.0.size.width) * 0.5), y: floor((cancelButtonFrame.height - cancelButtonLayout.0.size.height) * 0.5)), size: cancelButtonLayout.0.size) + } else { + strongSelf.arrowNode.isHidden = hasCloseButton + + if let okButton = strongSelf.okButton { + strongSelf.okButton = nil + okButton.removeFromSupernode() + } + if let cancelButton = strongSelf.cancelButton { + strongSelf.cancelButton = nil + cancelButton.removeFromSupernode() + } + if let okButtonText = strongSelf.okButtonText { + strongSelf.okButtonText = nil + okButtonText.removeFromSupernode() + } + if let cancelButtonText = strongSelf.cancelButtonText { + strongSelf.cancelButtonText = nil + cancelButtonText.removeFromSupernode() + } + + if hasCloseButton { + let closeButton: HighlightableButtonNode + if let current = strongSelf.closeButton { + closeButton = current + } else { + closeButton = HighlightableButtonNode() + closeButton.hitTestSlop = UIEdgeInsets(top: -8.0, left: -8.0, bottom: -8.0, right: -8.0) + closeButton.addTarget(self, action: #selector(strongSelf.closePressed), forControlEvents: [.touchUpInside]) + strongSelf.contentContainer.addSubnode(closeButton) + strongSelf.closeButton = closeButton + } + + if themeUpdated || closeButton.image(for: .normal) == nil { + closeButton.setImage(PresentationResourcesItemList.itemListCloseIconImage(item.theme), for: .normal) + } + + let closeButtonSize = closeButton.measure(CGSize(width: 100.0, height: 100.0)) + closeButton.frame = CGRect(origin: CGPoint(x: layout.size.width - sideInset - closeButtonSize.width, y: floor((layout.size.height - closeButtonSize.height) / 2.0)), size: closeButtonSize) + } else { + strongSelf.closeButton?.removeFromSupernode() + strongSelf.closeButton = nil + } + } + + strongSelf.contentSize = layout.contentSize + strongSelf.insets = layout.insets + + strongSelf.updateLayout(size: layout.contentSize, leftInset: params.leftInset, rightInset: params.rightInset) + + strongSelf.contentContainer.frame = CGRect(origin: CGPoint(), size: layout.contentSize) + + switch item.notice { + default: + strongSelf.setRevealOptions((left: [], right: [])) + } + } + }) + } + } + + override public func selected() { + super.selected() + + if case .setupPhoto = self.item?.notice { + self.avatarNode?.playCameraAnimation() + } + } + + @objc private func okButtonPressed() { + self.item?.action(.buttonChoice(isPositive: true)) + } + + @objc private func cancelButtonPressed() { + self.item?.action(.buttonChoice(isPositive: false)) + } + + override public func animateInsertion(_ currentTimestamp: Double, duration: Double, options: ListViewItemAnimationOptions) { + super.animateInsertion(currentTimestamp, duration: duration, options: options) + + //self.transitionOffset = self.bounds.size.height + //self.addTransitionOffsetAnimation(0.0, duration: duration, beginAt: currentTimestamp) + } + + override public func updateRevealOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) { + super.updateRevealOffset(offset: offset, transition: transition) + + transition.updateSublayerTransformOffset(layer: self.contentContainer.layer, offset: CGPoint(x: offset, y: 0.0)) + } + + override public func revealOptionSelected(_ option: ItemListRevealOption, animated: Bool) { + if let item = self.item { + item.action(.hide) + } + + self.setRevealOptionsOpened(false, animated: true) + self.revealOptionsInteractivelyClosed() + } +} diff --git a/submodules/TelegramUI/Components/ChatList/ChatListSearchFiltersContainerNode/BUILD b/submodules/TelegramUI/Components/ChatList/ChatListSearchFiltersContainerNode/BUILD new file mode 100644 index 00000000..74b602e4 --- /dev/null +++ b/submodules/TelegramUI/Components/ChatList/ChatListSearchFiltersContainerNode/BUILD @@ -0,0 +1,26 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "ChatListSearchFiltersContainerNode", + module_name = "ChatListSearchFiltersContainerNode", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/TelegramCore", + "//submodules/Postbox", + "//submodules/TelegramPresentationData", + "//submodules/AccountContext", + "//submodules/TelegramUI/Components/GlassBackgroundComponent", + "//submodules/ComponentFlow", + "//submodules/Components/ComponentDisplayAdapters", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/ChatList/ChatListSearchFiltersContainerNode/Sources/ChatListSearchFiltersContainerNode.swift b/submodules/TelegramUI/Components/ChatList/ChatListSearchFiltersContainerNode/Sources/ChatListSearchFiltersContainerNode.swift new file mode 100644 index 00000000..8b66c1bf --- /dev/null +++ b/submodules/TelegramUI/Components/ChatList/ChatListSearchFiltersContainerNode/Sources/ChatListSearchFiltersContainerNode.swift @@ -0,0 +1,295 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import Postbox +import TelegramCore +import TelegramPresentationData +import AccountContext +import GlassBackgroundComponent +import ComponentFlow +import ComponentDisplayAdapters + +public enum ChatListSearchFilterEntryId: Hashable { + case filter(Int64) +} + +public enum ChatListSearchFilterEntry: Equatable { + case filter(ChatListSearchFilter) + + public var id: ChatListSearchFilterEntryId { + switch self { + case let .filter(filter): + return .filter(filter.id) + } + } +} + +public final class ChatListSearchFiltersContainerNode: ASDisplayNode { + private let backgroundContainer: GlassBackgroundContainerView + private let backgroundView: GlassBackgroundView + + private let scrollNode: ASScrollNode + private let selectionView: UIImageView + private var itemNodes: [ChatListSearchFilterEntryId: ItemNode] = [:] + + public var filterPressed: ((ChatListSearchFilter) -> Void)? + + private var currentParams: (size: CGSize, sideInset: CGFloat, filters: [ChatListSearchFilterEntry], selectedFilter: ChatListSearchFilterEntryId?, transitionFraction: CGFloat, presentationData: PresentationData)? + + private var previousSelectedAbsFrame: CGRect? + private var previousSelectedFrame: CGRect? + + override public init() { + self.backgroundContainer = GlassBackgroundContainerView() + self.backgroundView = GlassBackgroundView() + self.backgroundContainer.contentView.addSubview(self.backgroundView) + + self.scrollNode = ASScrollNode() + + self.selectionView = UIImageView() + + super.init() + + self.scrollNode.view.showsHorizontalScrollIndicator = false + self.scrollNode.view.scrollsToTop = false + self.scrollNode.view.delaysContentTouches = false + self.scrollNode.view.canCancelContentTouches = true + self.scrollNode.view.contentInsetAdjustmentBehavior = .never + + self.view.addSubview(self.backgroundContainer) + + self.backgroundView.contentView.addSubview(self.scrollNode.view) + self.scrollNode.view.addSubview(self.selectionView) + } + + public func cancelAnimations() { + self.scrollNode.layer.removeAllAnimations() + } + + public func update(size: CGSize, sideInset: CGFloat, filters: [ChatListSearchFilterEntry], displayGlobalPostsNewBadge: Bool, selectedFilter: ChatListSearchFilterEntryId?, transitionFraction: CGFloat, presentationData: PresentationData, transition proposedTransition: ContainedViewLayoutTransition) { + let isFirstTime = self.currentParams == nil + let transition: ContainedViewLayoutTransition = isFirstTime ? .immediate : proposedTransition + + let componentTransition = ComponentTransition(transition) + + componentTransition.setFrame(view: self.backgroundContainer, frame: CGRect(origin: CGPoint(), size: size)) + self.backgroundContainer.update(size: size, isDark: presentationData.theme.overallDarkAppearance, transition: componentTransition) + componentTransition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: size)) + self.backgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: presentationData.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: componentTransition) + self.scrollNode.view.layer.cornerRadius = size.height * 0.5 + + var focusOnSelectedFilter = self.currentParams?.selectedFilter != selectedFilter + let previousScrollBounds = self.scrollNode.bounds + let previousContentWidth = self.scrollNode.view.contentSize.width + + self.currentParams = (size: size, sideInset: sideInset, filters: filters, selectedFilter: selectedFilter, transitionFraction: transitionFraction, presentationData: presentationData) + + transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: size)) + + var hasSelection = false + for i in 0 ..< filters.count { + let filter = filters[i] + if case let .filter(type) = filter { + let itemNode: ItemNode + var itemNodeTransition = transition + if let current = self.itemNodes[filter.id] { + itemNode = current + } else { + itemNodeTransition = .immediate + itemNode = ItemNode(pressed: { [weak self] in + self?.filterPressed?(type) + }) + self.itemNodes[filter.id] = itemNode + } + + let selectionFraction: CGFloat + if selectedFilter == filter.id { + selectionFraction = 1.0 - abs(transitionFraction) + hasSelection = true + } else if i != 0 && selectedFilter == filters[i - 1].id { + selectionFraction = max(0.0, -transitionFraction) + } else if i != filters.count - 1 && selectedFilter == filters[i + 1].id { + selectionFraction = max(0.0, transitionFraction) + } else { + selectionFraction = 0.0 + } + + var displayNewBadge = false + if case .globalPosts = type { + displayNewBadge = displayGlobalPostsNewBadge + } + + itemNode.update(type: type, displayNewBadge: displayNewBadge, presentationData: presentationData, selectionFraction: selectionFraction, transition: itemNodeTransition) + } + } + + var updated = false + + var removeKeys: [ChatListSearchFilterEntryId] = [] + for (id, _) in self.itemNodes { + if !filters.contains(where: { $0.id == id }) { + removeKeys.append(id) + updated = true + } + } + for id in removeKeys { + if let itemNode = self.itemNodes.removeValue(forKey: id) { + transition.updateAlpha(node: itemNode, alpha: 0.0, completion: { [weak itemNode] _ in + itemNode?.removeFromSupernode() + }) + transition.updateTransformScale(node: itemNode, scale: 0.1) + } + } + + var tabSizes: [(ChatListSearchFilterEntryId, CGSize, ItemNode, Bool)] = [] + var totalRawTabSize: CGFloat = 0.0 + var selectionFrames: [CGRect] = [] + + for filter in filters { + guard let itemNode = self.itemNodes[filter.id] else { + continue + } + let wasAdded = itemNode.supernode == nil + var itemNodeTransition = transition + if wasAdded { + itemNodeTransition = .immediate + self.scrollNode.addSubnode(itemNode) + } + let paneNodeWidth = itemNode.updateLayout(height: size.height, transition: itemNodeTransition) + let paneNodeSize = CGSize(width: paneNodeWidth, height: size.height) + tabSizes.append((filter.id, paneNodeSize, itemNode, wasAdded)) + totalRawTabSize += paneNodeSize.width + } + + let minSpacing: CGFloat = 24.0 + var spacing = minSpacing + + let resolvedSideInset: CGFloat = 16.0 + sideInset + var leftOffset: CGFloat = resolvedSideInset + + var longTitlesWidth: CGFloat = resolvedSideInset + var titlesWidth: CGFloat = 0.0 + for i in 0 ..< tabSizes.count { + let (_, paneNodeSize, _, _) = tabSizes[i] + longTitlesWidth += paneNodeSize.width + titlesWidth += paneNodeSize.width + if i != tabSizes.count - 1 { + longTitlesWidth += minSpacing + } + } + longTitlesWidth += resolvedSideInset + + if longTitlesWidth < size.width && hasSelection { + spacing = (size.width - titlesWidth - resolvedSideInset * 2.0) / CGFloat(tabSizes.count - 1) + } + + let verticalOffset: CGFloat = -4.0 + for i in 0 ..< tabSizes.count { + let (_, paneNodeSize, paneNode, wasAdded) = tabSizes[i] + let itemNodeTransition = transition + + let paneFrame = CGRect(origin: CGPoint(x: leftOffset, y: floor((size.height - paneNodeSize.height) / 2.0) + verticalOffset), size: paneNodeSize) + + var effectiveWasAdded = wasAdded + if !effectiveWasAdded && !self.bounds.intersects(self.scrollNode.view.convert(paneNode.frame, to: self.view)) && self.bounds.intersects(self.scrollNode.view.convert(paneFrame, to: self.view)) { + effectiveWasAdded = true + } + + if effectiveWasAdded { + paneNode.frame = paneFrame + paneNode.alpha = 0.0 + paneNode.subnodeTransform = CATransform3DMakeScale(0.1, 0.1, 1.0) + itemNodeTransition.updateSublayerTransformScale(node: paneNode, scale: 1.0) + itemNodeTransition.updateAlpha(node: paneNode, alpha: 1.0) + } else { + if self.bounds.intersects(self.scrollNode.view.convert(paneFrame, to: self.view)) { + itemNodeTransition.updateFrameAdditive(node: paneNode, frame: paneFrame) + } else if paneNode.frame != paneFrame { + paneNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.4) { [weak paneNode] _ in + paneNode?.frame = paneFrame + } + } + } + + paneNode.updateArea(size: paneFrame.size, sideInset: spacing / 2.0, transition: itemNodeTransition) + paneNode.hitTestSlop = UIEdgeInsets(top: 0.0, left: -spacing / 2.0, bottom: 0.0, right: -spacing / 2.0) + + selectionFrames.append(paneFrame) + + leftOffset += paneNodeSize.width + spacing + } + leftOffset -= spacing + leftOffset += resolvedSideInset + + self.scrollNode.view.contentSize = CGSize(width: leftOffset, height: size.height) + + var selectedFrame: CGRect? + if let selectedFilter = selectedFilter, let currentIndex = filters.firstIndex(where: { $0.id == selectedFilter }) { + func interpolateFrame(from fromValue: CGRect, to toValue: CGRect, t: CGFloat) -> CGRect { + return CGRect(x: floorToScreenPixels(toValue.origin.x * t + fromValue.origin.x * (1.0 - t)), y: floorToScreenPixels(toValue.origin.y * t + fromValue.origin.y * (1.0 - t)), width: floorToScreenPixels(toValue.size.width * t + fromValue.size.width * (1.0 - t)), height: floorToScreenPixels(toValue.size.height * t + fromValue.size.height * (1.0 - t))) + } + + if currentIndex != 0 && transitionFraction > 0.0 { + let currentFrame = selectionFrames[currentIndex] + let previousFrame = selectionFrames[currentIndex - 1] + selectedFrame = interpolateFrame(from: currentFrame, to: previousFrame, t: abs(transitionFraction)) + } else if currentIndex != filters.count - 1 && transitionFraction < 0.0 { + let currentFrame = selectionFrames[currentIndex] + let previousFrame = selectionFrames[currentIndex + 1] + selectedFrame = interpolateFrame(from: currentFrame, to: previousFrame, t: abs(transitionFraction)) + } else { + selectedFrame = selectionFrames[currentIndex] + } + } + + if let selectedFrame { + let wasAdded = self.selectionView.isHidden + let selectionFrame = CGRect(origin: CGPoint(x: selectedFrame.minX - 13.0, y: 3.0), size: CGSize(width: selectedFrame.width + 26.0, height: size.height - 3.0 * 2.0)) + if wasAdded { + self.selectionView.frame = selectionFrame + ComponentTransition(transition).animateAlpha(view: self.selectionView, from: 0.0, to: 1.0) + } else { + transition.updateFrame(view: self.selectionView, frame: selectionFrame) + } + self.selectionView.isHidden = false + + if self.selectionView.image?.size.height != selectionFrame.height { + self.selectionView.image = generateStretchableFilledCircleImage(diameter: selectionFrame.height, color: .white)?.withRenderingMode(.alwaysTemplate) + } + self.selectionView.tintColor = presentationData.theme.chat.inputPanel.panelControlColor.withAlphaComponent(0.1) + + if let previousSelectedFrame = self.previousSelectedFrame { + let previousContentOffsetX = max(0.0, min(previousContentWidth - previousScrollBounds.width, floor(previousSelectedFrame.midX - previousScrollBounds.width / 2.0))) + if abs(previousContentOffsetX - previousScrollBounds.minX) < 1.0 { + focusOnSelectedFilter = true + } + } + + if focusOnSelectedFilter { + let updatedBounds: CGRect + if transitionFraction.isZero && selectedFilter == filters.first?.id { + updatedBounds = CGRect(origin: CGPoint(), size: self.scrollNode.bounds.size) + } else if transitionFraction.isZero && selectedFilter == filters.last?.id { + updatedBounds = CGRect(origin: CGPoint(x: max(0.0, self.scrollNode.view.contentSize.width - self.scrollNode.bounds.width), y: 0.0), size: self.scrollNode.bounds.size) + } else { + let contentOffsetX = max(0.0, min(self.scrollNode.view.contentSize.width - self.scrollNode.bounds.width, floor(selectedFrame.midX - self.scrollNode.bounds.width / 2.0))) + updatedBounds = CGRect(origin: CGPoint(x: contentOffsetX, y: 0.0), size: self.scrollNode.bounds.size) + } + self.scrollNode.bounds = updatedBounds + } + transition.animateHorizontalOffsetAdditive(node: self.scrollNode, offset: previousScrollBounds.minX - self.scrollNode.bounds.minX) + + self.previousSelectedAbsFrame = selectedFrame.offsetBy(dx: -self.scrollNode.bounds.minX, dy: 0.0) + self.previousSelectedFrame = selectedFrame + } else { + self.selectionView.isHidden = true + self.previousSelectedAbsFrame = nil + self.previousSelectedFrame = nil + } + + if updated && self.scrollNode.view.contentOffset.x > 0.0 { + self.scrollNode.view.contentOffset = CGPoint() + } + } +} diff --git a/submodules/TelegramUI/Components/ChatList/ChatListSearchFiltersContainerNode/Sources/ItemNode.swift b/submodules/TelegramUI/Components/ChatList/ChatListSearchFiltersContainerNode/Sources/ItemNode.swift new file mode 100644 index 00000000..d5d2eaab --- /dev/null +++ b/submodules/TelegramUI/Components/ChatList/ChatListSearchFiltersContainerNode/Sources/ItemNode.swift @@ -0,0 +1,206 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import TelegramPresentationData +import TelegramCore +import AccountContext + +final class ItemNode: ASDisplayNode { + private let pressed: () -> Void + + private let iconNode: ASImageNode + private let titleNode: ImmediateTextNode + private var titleBadgeView: UIImageView? + private let buttonNode: HighlightTrackingButtonNode + + private var selectionFraction: CGFloat = 0.0 + + private var theme: PresentationTheme? + + init(pressed: @escaping () -> Void) { + self.pressed = pressed + + let titleInset: CGFloat = 4.0 + + self.iconNode = ASImageNode() + self.iconNode.displaysAsynchronously = false + self.iconNode.displayWithoutProcessing = true + + self.titleNode = ImmediateTextNode() + self.titleNode.displaysAsynchronously = false + self.titleNode.insets = UIEdgeInsets(top: titleInset, left: 0.0, bottom: titleInset, right: 0.0) + + self.buttonNode = HighlightTrackingButtonNode() + + super.init() + + self.addSubnode(self.titleNode) + self.addSubnode(self.iconNode) + self.addSubnode(self.buttonNode) + + self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside) + self.buttonNode.highligthedChanged = { [weak self] highlighted in + if let strongSelf = self { + if highlighted { + strongSelf.iconNode.layer.removeAnimation(forKey: "opacity") + strongSelf.iconNode.alpha = 0.4 + + strongSelf.titleNode.layer.removeAnimation(forKey: "opacity") + strongSelf.titleNode.alpha = 0.4 + } else { + strongSelf.iconNode.alpha = 1.0 + strongSelf.iconNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + + strongSelf.titleNode.alpha = 1.0 + strongSelf.titleNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + } + } + } + } + + @objc private func buttonPressed() { + self.pressed() + } + + func update(type: ChatListSearchFilter, displayNewBadge: Bool, presentationData: PresentationData, selectionFraction: CGFloat, transition: ContainedViewLayoutTransition) { + self.selectionFraction = selectionFraction + + let title: String + var titleBadge: String? + let icon: UIImage? + + let color = presentationData.theme.chat.inputPanel.panelControlColor + switch type { + case .chats: + title = presentationData.strings.ChatList_Search_FilterChats + icon = nil + case .topics: + title = presentationData.strings.ChatList_Search_FilterChats + icon = nil + case .channels: + title = presentationData.strings.ChatList_Search_FilterChannels + icon = nil + case .apps: + title = presentationData.strings.ChatList_Search_FilterApps + icon = nil + case .globalPosts: + title = presentationData.strings.ChatList_Search_FilterGlobalPosts + if displayNewBadge { + titleBadge = presentationData.strings.ChatList_ContextMenuBadgeNew + } + icon = nil + case .media: + title = presentationData.strings.ChatList_Search_FilterMedia + icon = nil + case .downloads: + title = presentationData.strings.ChatList_Search_FilterDownloads + icon = nil + case .links: + title = presentationData.strings.ChatList_Search_FilterLinks + icon = nil + case .files: + title = presentationData.strings.ChatList_Search_FilterFiles + icon = nil + case .music: + title = presentationData.strings.ChatList_Search_FilterMusic + icon = nil + case .voice: + title = presentationData.strings.ChatList_Search_FilterVoice + icon = nil + case .instantVideo: + title = presentationData.strings.ChatList_Search_FilterVoice + icon = nil + case .publicPosts: + title = presentationData.strings.ChatList_Search_FilterPublicPosts + icon = nil + case let .peer(peerId, isGroup, displayTitle, _): + title = displayTitle + let image: UIImage? + if isGroup { + image = UIImage(bundleImageName: "Chat List/Search/Group") + } else if peerId.namespace == Namespaces.Peer.CloudChannel { + image = UIImage(bundleImageName: "Chat List/Search/Channel") + } else { + image = UIImage(bundleImageName: "Chat List/Search/User") + } + icon = generateTintedImage(image: image, color: color) + case let .date(_, _, displayTitle): + title = displayTitle + icon = generateTintedImage(image: UIImage(bundleImageName: "Chat List/Search/Calendar"), color: color) + } + + self.titleNode.attributedText = NSAttributedString(string: title, font: Font.medium(15.0), textColor: color) + + if let titleBadge { + let titleBadgeView: UIImageView + if let current = self.titleBadgeView { + titleBadgeView = current + } else { + titleBadgeView = UIImageView() + self.titleBadgeView = titleBadgeView + self.view.addSubview(titleBadgeView) + + let labelText = NSAttributedString(string: titleBadge, font: Font.medium(11.0), textColor: presentationData.theme.list.itemCheckColors.foregroundColor) + let labelBounds = labelText.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: [.usesLineFragmentOrigin], context: nil) + let labelSize = CGSize(width: ceil(labelBounds.width), height: ceil(labelBounds.height)) + let badgeSize = CGSize(width: labelSize.width + 8.0, height: labelSize.height + 2.0 + 1.0) + titleBadgeView.image = generateImage(badgeSize, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + let rect = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height - UIScreenPixel * 2.0)) + + context.addPath(UIBezierPath(roundedRect: rect, cornerRadius: 5.0).cgPath) + context.setFillColor(presentationData.theme.list.itemCheckColors.fillColor.cgColor) + context.fillPath() + + UIGraphicsPushContext(context) + labelText.draw(at: CGPoint(x: 4.0, y: 1.0 + UIScreenPixel)) + UIGraphicsPopContext() + }) + } + } else if let titleBadgeView = self.titleBadgeView { + self.titleBadgeView = nil + titleBadgeView.removeFromSuperview() + } + + self.buttonNode.accessibilityLabel = title + if selectionFraction == 1.0 { + self.buttonNode.accessibilityTraits = [.button, .selected] + } else { + self.buttonNode.accessibilityTraits = [.button] + } + + if self.theme !== presentationData.theme { + self.theme = presentationData.theme + self.iconNode.image = icon + } + } + + func updateLayout(height: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat { + var iconInset: CGFloat = 0.0 + if let image = self.iconNode.image { + iconInset = 22.0 + self.iconNode.frame = CGRect(x: 0.0, y: 4.0 + floorToScreenPixels((height - image.size.height) / 2.0), width: image.size.width, height: image.size.height) + } + + let titleSize = self.titleNode.updateLayout(CGSize(width: 160.0, height: .greatestFiniteMagnitude)) + let titleFrame = CGRect(origin: CGPoint(x: -self.titleNode.insets.left + iconInset, y: self.titleNode.insets.top + floorToScreenPixels((height - titleSize.height) / 2.0)), size: titleSize) + self.titleNode.frame = titleFrame + + var width = titleSize.width - self.titleNode.insets.left - self.titleNode.insets.right + iconInset + + if let titleBadgeView = self.titleBadgeView, let image = titleBadgeView.image { + width += 4.0 + image.size.width + titleBadgeView.frame = CGRect(origin: CGPoint(x: titleFrame.maxX + 4.0, y: titleFrame.minY + floorToScreenPixels((titleFrame.height - image.size.height) * 0.5) + 1.0), size: image.size) + } + + return width + } + + func updateArea(size: CGSize, sideInset: CGFloat, transition: ContainedViewLayoutTransition) { + self.buttonNode.frame = CGRect(origin: CGPoint(x: -sideInset, y: 0.0), size: CGSize(width: size.width + sideInset * 2.0, height: size.height)) + + self.hitTestSlop = UIEdgeInsets(top: 0.0, left: -sideInset, bottom: 0.0, right: -sideInset) + } +} diff --git a/submodules/TelegramUI/Components/ChatListHeaderComponent/BUILD b/submodules/TelegramUI/Components/ChatListHeaderComponent/BUILD index 6f94cb0c..5355cc42 100644 --- a/submodules/TelegramUI/Components/ChatListHeaderComponent/BUILD +++ b/submodules/TelegramUI/Components/ChatListHeaderComponent/BUILD @@ -24,6 +24,8 @@ swift_library( "//submodules/Components/ComponentDisplayAdapters", "//submodules/SearchUI", "//submodules/TelegramUI/Components/MoreHeaderButton", + "//submodules/TelegramUI/Components/EdgeEffect", + "//submodules/TelegramUI/Components/GlassBackgroundComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListHeaderComponent.swift b/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListHeaderComponent.swift index 8e235da5..b85ffefe 100644 --- a/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListHeaderComponent.swift +++ b/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListHeaderComponent.swift @@ -9,6 +9,7 @@ import AppBundle import StoryPeerListComponent import TelegramCore import MoreHeaderButton +import GlassBackgroundComponent public final class HeaderNetworkStatusComponent: Component { public enum Content: Equatable { @@ -79,7 +80,6 @@ public final class ChatListHeaderComponent: Component { public let chatListTitle: NetworkStatusTitle? public let leftButton: AnyComponentWithIdentity? public let rightButtons: [AnyComponentWithIdentity] - public let backTitle: String? public let backPressed: (() -> Void)? public init( @@ -89,7 +89,6 @@ public final class ChatListHeaderComponent: Component { chatListTitle: NetworkStatusTitle?, leftButton: AnyComponentWithIdentity?, rightButtons: [AnyComponentWithIdentity], - backTitle: String?, backPressed: (() -> Void)? ) { self.title = title @@ -98,7 +97,6 @@ public final class ChatListHeaderComponent: Component { self.chatListTitle = chatListTitle self.leftButton = leftButton self.rightButtons = rightButtons - self.backTitle = backTitle self.backPressed = backPressed } @@ -121,7 +119,7 @@ public final class ChatListHeaderComponent: Component { if lhs.rightButtons != rhs.rightButtons { return false } - if lhs.backTitle != rhs.backTitle { + if (lhs.backPressed == nil) != (rhs.backPressed == nil) { return false } return true @@ -226,8 +224,6 @@ public final class ChatListHeaderComponent: Component { private let onPressed: () -> Void let arrowView: UIImageView - let titleOffsetContainer: UIView - let titleView: ImmediateTextView private var currentColor: UIColor? @@ -235,16 +231,11 @@ public final class ChatListHeaderComponent: Component { self.onPressed = onPressed self.arrowView = UIImageView() - self.titleOffsetContainer = UIView() - self.titleView = ImmediateTextView() super.init(frame: CGRect()) self.addSubview(self.arrowView) - self.addSubview(self.titleOffsetContainer) - self.titleOffsetContainer.addSubview(self.titleView) - self.highligthedChanged = { [weak self] highlighted in guard let self else { return @@ -266,37 +257,37 @@ public final class ChatListHeaderComponent: Component { self.onPressed() } - func update(title: String, theme: PresentationTheme, availableSize: CGSize, transition: ComponentTransition) -> CGSize { - let titleText = NSAttributedString(string: title, font: Font.regular(17.0), textColor: theme.rootController.navigationBar.accentTextColor) - let titleTextUpdated = self.titleView.attributedText != titleText - self.titleView.attributedText = titleText - let titleSize = self.titleView.updateLayout(CGSize(width: 100.0, height: 44.0)) - - self.accessibilityLabel = title + func update(theme: PresentationTheme, strings: PresentationStrings, availableSize: CGSize, transition: ComponentTransition) -> CGSize { + self.accessibilityLabel = strings.Common_Back self.accessibilityTraits = [.button] - if self.currentColor != theme.rootController.navigationBar.accentTextColor { - self.currentColor = theme.rootController.navigationBar.accentTextColor - self.arrowView.image = NavigationBarTheme.generateBackArrowImage(color: theme.rootController.navigationBar.accentTextColor) + if self.currentColor != theme.chat.inputPanel.panelControlColor { + self.currentColor = theme.chat.inputPanel.panelControlColor + let imageSize = CGSize(width: 44.0, height: 44.0) + let topRightPoint = CGPoint(x: 24.6, y: 14.0) + let centerPoint = CGPoint(x: 17.0, y: imageSize.height * 0.5) + self.arrowView.image = generateImage(imageSize, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setStrokeColor(UIColor.white.cgColor) + context.setLineWidth(2.0) + context.setLineCap(.round) + context.setLineJoin(.round) + context.move(to: topRightPoint) + context.addLine(to: centerPoint) + context.addLine(to: CGPoint(x: topRightPoint.x, y: size.height - topRightPoint.y)) + context.strokePath() + })?.withRenderingMode(.alwaysTemplate) + self.arrowView.tintColor = theme.chat.inputPanel.panelControlColor } - let iconSpacing: CGFloat = 8.0 - let iconOffset: CGFloat = -7.0 - + let size = CGSize(width: 44.0, height: availableSize.height) let arrowSize = self.arrowView.image?.size ?? CGSize(width: 13.0, height: 22.0) - let arrowFrame = CGRect(origin: CGPoint(x: iconOffset - 1.0, y: floor((availableSize.height - arrowSize.height) / 2.0)), size: arrowSize) + let arrowFrame = arrowSize.centered(in: CGRect(origin: CGPoint(), size: size)) transition.setPosition(view: self.arrowView, position: arrowFrame.center) transition.setBounds(view: self.arrowView, bounds: CGRect(origin: CGPoint(), size: arrowFrame.size)) - let titleFrame = CGRect(origin: CGPoint(x: iconOffset - 3.0 + arrowSize.width + iconSpacing, y: floor((availableSize.height - titleSize.height) / 2.0)), size: titleSize) - if titleTextUpdated { - self.titleView.frame = titleFrame - } else { - transition.setFrame(view: self.titleView, frame: titleFrame) - } - - return CGSize(width: iconOffset + arrowSize.width + iconSpacing + titleSize.width, height: availableSize.height) + return size } } @@ -305,9 +296,9 @@ public final class ChatListHeaderComponent: Component { let openStatusSetup: (UIView) -> Void let toggleIsLocked: () -> Void - let leftButtonOffsetContainer: UIView + let leftButtonsContainer: UIView var leftButtonViews: [AnyHashable: ComponentView] = [:] - let rightButtonOffsetContainer: UIView + let rightButtonsContainer: UIView var rightButtonViews: [AnyHashable: ComponentView] = [:] var backButtonView: BackButtonView? @@ -324,6 +315,9 @@ public final class ChatListHeaderComponent: Component { private(set) var centerContentOffsetX: CGFloat = 0.0 private(set) var centerContentOrigin: CGFloat = 0.0 + + private(set) var leftButtonsWidth: CGFloat = 0.0 + private(set) var rightButtonsWidth: CGFloat = 0.0 init( backPressed: @escaping () -> Void, @@ -334,8 +328,9 @@ public final class ChatListHeaderComponent: Component { self.openStatusSetup = openStatusSetup self.toggleIsLocked = toggleIsLocked - self.leftButtonOffsetContainer = UIView() - self.rightButtonOffsetContainer = UIView() + self.leftButtonsContainer = UIView() + self.rightButtonsContainer = UIView() + self.titleOffsetContainer = UIView() self.titleScaleContainer = UIView() @@ -345,8 +340,6 @@ public final class ChatListHeaderComponent: Component { self.addSubview(self.titleOffsetContainer) self.titleOffsetContainer.addSubview(self.titleScaleContainer) - self.addSubview(self.leftButtonOffsetContainer) - self.addSubview(self.rightButtonOffsetContainer) self.titleScaleContainer.addSubview(self.titleTextView) } @@ -393,12 +386,12 @@ public final class ChatListHeaderComponent: Component { transition.setSublayerTransform(view: self.titleOffsetContainer, transform: transform) } - func updateNavigationTransitionAsPrevious(nextView: ContentView, fraction: CGFloat, transition: ComponentTransition, completion: @escaping () -> Void) { - transition.setBounds(view: self.leftButtonOffsetContainer, bounds: CGRect(origin: CGPoint(x: fraction * self.bounds.width * 0.5, y: 0.0), size: self.leftButtonOffsetContainer.bounds.size), completion: { _ in - completion() - }) - transition.setAlpha(view: self.leftButtonOffsetContainer, alpha: pow(1.0 - fraction, 2.0)) - transition.setAlpha(view: self.rightButtonOffsetContainer, alpha: pow(1.0 - fraction, 2.0)) + func updateNavigationTransitionAsPrevious(nextView: ContentView, width: CGFloat, fraction: CGFloat, transition: ComponentTransition, completion: @escaping () -> Void) { + let alphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.3) + alphaTransition.setAlpha(view: self.leftButtonsContainer, alpha: pow(1.0 - fraction, 2.0)) + alphaTransition.setBlur(layer: self.leftButtonsContainer.layer, radius: fraction * 10.0) + alphaTransition.setAlpha(view: self.rightButtonsContainer, alpha: pow(1.0 - fraction, 2.0)) + alphaTransition.setBlur(layer: self.rightButtonsContainer.layer, radius: fraction * 10.0) if let backButtonView = self.backButtonView { transition.setBounds(view: backButtonView, bounds: CGRect(origin: CGPoint(x: fraction * self.bounds.width * 0.5, y: 0.0), size: backButtonView.bounds.size), completion: { _ in @@ -406,80 +399,53 @@ public final class ChatListHeaderComponent: Component { }) } - if let chatListTitleView = self.chatListTitleView, let nextBackButtonView = nextView.backButtonView { - let titleFrame = chatListTitleView.titleNode.view.convert(chatListTitleView.titleNode.bounds, to: self.titleOffsetContainer) - let backButtonTitleFrame = nextBackButtonView.convert(nextBackButtonView.titleView.frame, to: nextView) - - let totalOffset = titleFrame.minX - backButtonTitleFrame.minX - - transition.setBounds(view: self.titleOffsetContainer, bounds: CGRect(origin: CGPoint(x: totalOffset * fraction, y: 0.0), size: self.titleOffsetContainer.bounds.size)) - transition.setAlpha(view: self.titleOffsetContainer, alpha: pow(1.0 - fraction, 2.0)) - } + let totalOffset = -width * fraction + + transition.setBounds(view: self.titleOffsetContainer, bounds: CGRect(origin: CGPoint(x: totalOffset * fraction, y: 0.0), size: self.titleOffsetContainer.bounds.size)) + transition.setAlpha(view: self.titleOffsetContainer, alpha: pow(1.0 - fraction, 2.0)) } func updateNavigationTransitionAsNext(previousView: ContentView, storyPeerListView: StoryPeerListComponent.View?, fraction: CGFloat, transition: ComponentTransition, completion: @escaping () -> Void) { + let alphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.3) + transition.setBounds(view: self.titleOffsetContainer, bounds: CGRect(origin: CGPoint(x: -(1.0 - fraction) * self.bounds.width, y: 0.0), size: self.titleOffsetContainer.bounds.size), completion: { _ in completion() }) - transition.setAlpha(view: self.rightButtonOffsetContainer, alpha: pow(fraction, 2.0)) - transition.setBounds(view: self.rightButtonOffsetContainer, bounds: CGRect(origin: CGPoint(x: -(1.0 - fraction) * self.bounds.width, y: 0.0), size: self.rightButtonOffsetContainer.bounds.size)) - if let backButtonView = self.backButtonView { - transition.setScale(view: backButtonView.arrowView, scale: pow(max(0.001, fraction), 2.0)) - transition.setAlpha(view: backButtonView.arrowView, alpha: pow(fraction, 2.0)) - - if let storyPeerListView { - let previousTitleFrame = storyPeerListView.titleFrame() - let backButtonTitleFrame = backButtonView.convert(backButtonView.titleView.frame, to: self) - - let totalOffset = previousTitleFrame.minX - backButtonTitleFrame.minX - - transition.setBounds(view: backButtonView.titleOffsetContainer, bounds: CGRect(origin: CGPoint(x: -totalOffset * (1.0 - fraction), y: 0.0), size: backButtonView.titleOffsetContainer.bounds.size)) - transition.setAlpha(view: backButtonView.titleOffsetContainer, alpha: pow(fraction, 2.0)) - } else if let previousChatListTitleView = previousView.chatListTitleView { - let previousTitleFrame = previousChatListTitleView.titleNode.view.convert(previousChatListTitleView.titleNode.bounds, to: previousView.titleOffsetContainer) - let backButtonTitleFrame = backButtonView.convert(backButtonView.titleView.frame, to: self) - - let totalOffset = previousTitleFrame.minX - backButtonTitleFrame.minX - - transition.setBounds(view: backButtonView.titleOffsetContainer, bounds: CGRect(origin: CGPoint(x: -totalOffset * (1.0 - fraction), y: 0.0), size: backButtonView.titleOffsetContainer.bounds.size)) - transition.setAlpha(view: backButtonView.titleOffsetContainer, alpha: pow(fraction, 2.0)) - } - } + alphaTransition.setAlpha(view: self.rightButtonsContainer, alpha: pow(fraction, 2.0)) + + alphaTransition.setBlur(layer: self.leftButtonsContainer.layer, radius: (1.0 - fraction) * 10.0) + alphaTransition.setBlur(layer: self.rightButtonsContainer.layer, radius: (1.0 - fraction) * 10.0) } func updateNavigationTransitionAsPreviousInplace(nextView: ContentView, fraction: CGFloat, transition: ComponentTransition, completion: @escaping () -> Void) { - transition.setBounds(view: self.leftButtonOffsetContainer, bounds: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: self.leftButtonOffsetContainer.bounds.size), completion: { _ in - }) - transition.setAlpha(view: self.leftButtonOffsetContainer, alpha: pow(1.0 - fraction, 2.0)) - transition.setAlpha(view: self.rightButtonOffsetContainer, alpha: pow(1.0 - fraction, 2.0), completion: { _ in + let alphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.3) + + alphaTransition.setAlpha(view: self.leftButtonsContainer, alpha: pow(1.0 - fraction, 2.0)) + alphaTransition.setBlur(layer: self.leftButtonsContainer.layer, radius: fraction * 10.0) + alphaTransition.setAlpha(view: self.rightButtonsContainer, alpha: pow(1.0 - fraction, 2.0), completion: { _ in completion() }) - - if let backButtonView = self.backButtonView { - transition.setBounds(view: backButtonView, bounds: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: backButtonView.bounds.size), completion: { _ in - }) - } + alphaTransition.setBlur(layer: self.rightButtonsContainer.layer, radius: fraction * 10.0) transition.setBounds(view: self.titleOffsetContainer, bounds: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: self.titleOffsetContainer.bounds.size)) transition.setAlpha(view: self.titleOffsetContainer, alpha: pow(1.0 - fraction, 2.0)) } func updateNavigationTransitionAsNextInplace(previousView: ContentView, fraction: CGFloat, transition: ComponentTransition, completion: @escaping () -> Void) { + let alphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.3) + transition.setBounds(view: self.titleOffsetContainer, bounds: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: self.titleOffsetContainer.bounds.size), completion: { _ in completion() }) - transition.setAlpha(view: self.rightButtonOffsetContainer, alpha: pow(fraction, 2.0)) - transition.setBounds(view: self.rightButtonOffsetContainer, bounds: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: self.rightButtonOffsetContainer.bounds.size)) - if let backButtonView = self.backButtonView { - transition.setScale(view: backButtonView.arrowView, scale: pow(max(0.001, fraction), 2.0)) - transition.setAlpha(view: backButtonView.arrowView, alpha: pow(fraction, 2.0)) - - transition.setBounds(view: backButtonView.titleOffsetContainer, bounds: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: backButtonView.titleOffsetContainer.bounds.size)) - transition.setAlpha(view: backButtonView.titleOffsetContainer, alpha: pow(fraction, 2.0)) - } + alphaTransition.setBlur(layer: self.leftButtonsContainer.layer, radius: (1.0 - fraction) * 10.0) + alphaTransition.setAlpha(view: self.leftButtonsContainer, alpha: pow(fraction, 2.0)) + alphaTransition.setBlur(layer: self.rightButtonsContainer.layer, radius: (1.0 - fraction) * 10.0) + alphaTransition.setAlpha(view: self.rightButtonsContainer, alpha: pow(fraction, 2.0)) } - func update(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, content: Content, backTitle: String?, sideInset: CGFloat, sideContentWidth: CGFloat, sideContentFraction: CGFloat, size: CGSize, transition: ComponentTransition) { + func update(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, content: Content, displayBackButton: Bool, sideInset: CGFloat, sideContentWidth: CGFloat, sideContentFraction: CGFloat, size: CGSize, transition: ComponentTransition) { + let alphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.3) + transition.setPosition(view: self.titleOffsetContainer, position: CGPoint(x: size.width * 0.5, y: size.height * 0.5)) transition.setBounds(view: self.titleOffsetContainer, bounds: CGRect(origin: self.titleOffsetContainer.bounds.origin, size: size)) @@ -490,11 +456,10 @@ public final class ChatListHeaderComponent: Component { let titleTextUpdated = self.titleTextView.attributedText != titleText self.titleTextView.attributedText = titleText - let buttonSpacing: CGFloat = 8.0 + let buttonSpacing: CGFloat = 0.0 + var nextLeftButtonX: CGFloat = 0.0 - var leftOffset = sideInset - - if let backTitle = backTitle { + if displayBackButton { var backButtonTransition = transition let backButtonView: BackButtonView if let current = self.backButtonView { @@ -508,11 +473,14 @@ public final class ChatListHeaderComponent: Component { self.backPressed() }) self.backButtonView = backButtonView - self.addSubview(backButtonView) + self.leftButtonsContainer.addSubview(backButtonView) } - let backButtonSize = backButtonView.update(title: backTitle, theme: theme, availableSize: CGSize(width: 100.0, height: size.height), transition: backButtonTransition) - backButtonTransition.setFrame(view: backButtonView, frame: CGRect(origin: CGPoint(x: leftOffset, y: floor((size.height - backButtonSize.height) / 2.0)), size: backButtonSize)) - leftOffset += backButtonSize.width + buttonSpacing + let backButtonSize = backButtonView.update(theme: theme, strings: strings, availableSize: CGSize(width: 100.0, height: size.height), transition: backButtonTransition) + backButtonTransition.setFrame(view: backButtonView, frame: CGRect(origin: CGPoint(x: nextLeftButtonX, y: floor((size.height - backButtonSize.height) / 2.0)), size: backButtonSize)) + if nextLeftButtonX != 0.0 { + nextLeftButtonX += buttonSpacing + } + nextLeftButtonX += backButtonSize.width } else if let backButtonView = self.backButtonView { self.backButtonView = nil backButtonView.removeFromSuperview() @@ -521,6 +489,10 @@ public final class ChatListHeaderComponent: Component { var validLeftButtons = Set() if let leftButton = content.leftButton { validLeftButtons.insert(leftButton.id) + + if nextLeftButtonX != 0.0 { + nextLeftButtonX += buttonSpacing + } var buttonTransition = transition var animateButtonIn = false @@ -541,23 +513,25 @@ public final class ChatListHeaderComponent: Component { }, containerSize: CGSize(width: 100.0, height: size.height) ) - let buttonFrame = CGRect(origin: CGPoint(x: leftOffset, y: floor((size.height - buttonSize.height) / 2.0)), size: buttonSize) + let buttonFrame = CGRect(origin: CGPoint(x: nextLeftButtonX, y: floor((size.height - buttonSize.height) / 2.0)), size: buttonSize) if let buttonComponentView = buttonView.view { if buttonComponentView.superview == nil { - self.leftButtonOffsetContainer.addSubview(buttonComponentView) + self.leftButtonsContainer.addSubview(buttonComponentView) } buttonTransition.setFrame(view: buttonComponentView, frame: buttonFrame) if animateButtonIn { - transition.animateAlpha(view: buttonComponentView, from: 0.0, to: 1.0) + alphaTransition.animateBlur(layer: buttonComponentView.layer, fromRadius: 10.0, toRadius: 0.0) + alphaTransition.animateAlpha(view: buttonComponentView, from: 0.0, to: 1.0) } } - leftOffset = buttonFrame.maxX + buttonSpacing + nextLeftButtonX += buttonSize.width } var removeLeftButtons: [AnyHashable] = [] for (id, buttonView) in self.leftButtonViews { if !validLeftButtons.contains(id) { if let buttonComponentView = buttonView.view { - transition.setAlpha(view: buttonComponentView, alpha: 0.0, completion: { [weak buttonComponentView] _ in + alphaTransition.setBlur(layer: buttonComponentView.layer, radius: 10.0) + alphaTransition.setAlpha(view: buttonComponentView, alpha: 0.0, completion: { [weak buttonComponentView] _ in buttonComponentView?.removeFromSuperview() }) } @@ -568,10 +542,14 @@ public final class ChatListHeaderComponent: Component { self.leftButtonViews.removeValue(forKey: id) } - var rightOffset = size.width - sideInset + var nextRightButtonX: CGFloat = 0.0 var validRightButtons = Set() - for rightButton in content.rightButtons { + for rightButton in content.rightButtons.reversed() { validRightButtons.insert(rightButton.id) + + if nextRightButtonX != 0.0 { + nextRightButtonX += buttonSpacing + } var buttonTransition = transition var animateButtonIn = false @@ -592,23 +570,25 @@ public final class ChatListHeaderComponent: Component { }, containerSize: CGSize(width: 100.0, height: size.height) ) - let buttonFrame = CGRect(origin: CGPoint(x: rightOffset - buttonSize.width, y: floor((size.height - buttonSize.height) / 2.0)), size: buttonSize) + let buttonFrame = CGRect(origin: CGPoint(x: nextRightButtonX, y: floor((size.height - buttonSize.height) / 2.0)), size: buttonSize) if let buttonComponentView = buttonView.view { if buttonComponentView.superview == nil { - self.rightButtonOffsetContainer.addSubview(buttonComponentView) + self.rightButtonsContainer.addSubview(buttonComponentView) } buttonTransition.setFrame(view: buttonComponentView, frame: buttonFrame) if animateButtonIn { - transition.animateAlpha(view: buttonComponentView, from: 0.0, to: 1.0) + alphaTransition.animateBlur(layer: buttonComponentView.layer, fromRadius: 10.0, toRadius: 0.0) + alphaTransition.animateAlpha(view: buttonComponentView, from: 0.0, to: 1.0) } } - rightOffset = buttonFrame.minX - buttonSpacing + nextRightButtonX += buttonSize.width } var removeRightButtons: [AnyHashable] = [] for (id, buttonView) in self.rightButtonViews { if !validRightButtons.contains(id) { if let buttonComponentView = buttonView.view { - transition.setAlpha(view: buttonComponentView, alpha: 0.0, completion: { [weak buttonComponentView] _ in + alphaTransition.setBlur(layer: buttonComponentView.layer, radius: 10.0) + alphaTransition.setAlpha(view: buttonComponentView, alpha: 0.0, completion: { [weak buttonComponentView] _ in buttonComponentView?.removeFromSuperview() }) } @@ -618,8 +598,11 @@ public final class ChatListHeaderComponent: Component { for id in removeRightButtons { self.rightButtonViews.removeValue(forKey: id) } - - let commonInset: CGFloat = max(leftOffset, size.width - rightOffset) + + self.leftButtonsWidth = nextLeftButtonX + self.rightButtonsWidth = nextRightButtonX + + let commonInset: CGFloat = sideInset + max(nextLeftButtonX, nextRightButtonX) + 8.0 let remainingWidth = size.width - commonInset * 2.0 let titleTextSize = self.titleTextView.updateLayout(CGSize(width: remainingWidth, height: size.height)) @@ -662,10 +645,10 @@ public final class ChatListHeaderComponent: Component { } var centerContentLeftInset: CGFloat = 0.0 - centerContentLeftInset = leftOffset - 4.0 + centerContentLeftInset = nextLeftButtonX + 4.0 var centerContentRightInset: CGFloat = 0.0 - centerContentRightInset = size.width - rightOffset - 8.0 + centerContentRightInset = nextRightButtonX + 20.0 var centerContentWidth: CGFloat = 0.0 var centerContentOffsetX: CGFloat = 0.0 @@ -687,13 +670,11 @@ public final class ChatListHeaderComponent: Component { chatListTitleView.theme = theme chatListTitleView.strings = strings chatListTitleView.setTitle(chatListTitle, animated: false) - let titleContentRect = chatListTitleView.updateLayout(size: chatListTitleContentSize, clearBounds: CGRect(origin: CGPoint(), size: chatListTitleContentSize), transition: transition.containedViewLayoutTransition) + let titleContentRect = chatListTitleView.updateLayoutInternal(size: chatListTitleContentSize, transition: transition.containedViewLayoutTransition) centerContentWidth = floor((chatListTitleContentSize.width * 0.5 - titleContentRect.minX) * 2.0) - //sideWidth + centerWidth + centerOffset = size.width - //let centerOffset = -(size.width - (sideContentWidth + centerContentWidth)) * 0.5 + size.width * 0.5 let centerOffset = sideContentWidth * 0.5 - centerContentOffsetX = -max(0.0, centerOffset + titleContentRect.maxX - 2.0 - rightOffset) + centerContentOffsetX = -max(0.0, centerOffset + titleContentRect.maxX - 2.0 - (size.width - sideInset - nextRightButtonX)) chatListTitleView.openStatusSetup = { [weak self] sourceView in guard let self else { @@ -734,13 +715,18 @@ public final class ChatListHeaderComponent: Component { } } - public final class View: UIView, NavigationBarHeaderView { + public final class View: UIView { private var component: ChatListHeaderComponent? private weak var state: EmptyComponentState? private var primaryContentView: ContentView? private var secondaryContentView: ContentView? private var storyOffsetFraction: CGFloat = 0.0 + + private let leftButtonsContainer: UIView + private let rightButtonsContainer: UIView + private var leftButtonsBackgroundContainer: GlassBackgroundView? + private var rightButtonsBackgroundContainer: GlassBackgroundView? private let storyPeerListExternalState = StoryPeerListComponent.ExternalState() private var storyPeerList: ComponentView? @@ -753,6 +739,10 @@ public final class ChatListHeaderComponent: Component { } override init(frame: CGRect) { + self.leftButtonsContainer = UIView() + self.rightButtonsContainer = UIView() + self.rightButtonsContainer.layer.anchorPoint = CGPoint(x: 1.0, y: 0.0) + super.init(frame: frame) self.storyOffsetFraction = 1.0 @@ -766,10 +756,6 @@ public final class ChatListHeaderComponent: Component { return self.effectiveContentView?.backButtonView?.arrowView } - public var backButtonTitleView: UIView? { - return self.effectiveContentView?.backButtonView?.titleView - } - public var rightButtonView: UIView? { return self.effectiveContentView?.rightButtonViews.first?.value.view } @@ -784,29 +770,6 @@ public final class ChatListHeaderComponent: Component { return self.effectiveContentView?.titleContentView?.view } - public func makeTransitionBackArrowView(accentColor: UIColor) -> UIView? { - if let backArrowView = self.backArrowView { - let view = UIImageView() - view.image = NavigationBar.backArrowImage(color: accentColor) - view.frame = backArrowView.convert(backArrowView.bounds, to: self) - return view - } else { - return nil - } - } - - public func makeTransitionBackButtonView(accentColor: UIColor) -> UIView? { - if let backButtonTitleView = self.backButtonTitleView as? ImmediateTextView { - let view = ImmediateTextView() - view.attributedText = NSAttributedString(string: backButtonTitleView.attributedText?.string ?? "", font: Font.regular(17.0), textColor: accentColor) - let _ = view.updateLayout(CGSize(width: 100.0, height: 100.0)) - view.frame = backButtonTitleView.convert(backButtonTitleView.bounds, to: self) - return view - } else { - return nil - } - } - public func storyPeerListView() -> StoryPeerListComponent.View? { return self.storyPeerList?.view as? StoryPeerListComponent.View } @@ -848,8 +811,8 @@ public final class ChatListHeaderComponent: Component { let previousComponent = self.component self.component = component + var primaryContentTransition = transition if var primaryContent = component.primaryContent { - var primaryContentTransition = transition let primaryContentView: ContentView if let current = self.primaryContentView { primaryContentView = current @@ -877,6 +840,8 @@ public final class ChatListHeaderComponent: Component { ) self.primaryContentView = primaryContentView self.addSubview(primaryContentView) + self.leftButtonsContainer.addSubview(primaryContentView.leftButtonsContainer) + self.rightButtonsContainer.addSubview(primaryContentView.rightButtonsContainer) } let sideContentWidth: CGFloat = 0.0 @@ -889,18 +854,19 @@ public final class ChatListHeaderComponent: Component { chatListTitle: nil, leftButton: primaryContent.leftButton, rightButtons: primaryContent.rightButtons, - backTitle: primaryContent.backTitle, backPressed: primaryContent.backPressed ) } - primaryContentView.update(context: component.context, theme: component.theme, strings: component.strings, content: primaryContent, backTitle: primaryContent.backTitle, sideInset: component.sideInset, sideContentWidth: sideContentWidth, sideContentFraction: (1.0 - component.storiesFraction), size: availableSize, transition: primaryContentTransition) + primaryContentView.update(context: component.context, theme: component.theme, strings: component.strings, content: primaryContent, displayBackButton: primaryContent.backPressed != nil, sideInset: component.sideInset, sideContentWidth: sideContentWidth, sideContentFraction: (1.0 - component.storiesFraction), size: availableSize, transition: primaryContentTransition) primaryContentTransition.setFrame(view: primaryContentView, frame: CGRect(origin: CGPoint(), size: availableSize)) primaryContentView.updateContentOffsetFraction(contentOffsetFraction: 1.0 - self.storyOffsetFraction, transition: primaryContentTransition) } else if let primaryContentView = self.primaryContentView { self.primaryContentView = nil primaryContentView.removeFromSuperview() + primaryContentView.leftButtonsContainer.removeFromSuperview() + primaryContentView.rightButtonsContainer.removeFromSuperview() } var storyListTransition = transition @@ -991,13 +957,16 @@ public final class ChatListHeaderComponent: Component { ) } + var secondaryContentTransition = transition + var secondaryContentIsAnimatingIn = false + var removedSecondaryContentView: ContentView? if let secondaryContent = component.secondaryContent { - var secondaryContentTransition = transition let secondaryContentView: ContentView if let current = self.secondaryContentView { secondaryContentView = current } else { secondaryContentTransition = .immediate + secondaryContentIsAnimatingIn = true secondaryContentView = ContentView( backPressed: { [weak self] in guard let self, let component = self.component else { @@ -1019,9 +988,15 @@ public final class ChatListHeaderComponent: Component { } ) self.secondaryContentView = secondaryContentView - self.addSubview(secondaryContentView) + if let primaryContentView = self.primaryContentView { + self.insertSubview(secondaryContentView, aboveSubview: primaryContentView) + } else { + self.addSubview(secondaryContentView) + } + self.leftButtonsContainer.addSubview(secondaryContentView.leftButtonsContainer) + self.rightButtonsContainer.addSubview(secondaryContentView.rightButtonsContainer) } - secondaryContentView.update(context: component.context, theme: component.theme, strings: component.strings, content: secondaryContent, backTitle: component.primaryContent?.navigationBackTitle ?? component.primaryContent?.title, sideInset: component.sideInset, sideContentWidth: 0.0, sideContentFraction: 0.0, size: availableSize, transition: secondaryContentTransition) + secondaryContentView.update(context: component.context, theme: component.theme, strings: component.strings, content: secondaryContent, displayBackButton: true, sideInset: component.sideInset, sideContentWidth: 0.0, sideContentFraction: 0.0, size: availableSize, transition: secondaryContentTransition) secondaryContentTransition.setFrame(view: secondaryContentView, frame: CGRect(origin: CGPoint(), size: availableSize)) secondaryContentView.updateContentOffsetFraction(contentOffsetFraction: 1.0 - self.storyOffsetFraction, transition: secondaryContentTransition) @@ -1032,7 +1007,7 @@ public final class ChatListHeaderComponent: Component { primaryContentView.updateNavigationTransitionAsPreviousInplace(nextView: secondaryContentView, fraction: 0.0, transition: .immediate, completion: {}) secondaryContentView.updateNavigationTransitionAsNextInplace(previousView: primaryContentView, fraction: 0.0, transition: .immediate, completion: {}) } else { - primaryContentView.updateNavigationTransitionAsPrevious(nextView: secondaryContentView, fraction: 0.0, transition: .immediate, completion: {}) + primaryContentView.updateNavigationTransitionAsPrevious(nextView: secondaryContentView, width: availableSize.width, fraction: 0.0, transition: .immediate, completion: {}) secondaryContentView.updateNavigationTransitionAsNext(previousView: primaryContentView, storyPeerListView: self.storyPeerListView(), fraction: 0.0, transition: .immediate, completion: {}) } } @@ -1041,26 +1016,33 @@ public final class ChatListHeaderComponent: Component { primaryContentView.updateNavigationTransitionAsPreviousInplace(nextView: secondaryContentView, fraction: component.secondaryTransition, transition: transition, completion: {}) secondaryContentView.updateNavigationTransitionAsNextInplace(previousView: primaryContentView, fraction: component.secondaryTransition, transition: transition, completion: {}) } else { - primaryContentView.updateNavigationTransitionAsPrevious(nextView: secondaryContentView, fraction: component.secondaryTransition, transition: transition, completion: {}) + primaryContentView.updateNavigationTransitionAsPrevious(nextView: secondaryContentView, width: availableSize.width, fraction: component.secondaryTransition, transition: transition, completion: {}) secondaryContentView.updateNavigationTransitionAsNext(previousView: primaryContentView, storyPeerListView: self.storyPeerListView(), fraction: component.secondaryTransition, transition: transition, completion: {}) } } } else if let secondaryContentView = self.secondaryContentView { self.secondaryContentView = nil + removedSecondaryContentView = secondaryContentView if let primaryContentView = self.primaryContentView { if self.storyOffsetFraction < 0.8 { primaryContentView.updateNavigationTransitionAsPreviousInplace(nextView: secondaryContentView, fraction: 0.0, transition: transition, completion: {}) secondaryContentView.updateNavigationTransitionAsNextInplace(previousView: primaryContentView, fraction: 0.0, transition: transition, completion: { [weak secondaryContentView] in + secondaryContentView?.leftButtonsContainer.removeFromSuperview() + secondaryContentView?.rightButtonsContainer.removeFromSuperview() secondaryContentView?.removeFromSuperview() }) } else { - primaryContentView.updateNavigationTransitionAsPrevious(nextView: secondaryContentView, fraction: 0.0, transition: transition, completion: {}) + primaryContentView.updateNavigationTransitionAsPrevious(nextView: secondaryContentView, width: availableSize.width, fraction: 0.0, transition: transition, completion: {}) secondaryContentView.updateNavigationTransitionAsNext(previousView: primaryContentView, storyPeerListView: self.storyPeerListView(), fraction: 0.0, transition: transition, completion: { [weak secondaryContentView] in + secondaryContentView?.leftButtonsContainer.removeFromSuperview() + secondaryContentView?.rightButtonsContainer.removeFromSuperview() secondaryContentView?.removeFromSuperview() }) } } else { + secondaryContentView.leftButtonsContainer.removeFromSuperview() + secondaryContentView.rightButtonsContainer.removeFromSuperview() secondaryContentView.removeFromSuperview() } } @@ -1070,18 +1052,10 @@ public final class ChatListHeaderComponent: Component { self.addSubview(storyPeerListComponentView) } - //let storyPeerListMinOffset: CGFloat = -7.0 let storyPeerListMaxOffset: CGFloat = availableSize.height + 2.0 - //let storyPeerListPosition: CGFloat = storyPeerListMinOffset * (1.0 - component.storiesFraction) + storyPeerListMaxOffset * component.storiesFraction - var storiesX: CGFloat = 0.0 - if let nextBackButtonView = self.secondaryContentView?.backButtonView { - let backButtonTitleFrame = nextBackButtonView.convert(nextBackButtonView.titleView.frame, to: self) - let storyListTitleFrame = storyPeerListComponentView.titleFrame() - - storiesX += (backButtonTitleFrame.minX - storyListTitleFrame.minX) * component.secondaryTransition - } + storiesX -= availableSize.width * component.secondaryTransition storyListTransition.setFrame(view: storyPeerListComponentView, frame: CGRect(origin: CGPoint(x: storiesX, y: storyPeerListMaxOffset), size: CGSize(width: availableSize.width, height: 79.0))) @@ -1090,6 +1064,90 @@ public final class ChatListHeaderComponent: Component { let storyListAlpha: CGFloat = (1.0 - component.secondaryTransition) * storyListNormalAlpha storyListTransition.setAlpha(view: storyPeerListComponentView, alpha: storyListAlpha) } + + var leftButtonsEffectiveWidth: CGFloat = 0.0 + var rightButtonsEffectiveWidth: CGFloat = 0.0 + if let primaryContentView = self.primaryContentView, let secondaryContentView = self.secondaryContentView { + + leftButtonsEffectiveWidth = primaryContentView.leftButtonsWidth * (1.0 - component.secondaryTransition) + secondaryContentView.leftButtonsWidth * component.secondaryTransition + rightButtonsEffectiveWidth = primaryContentView.rightButtonsWidth * (1.0 - component.secondaryTransition) + secondaryContentView.rightButtonsWidth * component.secondaryTransition + + primaryContentTransition.setFrame(view: primaryContentView.leftButtonsContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: max(44.0, primaryContentView.leftButtonsWidth), height: 44.0))) + secondaryContentTransition.setFrame(view: secondaryContentView.leftButtonsContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: max(44.0, secondaryContentView.leftButtonsWidth), height: 44.0))) + + primaryContentTransition.setFrame(view: primaryContentView.rightButtonsContainer, frame: CGRect(origin: CGPoint(x: rightButtonsEffectiveWidth - primaryContentView.rightButtonsWidth, y: 0.0), size: CGSize(width: max(44.0, primaryContentView.rightButtonsWidth), height: 44.0))) + + if secondaryContentIsAnimatingIn { + secondaryContentView.rightButtonsContainer.frame = CGRect(origin: CGPoint(x: self.rightButtonsContainer.bounds.width - secondaryContentView.rightButtonsWidth, y: 0.0), size: CGSize(width: max(44.0, secondaryContentView.rightButtonsWidth), height: 44.0)) + } + transition.setFrame(view: secondaryContentView.rightButtonsContainer, frame: CGRect(origin: CGPoint(x: rightButtonsEffectiveWidth - secondaryContentView.rightButtonsWidth, y: 0.0), size: CGSize(width: max(44.0, secondaryContentView.rightButtonsWidth), height: 44.0))) + } else if let primaryContentView = self.primaryContentView { + leftButtonsEffectiveWidth = primaryContentView.leftButtonsWidth + rightButtonsEffectiveWidth = primaryContentView.rightButtonsWidth + + primaryContentTransition.setFrame(view: primaryContentView.leftButtonsContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: max(44.0, primaryContentView.leftButtonsWidth), height: 44.0))) + primaryContentTransition.setFrame(view: primaryContentView.rightButtonsContainer, frame: CGRect(origin: CGPoint(x: rightButtonsEffectiveWidth - primaryContentView.rightButtonsWidth, y: 0.0), size: CGSize(width: max(44.0, primaryContentView.rightButtonsWidth), height: 44.0))) + + if let removedSecondaryContentView { + transition.setFrame(view: removedSecondaryContentView.rightButtonsContainer, frame: CGRect(origin: CGPoint(x: rightButtonsEffectiveWidth - removedSecondaryContentView.rightButtonsWidth, y: 0.0), size: CGSize(width: max(44.0, removedSecondaryContentView.rightButtonsWidth), height: 44.0))) + } + } + + if leftButtonsEffectiveWidth != 0.0 { + let leftButtonsBackgroundContainer: GlassBackgroundView + var leftButtonsBackgroundContainerTransition = transition + if let current = self.leftButtonsBackgroundContainer { + leftButtonsBackgroundContainer = current + } else { + leftButtonsBackgroundContainerTransition = leftButtonsBackgroundContainerTransition.withAnimation(.none) + leftButtonsBackgroundContainer = GlassBackgroundView() + self.leftButtonsBackgroundContainer = leftButtonsBackgroundContainer + self.addSubview(leftButtonsBackgroundContainer) + leftButtonsBackgroundContainer.contentView.addSubview(self.leftButtonsContainer) + } + let leftButtonsContainerFrame = CGRect(origin: CGPoint(x: component.sideInset, y: 0.0), size: CGSize(width: max(44.0, leftButtonsEffectiveWidth), height: 44.0)) + leftButtonsBackgroundContainerTransition.setFrame(view: leftButtonsBackgroundContainer, frame: leftButtonsContainerFrame) + leftButtonsBackgroundContainer.update(size: leftButtonsContainerFrame.size, cornerRadius: leftButtonsContainerFrame.height * 0.5, isDark: component.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: component.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: leftButtonsBackgroundContainerTransition) + leftButtonsBackgroundContainerTransition.setFrame(view: self.leftButtonsContainer, frame: CGRect(origin: CGPoint(), size: leftButtonsContainerFrame.size)) + } else { + if let leftButtonsBackgroundContainer = self.leftButtonsBackgroundContainer { + self.leftButtonsBackgroundContainer = nil + transition.setAlpha(view: leftButtonsBackgroundContainer, alpha: 0.0, completion: { [weak leftButtonsBackgroundContainer] _ in + leftButtonsBackgroundContainer?.removeFromSuperview() + }) + } + } + + if rightButtonsEffectiveWidth != 0.0 { + let rightButtonsBackgroundContainer: GlassBackgroundView + var rightButtonsBackgroundContainerTransition = transition + + let rightButtonsContainerFrame = CGRect(origin: CGPoint(x: availableSize.width - component.sideInset - max(44.0, rightButtonsEffectiveWidth), y: 0.0), size: CGSize(width: max(44.0, rightButtonsEffectiveWidth), height: 44.0)) + + if let current = self.rightButtonsBackgroundContainer { + rightButtonsBackgroundContainer = current + } else { + rightButtonsBackgroundContainerTransition = rightButtonsBackgroundContainerTransition.withAnimation(.none) + rightButtonsBackgroundContainer = GlassBackgroundView() + self.rightButtonsBackgroundContainer = rightButtonsBackgroundContainer + self.addSubview(rightButtonsBackgroundContainer) + rightButtonsBackgroundContainer.contentView.addSubview(self.rightButtonsContainer) + + rightButtonsBackgroundContainer.update(size: rightButtonsContainerFrame.size, cornerRadius: rightButtonsContainerFrame.height * 0.5, isDark: component.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: component.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, isVisible: false, transition: .immediate) + } + rightButtonsBackgroundContainerTransition.setFrame(view: rightButtonsBackgroundContainer, frame: rightButtonsContainerFrame) + rightButtonsBackgroundContainer.update(size: rightButtonsContainerFrame.size, cornerRadius: rightButtonsContainerFrame.height * 0.5, isDark: component.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: component.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition) + rightButtonsBackgroundContainerTransition.setFrame(view: self.rightButtonsContainer, frame: CGRect(origin: CGPoint(), size: rightButtonsContainerFrame.size)) + } else { + if let rightButtonsBackgroundContainer = self.rightButtonsBackgroundContainer { + self.rightButtonsBackgroundContainer = nil + + rightButtonsBackgroundContainer.update(size: rightButtonsBackgroundContainer.bounds.size, cornerRadius: rightButtonsBackgroundContainer.bounds.height * 0.5, isDark: component.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: component.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, isVisible: false, transition: transition) + transition.attachAnimation(view: rightButtonsBackgroundContainer, id: "remove", completion: { [weak rightButtonsBackgroundContainer] _ in + rightButtonsBackgroundContainer?.removeFromSuperview() + }) + } + } return availableSize } @@ -1119,251 +1177,3 @@ public final class ChatListHeaderComponent: Component { return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) } } - -public final class NavigationButtonComponentEnvironment: Equatable { - public let theme: PresentationTheme - - public init(theme: PresentationTheme) { - self.theme = theme - } - - public static func ==(lhs: NavigationButtonComponentEnvironment, rhs: NavigationButtonComponentEnvironment) -> Bool { - if lhs.theme != rhs.theme { - return false - } - return true - } -} - -public final class NavigationButtonComponent: Component { - public typealias EnvironmentType = NavigationButtonComponentEnvironment - - public enum Content: Equatable { - case text(title: String, isBold: Bool) - case more - case icon(imageName: String) - case proxy(status: ChatTitleProxyStatus) - } - - public let content: Content - public let pressed: (UIView) -> Void - public let contextAction: ((UIView, ContextGesture?) -> Void)? - - public init( - content: Content, - pressed: @escaping (UIView) -> Void, - contextAction: ((UIView, ContextGesture?) -> Void)? = nil - ) { - self.content = content - self.pressed = pressed - self.contextAction = contextAction - } - - public static func ==(lhs: NavigationButtonComponent, rhs: NavigationButtonComponent) -> Bool { - if lhs.content != rhs.content { - return false - } - return true - } - - public final class View: HighlightTrackingButton { - private var textView: ImmediateTextView? - - private var iconView: UIImageView? - private var iconImageName: String? - - private var proxyNode: ChatTitleProxyNode? - - private var moreButton: MoreHeaderButton? - - private var component: NavigationButtonComponent? - private var theme: PresentationTheme? - - override init(frame: CGRect) { - super.init(frame: frame) - - self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside) - - self.highligthedChanged = { [weak self] highlighted in - guard let self else { - return - } - if highlighted { - self.textView?.alpha = 0.6 - self.proxyNode?.alpha = 0.6 - self.iconView?.alpha = 0.6 - } else { - self.textView?.alpha = 1.0 - self.textView?.layer.animateAlpha(from: 0.6, to: 1.0, duration: 0.2) - - self.proxyNode?.alpha = 1.0 - self.proxyNode?.layer.animateAlpha(from: 0.6, to: 1.0, duration: 0.2) - - self.iconView?.alpha = 1.0 - self.iconView?.layer.animateAlpha(from: 0.6, to: 1.0, duration: 0.2) - } - } - } - - required public init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - @objc private func pressed() { - self.component?.pressed(self) - } - - func update(component: NavigationButtonComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { - self.component = component - - let theme = environment[NavigationButtonComponentEnvironment.self].value.theme - var themeUpdated = false - if self.theme !== theme { - self.theme = theme - themeUpdated = true - } - - let iconOffset: CGFloat = 4.0 - - var textString: NSAttributedString? - var imageName: String? - var proxyStatus: ChatTitleProxyStatus? - var isMore: Bool = false - - switch component.content { - case let .text(title, isBold): - textString = NSAttributedString(string: title, font: isBold ? Font.bold(17.0) : Font.regular(17.0), textColor: theme.rootController.navigationBar.accentTextColor) - case .more: - isMore = true - case let .icon(imageNameValue): - imageName = imageNameValue - case let .proxy(status): - proxyStatus = status - } - - var size = CGSize(width: 0.0, height: availableSize.height) - - if let textString = textString { - let textView: ImmediateTextView - if let current = self.textView { - textView = current - } else { - textView = ImmediateTextView() - textView.isUserInteractionEnabled = false - self.textView = textView - self.addSubview(textView) - } - - textView.attributedText = textString - let textSize = textView.updateLayout(availableSize) - size.width = textSize.width - - textView.frame = CGRect(origin: CGPoint(x: 0.0, y: floor((availableSize.height - textSize.height) / 2.0)), size: textSize) - } else if let textView = self.textView { - self.textView = nil - textView.removeFromSuperview() - } - - if let imageName = imageName { - let iconView: UIImageView - if let current = self.iconView { - iconView = current - } else { - iconView = UIImageView() - iconView.isUserInteractionEnabled = false - self.iconView = iconView - self.addSubview(iconView) - } - if self.iconImageName != imageName || themeUpdated { - self.iconImageName = imageName - iconView.image = generateTintedImage(image: UIImage(bundleImageName: imageName), color: theme.rootController.navigationBar.accentTextColor) - } - - if let iconSize = iconView.image?.size { - size.width = iconSize.width - - iconView.frame = CGRect(origin: CGPoint(x: iconOffset, y: floor((availableSize.height - iconSize.height) / 2.0)), size: iconSize) - } - } else if let iconView = self.iconView { - self.iconView = nil - iconView.removeFromSuperview() - self.iconImageName = nil - } - - if let proxyStatus = proxyStatus { - let proxyNode: ChatTitleProxyNode - if let current = self.proxyNode { - proxyNode = current - } else { - proxyNode = ChatTitleProxyNode(theme: theme) - proxyNode.isUserInteractionEnabled = false - self.proxyNode = proxyNode - self.addSubnode(proxyNode) - } - - let proxySize = CGSize(width: 30.0, height: 30.0) - size.width = proxySize.width - - proxyNode.theme = theme - proxyNode.status = proxyStatus - - proxyNode.frame = CGRect(origin: CGPoint(x: iconOffset, y: floor((availableSize.height - proxySize.height) / 2.0)), size: proxySize) - } else if let proxyNode = self.proxyNode { - self.proxyNode = nil - proxyNode.removeFromSupernode() - } - - if isMore { - let moreButton: MoreHeaderButton - if let current = self.moreButton, !themeUpdated { - moreButton = current - } else { - if let moreButton = self.moreButton { - moreButton.removeFromSupernode() - self.moreButton = nil - } - - moreButton = MoreHeaderButton(color: theme.rootController.navigationBar.buttonColor) - moreButton.isUserInteractionEnabled = true - moreButton.setContent(.more(MoreHeaderButton.optionsCircleImage(color: theme.rootController.navigationBar.buttonColor))) - moreButton.onPressed = { [weak self] in - guard let self, let component = self.component else { - return - } - self.moreButton?.play() - component.pressed(self) - } - moreButton.contextAction = { [weak self] sourceNode, gesture in - guard let self, let component = self.component else { - return - } - self.moreButton?.play() - component.contextAction?(self, gesture) - } - self.moreButton = moreButton - self.addSubnode(moreButton) - } - - let buttonSize = CGSize(width: 26.0, height: 44.0) - size.width = buttonSize.width - - moreButton.setContent(.more(MoreHeaderButton.optionsCircleImage(color: theme.rootController.navigationBar.buttonColor))) - - moreButton.frame = CGRect(origin: CGPoint(x: iconOffset, y: floor((availableSize.height - buttonSize.height) / 2.0)), size: buttonSize) - } else if let moreButton = self.moreButton { - self.moreButton = nil - moreButton.removeFromSupernode() - } - - return size - } - } - - public func makeView() -> View { - return View(frame: CGRect()) - } - - public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { - return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) - } -} diff --git a/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListNavigationBar.swift b/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListNavigationBar.swift index 549c6fb4..4a9be3f3 100644 --- a/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListNavigationBar.swift +++ b/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/ChatListNavigationBar.swift @@ -9,6 +9,16 @@ import SearchUI import AccountContext import TelegramCore import StoryPeerListComponent +import EdgeEffect +import GlassBackgroundComponent + +private func searchScrollHeightValue() -> CGFloat { + return 54.0 +} + +private func storiesHeightValue() -> CGFloat { + return 96.0 +} public final class ChatListNavigationBar: Component { public final class AnimationHint { @@ -20,20 +30,37 @@ public final class ChatListNavigationBar: Component { self.crossfadeStoryPeers = crossfadeStoryPeers } } + + public struct Search: Equatable { + public var isEnabled: Bool + + public init(isEnabled: Bool) { + self.isEnabled = isEnabled + } + } + + public struct ActiveSearch: Equatable { + public var isExternal: Bool + + public init(isExternal: Bool) { + self.isExternal = isExternal + } + } public let context: AccountContext public let theme: PresentationTheme public let strings: PresentationStrings public let statusBarHeight: CGFloat public let sideInset: CGFloat - public let isSearchActive: Bool - public let isSearchEnabled: Bool + public let search: Search? + public let activeSearch: ActiveSearch? public let primaryContent: ChatListHeaderComponent.Content? public let secondaryContent: ChatListHeaderComponent.Content? public let secondaryTransition: CGFloat public let storySubscriptions: EngineStorySubscriptions? public let storiesIncludeHidden: Bool public let uploadProgress: [EnginePeer.Id: Float] + public let headerPanels: AnyComponent? public let tabsNode: ASDisplayNode? public let tabsNodeIsSearch: Bool public let accessoryPanelContainer: ASDisplayNode? @@ -48,14 +75,15 @@ public final class ChatListNavigationBar: Component { strings: PresentationStrings, statusBarHeight: CGFloat, sideInset: CGFloat, - isSearchActive: Bool, - isSearchEnabled: Bool, + search: Search?, + activeSearch: ActiveSearch?, primaryContent: ChatListHeaderComponent.Content?, secondaryContent: ChatListHeaderComponent.Content?, secondaryTransition: CGFloat, storySubscriptions: EngineStorySubscriptions?, storiesIncludeHidden: Bool, uploadProgress: [EnginePeer.Id: Float], + headerPanels: AnyComponent?, tabsNode: ASDisplayNode?, tabsNodeIsSearch: Bool, accessoryPanelContainer: ASDisplayNode?, @@ -69,14 +97,15 @@ public final class ChatListNavigationBar: Component { self.strings = strings self.statusBarHeight = statusBarHeight self.sideInset = sideInset - self.isSearchActive = isSearchActive - self.isSearchEnabled = isSearchEnabled + self.search = search + self.activeSearch = activeSearch self.primaryContent = primaryContent self.secondaryContent = secondaryContent self.secondaryTransition = secondaryTransition self.storySubscriptions = storySubscriptions self.storiesIncludeHidden = storiesIncludeHidden self.uploadProgress = uploadProgress + self.headerPanels = headerPanels self.tabsNode = tabsNode self.tabsNodeIsSearch = tabsNodeIsSearch self.accessoryPanelContainer = accessoryPanelContainer @@ -102,10 +131,10 @@ public final class ChatListNavigationBar: Component { if lhs.sideInset != rhs.sideInset { return false } - if lhs.isSearchActive != rhs.isSearchActive { + if lhs.search != rhs.search { return false } - if lhs.isSearchEnabled != rhs.isSearchEnabled { + if lhs.activeSearch != rhs.activeSearch { return false } if lhs.primaryContent != rhs.primaryContent { @@ -126,6 +155,9 @@ public final class ChatListNavigationBar: Component { if lhs.uploadProgress != rhs.uploadProgress { return false } + if lhs.headerPanels != rhs.headerPanels { + return false + } if lhs.tabsNode !== rhs.tabsNode { return false } @@ -149,15 +181,13 @@ public final class ChatListNavigationBar: Component { } } - public static let searchScrollHeight: CGFloat = 52.0 - public static let storiesScrollHeight: CGFloat = { - return 83.0 - }() + public static let searchScrollHeight: CGFloat = searchScrollHeightValue() + public static let storiesScrollHeight: CGFloat = storiesHeightValue() public final class View: UIView { - private let backgroundView: BlurredBackgroundView - private let separatorLayer: SimpleLayer + private let edgeEffectView: EdgeEffectView + private let headerBackgroundContainer: GlassBackgroundContainerView public let headerContent = ComponentView() public private(set) var searchContentNode: NavigationBarSearchContentNode? @@ -178,23 +208,36 @@ public final class ChatListNavigationBar: Component { public private(set) var storiesUnlocked: Bool = false + private let bottomContentsContainer: UIView + private var tabsNode: ASDisplayNode? private var tabsNodeIsSearch: Bool = false private weak var disappearingTabsView: UIView? private var disappearingTabsViewSearch: Bool = false + private var headerPanelsView: ComponentView? + private var disappearingHeaderPanels: ComponentView? + public var headerPanels: UIView? { + return self.headerPanelsView?.view + } + private var currentHeaderComponent: ChatListHeaderComponent? + + private var currentHeight: CGFloat = 0.0 override public init(frame: CGRect) { - self.backgroundView = BlurredBackgroundView(color: .clear, enableBlur: true) - self.backgroundView.layer.anchorPoint = CGPoint(x: 0.0, y: 1.0) - self.separatorLayer = SimpleLayer() - self.separatorLayer.anchorPoint = CGPoint() + self.edgeEffectView = EdgeEffectView() + + self.headerBackgroundContainer = GlassBackgroundContainerView() + self.headerBackgroundContainer.layer.anchorPoint = CGPoint() + + self.bottomContentsContainer = UIView() + self.bottomContentsContainer.layer.anchorPoint = CGPoint() super.init(frame: frame) - self.addSubview(self.backgroundView) - self.layer.addSublayer(self.separatorLayer) + self.addSubview(self.edgeEffectView) + self.addSubview(self.bottomContentsContainer) } required init?(coder: NSCoder) { @@ -202,7 +245,7 @@ public final class ChatListNavigationBar: Component { } override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - if !self.backgroundView.frame.contains(point) { + if point.y >= self.currentHeight { return nil } @@ -248,7 +291,12 @@ public final class ChatListNavigationBar: Component { let searchOffsetDistance: CGFloat = ChatListNavigationBar.searchScrollHeight - let minContentOffset: CGFloat = ChatListNavigationBar.searchScrollHeight + let minContentOffset: CGFloat + if component.search != nil { + minContentOffset = ChatListNavigationBar.searchScrollHeight + } else { + minContentOffset = 0.0 + } let clippedScrollOffset = min(minContentOffset, offset) if self.clippedScrollOffset == clippedScrollOffset && !self.hasDeferredScrollOffset && !forceUpdate && !allowAvatarsExpansionUpdated { @@ -259,71 +307,105 @@ public final class ChatListNavigationBar: Component { let visibleSize = CGSize(width: currentLayout.size.width, height: max(0.0, currentLayout.size.height - clippedScrollOffset)) - let previousHeight = self.separatorLayer.position.y + let previousHeight = self.currentHeight + + self.currentHeight = visibleSize.height - self.backgroundView.update(size: CGSize(width: visibleSize.width, height: 1000.0), transition: transition.containedViewLayoutTransition) - - transition.setBounds(view: self.backgroundView, bounds: CGRect(origin: CGPoint(), size: CGSize(width: visibleSize.width, height: 1000.0))) - transition.animatePosition(view: self.backgroundView, from: CGPoint(x: 0.0, y: -visibleSize.height + self.backgroundView.layer.position.y), to: CGPoint(), additive: true) - self.backgroundView.layer.position = CGPoint(x: 0.0, y: visibleSize.height) - - transition.setFrameWithAdditivePosition(layer: self.separatorLayer, frame: CGRect(origin: CGPoint(x: 0.0, y: visibleSize.height), size: CGSize(width: visibleSize.width, height: UIScreenPixel))) - - let searchContentNode: NavigationBarSearchContentNode - if let current = self.searchContentNode { - searchContentNode = current - - if themeUpdated { + var embeddedSearchBarExpansionHeight: CGFloat = 0.0 + var searchFrameValue: CGRect? + if let search = component.search { + let searchContentNode: NavigationBarSearchContentNode + if let current = self.searchContentNode { + searchContentNode = current + + if themeUpdated { + let placeholder: String + let compactPlaceholder: String + + placeholder = component.strings.Common_Search + compactPlaceholder = component.strings.Common_Search + + searchContentNode.updateThemeAndPlaceholder(theme: component.theme, placeholder: placeholder, compactPlaceholder: compactPlaceholder) + } + } else { let placeholder: String let compactPlaceholder: String placeholder = component.strings.Common_Search compactPlaceholder = component.strings.Common_Search - searchContentNode.updateThemeAndPlaceholder(theme: component.theme, placeholder: placeholder, compactPlaceholder: compactPlaceholder) - } - } else { - let placeholder: String - let compactPlaceholder: String - - placeholder = component.strings.Common_Search - compactPlaceholder = component.strings.Common_Search - - searchContentNode = NavigationBarSearchContentNode( - theme: component.theme, - placeholder: placeholder, - compactPlaceholder: compactPlaceholder, - activate: { [weak self] in - guard let self, let component = self.component, let searchContentNode = self.searchContentNode else { - return + searchContentNode = NavigationBarSearchContentNode( + theme: component.theme, + placeholder: placeholder, + compactPlaceholder: compactPlaceholder, + activate: { [weak self] in + guard let self, let component = self.component, let searchContentNode = self.searchContentNode else { + return + } + component.activateSearch(searchContentNode) } - component.activateSearch(searchContentNode) + ) + searchContentNode.view.layer.anchorPoint = CGPoint() + self.searchContentNode = searchContentNode + self.addSubview(searchContentNode.view) + } + + let searchSize = CGSize(width: currentLayout.size.width, height: navigationBarSearchContentHeight) + var searchFrame = CGRect(origin: CGPoint(x: 0.0, y: visibleSize.height - searchSize.height - self.bottomContentsContainer.bounds.height - 2.0), size: searchSize) + if let activeSearch = component.activeSearch, !activeSearch.isExternal { + searchFrame.origin.y = component.statusBarHeight + 8.0 + } + if component.tabsNode != nil { + searchFrame.origin.y -= 40.0 + } + if let activeSearch = component.activeSearch { + if !activeSearch.isExternal { + searchFrame.origin.y -= component.accessoryPanelContainerHeight } - ) - searchContentNode.view.layer.anchorPoint = CGPoint() - self.searchContentNode = searchContentNode - self.addSubview(searchContentNode.view) + } else { + searchFrame.origin.y -= component.accessoryPanelContainerHeight + } + + let clippedSearchOffset = max(0.0, min(clippedScrollOffset, searchOffsetDistance)) + let searchOffsetFraction = clippedSearchOffset / searchOffsetDistance + searchContentNode.expansionProgress = 1.0 - searchOffsetFraction + embeddedSearchBarExpansionHeight = 60.0 - floorToScreenPixels((1.0 - searchOffsetFraction) * searchSize.height) + if searchOffsetFraction > 0.0 { + searchFrame.origin.y -= (60.0 - 44.0) * 0.5 * searchOffsetFraction + } + + searchFrameValue = searchFrame + transition.setFrameWithAdditivePosition(view: searchContentNode.view, frame: searchFrame) + + let _ = searchContentNode.updateLayout(size: searchSize, leftInset: component.sideInset, rightInset: component.sideInset, transition: transition.containedViewLayoutTransition) + + var searchAlpha: CGFloat = search.isEnabled ? 1.0 : 0.5 + if let activeSearch = component.activeSearch, activeSearch.isExternal { + searchAlpha = 0.0 + } + transition.setAlpha(view: searchContentNode.view, alpha: searchAlpha) + searchContentNode.isUserInteractionEnabled = search.isEnabled + } else { + if let searchContentNode = self.searchContentNode { + self.searchContentNode = nil + searchContentNode.view.removeFromSuperview() + } } - let searchSize = CGSize(width: currentLayout.size.width - 6.0 * 2.0, height: navigationBarSearchContentHeight) - var searchFrame = CGRect(origin: CGPoint(x: 6.0, y: visibleSize.height - searchSize.height), size: searchSize) - if component.tabsNode != nil { - searchFrame.origin.y -= 40.0 - } - if !component.isSearchActive { - searchFrame.origin.y -= component.accessoryPanelContainerHeight + let edgeEffectHeight: CGFloat = currentLayout.size.height + 20.0 + var edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: currentLayout.size.width, height: edgeEffectHeight)) + if component.search != nil { + if component.activeSearch != nil { + edgeEffectFrame.origin.y -= 16.0 + } else { + edgeEffectFrame.origin.y -= embeddedSearchBarExpansionHeight + } + } else if component.activeSearch != nil { + edgeEffectFrame.origin.y -= 16.0 } + transition.setFrame(view: self.edgeEffectView, frame: edgeEffectFrame) + self.edgeEffectView.update(content: component.theme.list.plainBackgroundColor, blur: true, alpha: 0.55, rect: edgeEffectFrame, edge: .top, edgeSize: 40.0, transition: transition) - let clippedSearchOffset = max(0.0, min(clippedScrollOffset, searchOffsetDistance)) - let searchOffsetFraction = clippedSearchOffset / searchOffsetDistance - searchContentNode.expansionProgress = 1.0 - searchOffsetFraction - - transition.setFrameWithAdditivePosition(view: searchContentNode.view, frame: searchFrame) - - searchContentNode.updateLayout(size: searchSize, leftInset: component.sideInset, rightInset: component.sideInset, transition: transition.containedViewLayoutTransition) - - transition.setAlpha(view: searchContentNode.view, alpha: component.isSearchEnabled ? 1.0 : 0.5) - searchContentNode.isUserInteractionEnabled = component.isSearchEnabled let headerTransition = transition @@ -405,28 +487,31 @@ public final class ChatListNavigationBar: Component { containerSize: CGSize(width: currentLayout.size.width, height: 44.0) ) let headerContentY: CGFloat - if component.isSearchActive { + if component.activeSearch != nil { headerContentY = -headerContentSize.height } else { if component.statusBarHeight < 1.0 { headerContentY = 0.0 } else { - headerContentY = component.statusBarHeight + 5.0 + headerContentY = component.statusBarHeight + 10.0 } } let headerContentFrame = CGRect(origin: CGPoint(x: 0.0, y: headerContentY), size: headerContentSize) if let headerContentView = self.headerContent.view { if headerContentView.superview == nil { headerContentView.layer.anchorPoint = CGPoint() - self.addSubview(headerContentView) + self.addSubview(self.headerBackgroundContainer) + self.headerBackgroundContainer.contentView.addSubview(headerContentView) } - transition.setFrameWithAdditivePosition(view: headerContentView, frame: headerContentFrame) + transition.setFrameWithAdditivePosition(view: self.headerBackgroundContainer, frame: headerContentFrame) + self.headerBackgroundContainer.update(size: headerContentFrame.size, isDark: component.theme.overallDarkAppearance, transition: transition) + transition.setFrameWithAdditivePosition(view: headerContentView, frame: CGRect(origin: CGPoint(), size: headerContentFrame.size)) - if component.isSearchActive != (headerContentView.alpha == 0.0) { - headerContentView.alpha = component.isSearchActive ? 0.0 : 1.0 + if (component.activeSearch != nil) != (headerContentView.alpha == 0.0) { + headerContentView.alpha = component.activeSearch != nil ? 0.0 : 1.0 if !transition.animation.isImmediate { - if component.isSearchActive { + if component.activeSearch != nil { headerContentView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.14) } else { headerContentView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) @@ -435,6 +520,18 @@ public final class ChatListNavigationBar: Component { } } + let bottomContentsContainerPosition: CGPoint + if let activeSearch = component.activeSearch { + if let searchFrameValue, !activeSearch.isExternal { + bottomContentsContainerPosition = CGPoint(x: 0.0, y: searchFrameValue.maxY - 8.0) + } else { + bottomContentsContainerPosition = CGPoint(x: 0.0, y: -self.bottomContentsContainer.bounds.height) + } + } else { + bottomContentsContainerPosition = CGPoint(x: 0.0, y: visibleSize.height - self.bottomContentsContainer.bounds.height) + } + transition.setPosition(view: self.bottomContentsContainer, position: bottomContentsContainerPosition) + if component.tabsNode !== self.tabsNode { if let tabsNode = self.tabsNode { tabsNode.layer.anchorPoint = CGPoint() @@ -455,7 +552,8 @@ public final class ChatListNavigationBar: Component { } var tabsFrame = CGRect(origin: CGPoint(x: 0.0, y: visibleSize.height), size: CGSize(width: visibleSize.width, height: 46.0)) - if !component.isSearchActive { + if component.activeSearch != nil { + } else { tabsFrame.origin.y -= component.accessoryPanelContainerHeight } if component.tabsNode != nil { @@ -463,7 +561,8 @@ public final class ChatListNavigationBar: Component { } var accessoryPanelContainerFrame = CGRect(origin: CGPoint(x: 0.0, y: visibleSize.height), size: CGSize(width: visibleSize.width, height: component.accessoryPanelContainerHeight)) - if !component.isSearchActive { + if component.activeSearch != nil { + } else { accessoryPanelContainerFrame.origin.y -= component.accessoryPanelContainerHeight } @@ -528,14 +627,15 @@ public final class ChatListNavigationBar: Component { strings: component.strings, statusBarHeight: component.statusBarHeight, sideInset: component.sideInset, - isSearchActive: component.isSearchActive, - isSearchEnabled: component.isSearchEnabled, + search: component.search, + activeSearch: component.activeSearch, primaryContent: component.primaryContent, secondaryContent: component.secondaryContent, secondaryTransition: component.secondaryTransition, storySubscriptions: component.storySubscriptions, storiesIncludeHidden: component.storiesIncludeHidden, uploadProgress: storyUploadProgress, + headerPanels: component.headerPanels, tabsNode: component.tabsNode, tabsNodeIsSearch: component.tabsNodeIsSearch, accessoryPanelContainer: component.accessoryPanelContainer, @@ -575,8 +675,6 @@ public final class ChatListNavigationBar: Component { } func update(component: ChatListNavigationBar, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { - let themeUpdated = self.component?.theme !== component.theme - var uploadProgressUpdated = false var storySubscriptionsUpdated = false if let previousComponent = self.component { @@ -591,32 +689,85 @@ public final class ChatListNavigationBar: Component { self.component = component self.state = state - if themeUpdated { - self.backgroundView.updateColor(color: component.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate) - self.separatorLayer.backgroundColor = component.theme.rootController.navigationBar.separatorColor.cgColor - } - var contentHeight = component.statusBarHeight if component.statusBarHeight >= 1.0 { contentHeight += 3.0 } - contentHeight += 44.0 - - if component.isSearchActive { - if component.statusBarHeight < 1.0 { - contentHeight += 8.0 + if let activeSearch = component.activeSearch { + if !activeSearch.isExternal { + contentHeight += navigationBarSearchContentHeight } } else { - contentHeight += navigationBarSearchContentHeight + contentHeight += 44.0 + contentHeight += 9.0 + + if component.search != nil { + contentHeight += navigationBarSearchContentHeight + 2.0 + } } - if component.tabsNode != nil { - contentHeight += 40.0 + var headersContentHeight: CGFloat = 0.0 + if let disappearingHeaderPanelsView = self.disappearingHeaderPanels?.view { + let headerPanelsFrame = CGRect(origin: CGPoint(x: 0.0, y: headersContentHeight), size: disappearingHeaderPanelsView.bounds.size) + transition.setFrame(view: disappearingHeaderPanelsView, frame: headerPanelsFrame) + } + if let headerPanels = component.headerPanels { + let headerPanelsView: ComponentView + var headerPanelsTransition = transition + if let current = self.headerPanelsView { + headerPanelsView = current + } else { + headerPanelsTransition = headerPanelsTransition.withAnimation(.none) + headerPanelsView = ComponentView() + self.headerPanelsView = headerPanelsView + } + let headerPanelsSize = headerPanelsView.update( + transition: headerPanelsTransition, + component: headerPanels, + environment: {}, + containerSize: CGSize(width: availableSize.width - component.sideInset * 2.0, height: 10000.0) + ) + let headerPanelsFrame = CGRect(origin: CGPoint(x: component.sideInset, y: headersContentHeight), size: headerPanelsSize) + if let headerPanelsComponentView = headerPanelsView.view { + if headerPanelsComponentView.superview == nil { + self.bottomContentsContainer.addSubview(headerPanelsComponentView) + transition.animateAlpha(view: headerPanelsComponentView, from: 0.0, to: 1.0) + } + headerPanelsTransition.setFrame(view: headerPanelsComponentView, frame: headerPanelsFrame) + } + headersContentHeight += headerPanelsSize.height + } else if let headerPanelsView = self.headerPanelsView { + self.headerPanelsView = nil + self.disappearingHeaderPanels = headerPanelsView + + if let headerPanelsComponentView = headerPanelsView.view { + transition.setAlpha(view: headerPanelsComponentView, alpha: 0.0, completion: { [weak self, weak headerPanelsComponentView] _ in + guard let self, let headerPanelsComponentView else { + return + } + headerPanelsComponentView.removeFromSuperview() + if self.disappearingHeaderPanels?.view === headerPanelsComponentView { + self.disappearingHeaderPanels = nil + } + }) + } + } + headersContentHeight += 3.0 + transition.setBounds(view: self.bottomContentsContainer, bounds: CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: headersContentHeight))) + + if let activeSearch = component.activeSearch, !activeSearch.isExternal { + transition.setAlpha(view: self.bottomContentsContainer, alpha: 0.0) + } else { + transition.setAlpha(view: self.bottomContentsContainer, alpha: 1.0) } - if component.accessoryPanelContainer != nil && !component.isSearchActive { - contentHeight += component.accessoryPanelContainerHeight + if component.activeSearch == nil { + contentHeight += headersContentHeight + + if component.tabsNode != nil { + contentHeight += 40.0 + } } let size = CGSize(width: availableSize.width, height: contentHeight) diff --git a/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/NavigationButtonComponent.swift b/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/NavigationButtonComponent.swift new file mode 100644 index 00000000..5ebc8200 --- /dev/null +++ b/submodules/TelegramUI/Components/ChatListHeaderComponent/Sources/NavigationButtonComponent.swift @@ -0,0 +1,254 @@ +import Foundation +import UIKit +import ComponentFlow +import ChatListTitleView +import TelegramPresentationData +import Display +import MoreHeaderButton + +public final class NavigationButtonComponentEnvironment: Equatable { + public let theme: PresentationTheme + + public init(theme: PresentationTheme) { + self.theme = theme + } + + public static func ==(lhs: NavigationButtonComponentEnvironment, rhs: NavigationButtonComponentEnvironment) -> Bool { + if lhs.theme != rhs.theme { + return false + } + return true + } +} + +public final class NavigationButtonComponent: Component { + public typealias EnvironmentType = NavigationButtonComponentEnvironment + + public enum Content: Equatable { + case text(title: String, isBold: Bool) + case more + case icon(imageName: String) + case proxy(status: ChatTitleProxyStatus) + } + + public let content: Content + public let pressed: (UIView) -> Void + public let contextAction: ((UIView, ContextGesture?) -> Void)? + + public init( + content: Content, + pressed: @escaping (UIView) -> Void, + contextAction: ((UIView, ContextGesture?) -> Void)? = nil + ) { + self.content = content + self.pressed = pressed + self.contextAction = contextAction + } + + public static func ==(lhs: NavigationButtonComponent, rhs: NavigationButtonComponent) -> Bool { + if lhs.content != rhs.content { + return false + } + return true + } + + public final class View: HighlightTrackingButton { + private var textView: ImmediateTextView? + + private var iconView: UIImageView? + private var iconImageName: String? + + private var proxyNode: ChatTitleProxyNode? + + private var moreButton: MoreHeaderButton? + + private var component: NavigationButtonComponent? + private var theme: PresentationTheme? + + override init(frame: CGRect) { + super.init(frame: frame) + + self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside) + + self.highligthedChanged = { [weak self] highlighted in + guard let self else { + return + } + if highlighted { + self.textView?.alpha = 0.6 + self.proxyNode?.alpha = 0.6 + self.iconView?.alpha = 0.6 + } else { + self.textView?.alpha = 1.0 + self.textView?.layer.animateAlpha(from: 0.6, to: 1.0, duration: 0.2) + + self.proxyNode?.alpha = 1.0 + self.proxyNode?.layer.animateAlpha(from: 0.6, to: 1.0, duration: 0.2) + + self.iconView?.alpha = 1.0 + self.iconView?.layer.animateAlpha(from: 0.6, to: 1.0, duration: 0.2) + } + } + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc private func pressed() { + self.component?.pressed(self) + } + + func update(component: NavigationButtonComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + + let theme = environment[NavigationButtonComponentEnvironment.self].value.theme + var themeUpdated = false + if self.theme !== theme { + self.theme = theme + themeUpdated = true + } + + var textString: NSAttributedString? + var imageName: String? + var proxyStatus: ChatTitleProxyStatus? + var isMore: Bool = false + + switch component.content { + case let .text(title, isBold): + textString = NSAttributedString(string: title, font: isBold ? Font.bold(17.0) : Font.medium(17.0), textColor: theme.chat.inputPanel.panelControlColor) + case .more: + isMore = true + case let .icon(imageNameValue): + imageName = imageNameValue + case let .proxy(status): + proxyStatus = status + } + + var size = CGSize(width: 0.0, height: availableSize.height) + + if let textString = textString { + let textView: ImmediateTextView + if let current = self.textView { + textView = current + } else { + textView = ImmediateTextView() + textView.isUserInteractionEnabled = false + self.textView = textView + self.addSubview(textView) + } + + textView.attributedText = textString + let textSize = textView.updateLayout(availableSize) + let textInset: CGFloat = 12.0 + size.width = max(44.0, textSize.width + textInset * 2.0) + + textView.frame = CGRect(origin: CGPoint(x: floor((size.width - textSize.width) / 2.0), y: floor((availableSize.height - textSize.height) / 2.0)), size: textSize) + } else if let textView = self.textView { + self.textView = nil + textView.removeFromSuperview() + } + + if let imageName = imageName { + let iconView: UIImageView + if let current = self.iconView { + iconView = current + } else { + iconView = UIImageView() + iconView.isUserInteractionEnabled = false + self.iconView = iconView + self.addSubview(iconView) + } + if self.iconImageName != imageName || themeUpdated { + self.iconImageName = imageName + iconView.image = generateTintedImage(image: UIImage(bundleImageName: imageName), color: theme.chat.inputPanel.panelControlColor) + } + + if let iconSize = iconView.image?.size { + size.width = 44.0 + + iconView.frame = CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) / 2.0), y: floor((availableSize.height - iconSize.height) / 2.0)), size: iconSize) + } + } else if let iconView = self.iconView { + self.iconView = nil + iconView.removeFromSuperview() + self.iconImageName = nil + } + + if let proxyStatus = proxyStatus { + let proxyNode: ChatTitleProxyNode + if let current = self.proxyNode { + proxyNode = current + } else { + proxyNode = ChatTitleProxyNode(theme: theme) + proxyNode.isUserInteractionEnabled = false + self.proxyNode = proxyNode + self.addSubnode(proxyNode) + } + + let proxySize = CGSize(width: 30.0, height: 30.0) + size.width = 44.0 + + proxyNode.theme = theme + proxyNode.status = proxyStatus + + proxyNode.frame = CGRect(origin: CGPoint(x: floor((size.width - proxySize.width) / 2.0), y: floor((availableSize.height - proxySize.height) / 2.0)), size: proxySize) + } else if let proxyNode = self.proxyNode { + self.proxyNode = nil + proxyNode.removeFromSupernode() + } + + if isMore { + let moreButton: MoreHeaderButton + if let current = self.moreButton, !themeUpdated { + moreButton = current + } else { + if let moreButton = self.moreButton { + moreButton.removeFromSupernode() + self.moreButton = nil + } + + moreButton = MoreHeaderButton(color: theme.chat.inputPanel.panelControlColor) + moreButton.isUserInteractionEnabled = true + moreButton.setContent(.more(MoreHeaderButton.optionsCircleImage(color: theme.chat.inputPanel.panelControlColor))) + moreButton.onPressed = { [weak self] in + guard let self, let component = self.component else { + return + } + self.moreButton?.play() + component.pressed(self) + } + moreButton.contextAction = { [weak self] sourceNode, gesture in + guard let self, let component = self.component else { + return + } + self.moreButton?.play() + component.contextAction?(self, gesture) + } + self.moreButton = moreButton + self.addSubnode(moreButton) + } + + let buttonSize = CGSize(width: 44.0, height: 44.0) + size.width = 44.0 + + moreButton.setContent(.more(MoreHeaderButton.optionsCircleImage(color: theme.rootController.navigationBar.buttonColor))) + + moreButton.frame = CGRect(origin: CGPoint(x: floor((size.width - buttonSize.width) / 2.0), y: floor((size.height - buttonSize.height) / 2.0)), size: buttonSize) + } else if let moreButton = self.moreButton { + self.moreButton = nil + moreButton.removeFromSupernode() + } + + return size + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/ChatListTitleView/Sources/ChatListTitleProxyNode.swift b/submodules/TelegramUI/Components/ChatListTitleView/Sources/ChatListTitleProxyNode.swift index 5ec474b5..b3d2606b 100644 --- a/submodules/TelegramUI/Components/ChatListTitleView/Sources/ChatListTitleProxyNode.swift +++ b/submodules/TelegramUI/Components/ChatListTitleView/Sources/ChatListTitleProxyNode.swift @@ -44,13 +44,13 @@ public final class ChatTitleProxyNode: ASDisplayNode { if self.theme !== oldValue { switch self.status { case .connecting: - self.iconNode.image = generateIcon(color: theme.rootController.navigationBar.accentTextColor, connected: false, off: false) + self.iconNode.image = generateIcon(color: theme.chat.inputPanel.panelControlColor, connected: false, off: false) case .connected: - self.iconNode.image = generateIcon(color: theme.rootController.navigationBar.accentTextColor, connected: true, off: false) + self.iconNode.image = generateIcon(color: theme.chat.inputPanel.panelControlColor, connected: true, off: false) case .available: - self.iconNode.image = generateIcon(color: theme.rootController.navigationBar.accentTextColor, connected: false, off: true) + self.iconNode.image = generateIcon(color: theme.chat.inputPanel.panelControlColor, connected: false, off: true) } - self.activityIndicator.type = .custom(theme.rootController.navigationBar.accentTextColor, 10.0, 1.3333, true) + self.activityIndicator.type = .custom(theme.chat.inputPanel.panelControlColor, 10.0, 1.3333, true) } } } @@ -61,13 +61,13 @@ public final class ChatTitleProxyNode: ASDisplayNode { switch self.status { case .connecting: self.activityIndicator.isHidden = false - self.iconNode.image = generateIcon(color: theme.rootController.navigationBar.accentTextColor, connected: false, off: false) + self.iconNode.image = generateIcon(color: theme.chat.inputPanel.panelControlColor, connected: false, off: false) case .connected: self.activityIndicator.isHidden = true - self.iconNode.image = generateIcon(color: theme.rootController.navigationBar.accentTextColor, connected: true, off: false) + self.iconNode.image = generateIcon(color: theme.chat.inputPanel.panelControlColor, connected: true, off: false) case .available: self.activityIndicator.isHidden = true - self.iconNode.image = generateIcon(color: theme.rootController.navigationBar.accentTextColor, connected: false, off: true) + self.iconNode.image = generateIcon(color: theme.chat.inputPanel.panelControlColor, connected: false, off: true) } } } @@ -80,9 +80,9 @@ public final class ChatTitleProxyNode: ASDisplayNode { self.iconNode.isLayerBacked = true self.iconNode.displayWithoutProcessing = true self.iconNode.displaysAsynchronously = false - self.iconNode.image = generateIcon(color: theme.rootController.navigationBar.accentTextColor, connected: false, off: true) + self.iconNode.image = generateIcon(color: theme.chat.inputPanel.panelControlColor, connected: false, off: true) - self.activityIndicator = ActivityIndicator(type: .custom(theme.rootController.navigationBar.accentTextColor, 10.0, 1.3333, true), speed: .slow) + self.activityIndicator = ActivityIndicator(type: .custom(theme.chat.inputPanel.panelControlColor, 10.0, 1.3333, true), speed: .slow) super.init() diff --git a/submodules/TelegramUI/Components/ChatListTitleView/Sources/ChatListTitleView.swift b/submodules/TelegramUI/Components/ChatListTitleView/Sources/ChatListTitleView.swift index 0fc758a8..41cfe13d 100644 --- a/submodules/TelegramUI/Components/ChatListTitleView/Sources/ChatListTitleView.swift +++ b/submodules/TelegramUI/Components/ChatListTitleView/Sources/ChatListTitleView.swift @@ -60,9 +60,10 @@ public final class ChatListTitleView: UIView, NavigationBarTitleView, Navigation private let animationCache: AnimationCache private let animationRenderer: MultiAnimationRenderer + public var requestUpdate: ((ContainedViewLayoutTransition) -> Void)? public var openStatusSetup: ((UIView) -> Void)? - private var validLayout: (CGSize, CGRect)? + private var validLayout: CGSize? public var manualLayout: Bool = false @@ -321,13 +322,18 @@ public final class ChatListTitleView: UIView, NavigationBarTitleView, Navigation override public func layoutSubviews() { super.layoutSubviews() - if !self.manualLayout, let (size, clearBounds) = self.validLayout { - let _ = self.updateLayout(size: size, clearBounds: clearBounds, transition: .immediate) + if !self.manualLayout, let size = self.validLayout { + let _ = self.updateLayout(availableSize: size, transition: .immediate) } } - public func updateLayout(size: CGSize, clearBounds: CGRect, transition: ContainedViewLayoutTransition) -> CGRect { - self.validLayout = (size, clearBounds) + public func updateLayout(availableSize: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { + let _ = self.updateLayoutInternal(size: availableSize, transition: transition) + return availableSize + } + + public func updateLayoutInternal(size: CGSize, transition: ContainedViewLayoutTransition) -> CGRect { + self.validLayout = size var indicatorPadding: CGFloat = 0.0 let indicatorSize = self.activityIndicator.bounds.size @@ -335,7 +341,7 @@ public final class ChatListTitleView: UIView, NavigationBarTitleView, Navigation if !self.activityIndicator.isHidden { indicatorPadding = indicatorSize.width + 6.0 } - var maxTitleWidth = clearBounds.size.width - indicatorPadding + var maxTitleWidth = size.width - indicatorPadding var proxyPadding: CGFloat = 0.0 if !self.proxyNode.isHidden { maxTitleWidth -= 25.0 @@ -353,7 +359,7 @@ public final class ChatListTitleView: UIView, NavigationBarTitleView, Navigation var titleContentRect = CGRect(origin: CGPoint(x: indicatorPadding + floor((size.width - combinedWidth - indicatorPadding) / 2.0), y: floor((size.height - combinedHeight) / 2.0)), size: titleSize) - titleContentRect.origin.x = min(titleContentRect.origin.x, clearBounds.maxX - proxyPadding - titleContentRect.width) + titleContentRect.origin.x = min(titleContentRect.origin.x, size.width - proxyPadding - titleContentRect.width) let titleFrame = titleContentRect var titleTransition = transition @@ -362,7 +368,7 @@ public final class ChatListTitleView: UIView, NavigationBarTitleView, Navigation } titleTransition.updateFrame(node: self.titleNode, frame: titleFrame) - let proxyFrame = CGRect(origin: CGPoint(x: clearBounds.maxX - 9.0 - self.proxyNode.bounds.width, y: floor((size.height - self.proxyNode.bounds.height) / 2.0)), size: self.proxyNode.bounds.size) + let proxyFrame = CGRect(origin: CGPoint(x: size.width - 9.0 - self.proxyNode.bounds.width, y: floor((size.height - self.proxyNode.bounds.height) / 2.0)), size: self.proxyNode.bounds.size) self.proxyNode.frame = proxyFrame self.proxyButton.frame = proxyFrame.insetBy(dx: -2.0, dy: -2.0) diff --git a/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeScreen.swift b/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeScreen.swift index 4e49cf0c..5e1db9b4 100644 --- a/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeScreen.swift +++ b/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeScreen.swift @@ -179,7 +179,7 @@ private final class ChatScheduleTimeSheetContentComponent: Component { component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: environment.theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: environment.theme.chat.inputPanel.panelControlColor ) )), action: { [weak self] _ in diff --git a/submodules/TelegramUI/Components/ChatThemeScreen/BUILD b/submodules/TelegramUI/Components/ChatThemeScreen/BUILD index 783bf604..cb29f704 100644 --- a/submodules/TelegramUI/Components/ChatThemeScreen/BUILD +++ b/submodules/TelegramUI/Components/ChatThemeScreen/BUILD @@ -37,6 +37,9 @@ swift_library( "//submodules/AppBundle", "//submodules/ActivityIndicator", "//submodules/TelegramUI/Components/Gifts/GiftItemComponent", + "//submodules/TelegramUI/Components/AlertComponent", + "//submodules/TelegramUI/Components/AlertComponent/AlertTransferHeaderComponent", + "//submodules/TelegramUI/Components/AvatarComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/ChatThemeScreen/Sources/ChatThemeScreen.swift b/submodules/TelegramUI/Components/ChatThemeScreen/Sources/ChatThemeScreen.swift index 589c5c55..abcfe86f 100644 --- a/submodules/TelegramUI/Components/ChatThemeScreen/Sources/ChatThemeScreen.swift +++ b/submodules/TelegramUI/Components/ChatThemeScreen/Sources/ChatThemeScreen.swift @@ -22,6 +22,7 @@ import TelegramAnimatedStickerNode import ShimmerEffect import AttachmentUI import AvatarNode +import AlertComponent private struct ThemeSettingsThemeEntry: Comparable, Identifiable { let index: Int @@ -308,7 +309,7 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode { self.placeholderNode = StickerShimmerEffectNode() - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.containerNode) self.containerNode.addSubnode(self.imageNode) @@ -925,7 +926,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, ASScrollViewDelega self.animationNode = AnimationNode(animation: self.isDarkAppearance ? "anim_sun_reverse" : "anim_sun", colors: iconColors(theme: self.presentationData.theme), scale: 1.0) self.animationNode.isUserInteractionEnabled = false - self.doneButton = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(theme: self.presentationData.theme), height: 50.0, cornerRadius: 11.0) + self.doneButton = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(theme: self.presentationData.theme), glass: true, height: 52.0, cornerRadius: 26.0) self.otherButton = HighlightableButtonNode() @@ -1530,7 +1531,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, ASScrollViewDelega override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { var presentingAlertController = false self.controller?.forEachController({ c in - if c is AlertController { + if c is AlertScreen { presentingAlertController = true } return true @@ -1605,13 +1606,14 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, ASScrollViewDelega let cancelFrame = CGRect(origin: CGPoint(x: 16.0, y: 0.0), size: cancelSize) transition.updateFrame(node: self.cancelButtonNode, frame: cancelFrame) + let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: layout.intrinsicInsets.bottom, innerDiameter: 52.0, sideInset: 30.0) let buttonInset: CGFloat = 16.0 - let doneButtonHeight = self.doneButton.updateLayout(width: contentFrame.width - buttonInset * 2.0, transition: transition) + let doneButtonHeight = self.doneButton.updateLayout(width: contentFrame.width - buttonInsets.left - buttonInsets.right, transition: transition) var doneY = contentHeight - doneButtonHeight - 2.0 - insets.bottom if self.controller?.canResetWallpaper == true { doneY = contentHeight - doneButtonHeight - 52.0 - insets.bottom } - transition.updateFrame(node: self.doneButton, frame: CGRect(x: buttonInset, y: doneY, width: contentFrame.width, height: doneButtonHeight)) + transition.updateFrame(node: self.doneButton, frame: CGRect(x: buttonInsets.left, y: doneY, width: contentFrame.width, height: doneButtonHeight)) let otherButtonSize = self.otherButton.measure(CGSize(width: contentFrame.width - buttonInset * 2.0, height: .greatestFiniteMagnitude)) self.otherButton.frame = CGRect(origin: CGPoint(x: floor((contentFrame.width - otherButtonSize.width) / 2.0), y: contentHeight - otherButtonSize.height - insets.bottom - 15.0), size: otherButtonSize) diff --git a/submodules/TelegramUI/Components/ChatThemeScreen/Sources/GiftThemeTransferAlertController.swift b/submodules/TelegramUI/Components/ChatThemeScreen/Sources/GiftThemeTransferAlertController.swift index 03ea7800..7b4a4907 100644 --- a/submodules/TelegramUI/Components/ChatThemeScreen/Sources/GiftThemeTransferAlertController.swift +++ b/submodules/TelegramUI/Components/ChatThemeScreen/Sources/GiftThemeTransferAlertController.swift @@ -3,293 +3,65 @@ import UIKit import AsyncDisplayKit import Display import ComponentFlow -import Postbox import TelegramCore import TelegramPresentationData -import TelegramUIPreferences import AccountContext -import AppBundle -import AvatarNode -import Markdown +import AlertComponent +import AlertTransferHeaderComponent import GiftItemComponent -import ActivityIndicator - -private final class GiftThemeTransferAlertContentNode: AlertContentNode { - private let context: AccountContext - private let strings: PresentationStrings - private var presentationTheme: PresentationTheme - private let title: String - private let text: String - private let gift: StarGift.UniqueGift - - private let titleNode: ASTextNode - private let giftView = ComponentView() - private let textNode: ASTextNode - private let arrowNode: ASImageNode - private let avatarNode: AvatarNode - - private let actionNodesSeparator: ASDisplayNode - private let actionNodes: [TextAlertContentActionNode] - private let actionVerticalSeparators: [ASDisplayNode] - - private var activityIndicator: ActivityIndicator? - - private var validLayout: CGSize? - - var inProgress = false { - didSet { - if let size = self.validLayout { - let _ = self.updateLayout(size: size, transition: .immediate) - } - } - } - - override var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled - } - - init( - context: AccountContext, - theme: AlertControllerTheme, - ptheme: PresentationTheme, - strings: PresentationStrings, - gift: StarGift.UniqueGift, - peer: EnginePeer, - title: String, - text: String, - actions: [TextAlertAction] - ) { - self.context = context - self.strings = strings - self.presentationTheme = ptheme - self.title = title - self.text = text - self.gift = gift - - self.titleNode = ASTextNode() - self.titleNode.maximumNumberOfLines = 0 - - self.textNode = ASTextNode() - self.textNode.maximumNumberOfLines = 0 - - self.arrowNode = ASImageNode() - self.arrowNode.displaysAsynchronously = false - self.arrowNode.displayWithoutProcessing = true - - self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0)) - - self.actionNodesSeparator = ASDisplayNode() - self.actionNodesSeparator.isLayerBacked = true - - self.actionNodes = actions.map { action -> TextAlertContentActionNode in - return TextAlertContentActionNode(theme: theme, action: action) - } - - var actionVerticalSeparators: [ASDisplayNode] = [] - if actions.count > 1 { - for _ in 0 ..< actions.count - 1 { - let separatorNode = ASDisplayNode() - separatorNode.isLayerBacked = true - actionVerticalSeparators.append(separatorNode) - } - } - self.actionVerticalSeparators = actionVerticalSeparators - - super.init() - - self.addSubnode(self.titleNode) - self.addSubnode(self.textNode) - self.addSubnode(self.arrowNode) - self.addSubnode(self.avatarNode) - - self.addSubnode(self.actionNodesSeparator) - - for actionNode in self.actionNodes { - self.addSubnode(actionNode) - } - - for separatorNode in self.actionVerticalSeparators { - self.addSubnode(separatorNode) - } - - self.updateTheme(theme) - - self.avatarNode.setPeer(context: context, theme: ptheme, peer: peer) - } - - override func updateTheme(_ theme: AlertControllerTheme) { - self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.semibold(17.0), textColor: theme.primaryColor) - self.textNode.attributedText = parseMarkdownIntoAttributedString(self.text, attributes: MarkdownAttributes( - body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor), - bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: theme.primaryColor), - link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor), - linkAttribute: { url in - return ("URL", url) - } - ), textAlignment: .center) - self.arrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Media Editor/CutoutUndo"), color: theme.secondaryColor.withAlphaComponent(0.9)) - - self.actionNodesSeparator.backgroundColor = theme.separatorColor - for actionNode in self.actionNodes { - actionNode.updateTheme(theme) - } - for separatorNode in self.actionVerticalSeparators { - separatorNode.backgroundColor = theme.separatorColor - } - - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) - } - } - - override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - var size = size - size.width = min(size.width, 270.0) - - self.validLayout = size - - var origin: CGPoint = CGPoint(x: 0.0, y: 20.0) - - let avatarSize = CGSize(width: 60.0, height: 60.0) - self.avatarNode.updateSize(size: avatarSize) - - let giftFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) - 52.0, y: origin.y), size: avatarSize) - - let _ = self.giftView.update( - transition: .immediate, - component: AnyComponent( - GiftItemComponent( - context: self.context, - theme: self.presentationTheme, - strings: self.strings, - peer: nil, - subject: .uniqueGift(gift: self.gift, price: nil), - mode: .thumbnail - ) - ), - environment: {}, - containerSize: avatarSize - ) - if let view = self.giftView.view { - if view.superview == nil { - self.view.addSubview(view) - } - view.frame = giftFrame - } - - if let arrowImage = self.arrowNode.image { - let arrowFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - arrowImage.size.width) / 2.0), y: origin.y + floorToScreenPixels((avatarSize.height - arrowImage.size.height) / 2.0)), size: arrowImage.size) - transition.updateFrame(node: self.arrowNode, frame: arrowFrame) - } - - let avatarFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) + 52.0, y: origin.y), size: avatarSize) - transition.updateFrame(node: self.avatarNode, frame: avatarFrame) - - origin.y += avatarSize.height + 17.0 - - let titleSize = self.titleNode.measure(CGSize(width: size.width - 32.0, height: size.height)) - transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize)) - origin.y += titleSize.height + 5.0 - - let textSize = self.textNode.measure(CGSize(width: size.width - 32.0, height: size.height)) - transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize)) - origin.y += textSize.height + 10.0 - - let actionButtonHeight: CGFloat = 44.0 - var minActionsWidth: CGFloat = 0.0 - let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) - let actionTitleInsets: CGFloat = 8.0 - - for actionNode in self.actionNodes { - let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) - minActionsWidth += actionTitleSize.width + actionTitleInsets - } - - let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0) - - let contentWidth = max(size.width, minActionsWidth) - - let actionsHeight = actionButtonHeight - - let resultSize = CGSize(width: contentWidth, height: avatarSize.height + titleSize.height + textSize.height + actionsHeight + 24.0 + insets.top + insets.bottom) - self.actionNodesSeparator.frame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)) - - var actionOffset: CGFloat = 0.0 - let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) - var separatorIndex = -1 - var nodeIndex = 0 - for actionNode in self.actionNodes { - if separatorIndex >= 0 { - let separatorNode = self.actionVerticalSeparators[separatorIndex] - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) - } - separatorIndex += 1 - - let currentActionWidth: CGFloat - if nodeIndex == self.actionNodes.count - 1 { - currentActionWidth = resultSize.width - actionOffset - } else { - currentActionWidth = actionWidth - } - - let actionNodeFrame: CGRect - actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += currentActionWidth - - transition.updateFrame(node: actionNode, frame: actionNodeFrame) - - nodeIndex += 1 - } - - if self.inProgress { - let activityIndicator: ActivityIndicator - if let current = self.activityIndicator { - activityIndicator = current - } else { - activityIndicator = ActivityIndicator(type: .custom(self.presentationTheme.list.freeInputField.controlColor, 18.0, 1.5, false)) - self.addSubnode(activityIndicator) - } - - if let actionNode = self.actionNodes.first { - actionNode.isHidden = true - - let indicatorSize = CGSize(width: 22.0, height: 22.0) - transition.updateFrame(node: activityIndicator, frame: CGRect(origin: CGPoint(x: actionNode.frame.minX + floor((actionNode.frame.width - indicatorSize.width) / 2.0), y: actionNode.frame.minY + floor((actionNode.frame.height - indicatorSize.height) / 2.0)), size: indicatorSize)) - } - } - - return resultSize - } -} +import AvatarComponent public func giftThemeTransferAlertController( context: AccountContext, gift: StarGift.UniqueGift, previousPeer: EnginePeer, commit: @escaping () -> Void -) -> AlertController { +) -> ViewController { let presentationData = context.sharedContext.currentPresentationData.with { $0 } let strings = presentationData.strings - - var contentNode: GiftThemeTransferAlertContentNode? - var dismissImpl: ((Bool) -> Void)? - let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - dismissImpl?(true) - }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Conversation_Theme_GiftTransfer_Proceed, action: { - dismissImpl?(true) - commit() - })] - let text = strings.Conversation_Theme_GiftTransfer_Text(previousPeer.compactDisplayTitle).string - contentNode = GiftThemeTransferAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: strings, gift: gift, peer: previousPeer, title: "", text: text, actions: actions) + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "header", + component: AnyComponent( + AlertTransferHeaderComponent( + fromComponent: AnyComponentWithIdentity(id: "gift", component: AnyComponent( + GiftItemComponent( + context: context, + theme: presentationData.theme, + strings: strings, + peer: nil, + subject: .uniqueGift(gift: gift, price: nil), + mode: .thumbnail + ) + )), + toComponent: AnyComponentWithIdentity(id: "user", component: AnyComponent( + AvatarComponent( + context: context, + theme: presentationData.theme, + peer: previousPeer + ) + )), + type: .take + ) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(strings.Conversation_Theme_GiftTransfer_Text(previousPeer.compactDisplayTitle).string)) + ) + )) - let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode!) - dismissImpl = { [weak controller] animated in - if animated { - controller?.dismissAnimated() - } else { - controller?.dismiss() - } - } - return controller + let alertController = AlertScreen( + context: context, + content: content, + actions: [ + .init(title: strings.Common_Cancel), + .init(title: strings.Conversation_Theme_GiftTransfer_Proceed, type: .default, action: { + commit() + }) + ] + ) + return alertController } diff --git a/submodules/TelegramUI/Components/ChatTitleView/BUILD b/submodules/TelegramUI/Components/ChatTitleView/BUILD index 26fb6712..aafb2e9f 100644 --- a/submodules/TelegramUI/Components/ChatTitleView/BUILD +++ b/submodules/TelegramUI/Components/ChatTitleView/BUILD @@ -31,6 +31,8 @@ swift_library( "//submodules/TelegramUI/Components/AnimationCache:AnimationCache", "//submodules/TelegramUI/Components/MultiAnimationRenderer:MultiAnimationRenderer", "//submodules/Components/ComponentDisplayAdapters:ComponentDisplayAdapters", + "//submodules/TelegramUI/Components/GlassBackgroundComponent", + "//submodules/TelegramUI/Components/AnimatedTextComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleComponent.swift b/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleComponent.swift new file mode 100644 index 00000000..9b62a002 --- /dev/null +++ b/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleComponent.swift @@ -0,0 +1,1164 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import TelegramPresentationData +import AccountContext +import TelegramUIPreferences +import Postbox +import TelegramCore +import PeerPresenceStatusManager +import ChatTitleActivityNode +import AnimatedTextComponent +import PhoneNumberFormat +import TelegramStringFormatting +import EmojiStatusComponent +import GlassBackgroundComponent + +public final class ChatNavigationBarTitleView: UIView, NavigationBarTitleView { + private final class ContentData { + let context: AccountContext + let theme: PresentationTheme + let strings: PresentationStrings + let dateTimeFormat: PresentationDateTimeFormat + let nameDisplayOrder: PresentationPersonNameOrder + let content: ChatTitleContent + + init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, content: ChatTitleContent) { + self.context = context + self.theme = theme + self.strings = strings + self.dateTimeFormat = dateTimeFormat + self.nameDisplayOrder = nameDisplayOrder + self.content = content + } + } + + private let parentTitleState = ComponentState() + private let title = ComponentView() + + private var contentData: ContentData? + private var activities: ChatTitleComponent.Activities? + private var networkState: AccountNetworkState? + + public var requestUpdate: ((ContainedViewLayoutTransition) -> Void)? + public var tapAction: (() -> Void)? + public var longTapAction: (() -> Void)? + + override public init(frame: CGRect) { + super.init(frame: frame) + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public func animateLayoutTransition() { + } + + public func prepareSnapshotState() -> ChatTitleView.SnapshotState? { + //return titleView.contentView?.snapshotView(afterScreenUpdates: false) + return nil + } + + public func animateFromSnapshot(_ snapshotState: ChatTitleView.SnapshotState, direction: ChatTitleView.AnimateFromSnapshotDirection) { + guard let titleView = self.title.view as? ChatTitleComponent.View else { + return + } + //titleView.contentView?.animateFromSnapshot(snapshotState, direction: direction) + titleView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + + public func update( + context: AccountContext, + theme: PresentationTheme, + strings: PresentationStrings, + dateTimeFormat: PresentationDateTimeFormat, + nameDisplayOrder: PresentationPersonNameOrder, + content: ChatTitleContent, + transition: ComponentTransition + ) { + self.contentData = ContentData( + context: context, + theme: theme, + strings: strings, + dateTimeFormat: dateTimeFormat, + nameDisplayOrder: nameDisplayOrder, + content: content + ) + self.update(transition: transition) + } + + public func updateActivities(activities: ChatTitleComponent.Activities?, transition: ComponentTransition) { + if self.activities != activities { + self.activities = activities + self.update(transition: transition) + } + } + + public func updateNetworkState(networkState: AccountNetworkState, transition: ComponentTransition) { + if self.networkState != networkState { + self.networkState = networkState + self.update(transition: transition) + } + } + + private func update(transition: ComponentTransition) { + self.requestUpdate?(transition.containedViewLayoutTransition) + } + + public func updateLayout(availableSize: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { + let transition = ComponentTransition(transition) + + if let contentData = self.contentData { + let titleSize = self.title.update( + transition: transition, + component: AnyComponent(ChatTitleComponent( + context: contentData.context, + theme: contentData.theme, + strings: contentData.strings, + dateTimeFormat: contentData.dateTimeFormat, + nameDisplayOrder: contentData.nameDisplayOrder, + displayBackground: true, + content: contentData.content, + activities: self.activities, + networkState: self.networkState, + tapped: { [weak self] in + guard let self else { + return + } + self.tapAction?() + }, + longTapped: { [weak self] in + guard let self else { + return + } + self.longTapAction?() + } + )), + environment: {}, + containerSize: availableSize + ) + if let titleView = self.title.view { + if titleView.superview == nil { + self.title.parentState = self.parentTitleState + self.parentTitleState._updated = { [weak self] transition, _ in + guard let self else { + return + } + self.requestUpdate?(transition.containedViewLayoutTransition) + } + self.addSubview(titleView) + } + transition.setFrame(view: titleView, frame: CGRect(origin: CGPoint(), size: titleSize)) + } + return titleSize + } else { + return availableSize + } + } +} + +public final class ChatTitleComponent: Component { + public struct Activities: Equatable { + public struct Item: Equatable { + public let peer: EnginePeer + public let activity: PeerInputActivity + + public init(peer: EnginePeer, activity: PeerInputActivity) { + self.peer = peer + self.activity = activity + } + } + + public let peerId: EnginePeer.Id + public let items: [Item] + + public init(peerId: EnginePeer.Id, items: [Item]) { + self.peerId = peerId + self.items = items + } + } + + public let context: AccountContext + public let theme: PresentationTheme + public let strings: PresentationStrings + public let dateTimeFormat: PresentationDateTimeFormat + public let nameDisplayOrder: PresentationPersonNameOrder + public let displayBackground: Bool + public let content: ChatTitleContent + public let activities: Activities? + public let networkState: AccountNetworkState? + public let tapped: () -> Void + public let longTapped: () -> Void + + public init( + context: AccountContext, + theme: PresentationTheme, + strings: PresentationStrings, + dateTimeFormat: PresentationDateTimeFormat, + nameDisplayOrder: PresentationPersonNameOrder, + displayBackground: Bool, + content: ChatTitleContent, + activities: Activities?, + networkState: AccountNetworkState?, + tapped: @escaping () -> Void, + longTapped: @escaping () -> Void + ) { + self.context = context + self.theme = theme + self.strings = strings + self.dateTimeFormat = dateTimeFormat + self.nameDisplayOrder = nameDisplayOrder + self.displayBackground = displayBackground + self.content = content + self.activities = activities + self.networkState = networkState + self.tapped = tapped + self.longTapped = longTapped + } + + public static func ==(lhs: ChatTitleComponent, rhs: ChatTitleComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.theme !== rhs.theme { + return false + } + if lhs.strings !== rhs.strings { + return false + } + if lhs.dateTimeFormat != rhs.dateTimeFormat { + return false + } + if lhs.nameDisplayOrder != rhs.nameDisplayOrder { + return false + } + if lhs.displayBackground != rhs.displayBackground { + return false + } + if lhs.content != rhs.content { + return false + } + if lhs.activities != rhs.activities { + return false + } + if lhs.networkState != rhs.networkState { + return false + } + return true + } + + public final class View: UIView { + private var backgroundView: GlassBackgroundView? + private let contentContainer: UIView + private let title = ComponentView() + private var subtitleNode: ChatTitleActivityNode? + private var activityMeasureSubtitleNode: ChatTitleActivityNode? + private var leftIcon: ComponentView? + private var rightIcon: ComponentView? + private var credibilityIcon: ComponentView? + private var verifiedIcon: ComponentView? + private var statusIcon: ComponentView? + + private var presenceManager: PeerPresenceStatusManager? + + private var component: ChatTitleComponent? + private weak var state: EmptyComponentState? + + override init(frame: CGRect) { + self.contentContainer = UIView() + self.contentContainer.clipsToBounds = true + + super.init(frame: frame) + + self.presenceManager = PeerPresenceStatusManager(update: { [weak self] in + guard let self else { + return + } + self.state?.updated(transition: .spring(duration: 0.4)) + }) + + let recognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.onTapGesture(_:))) + recognizer.tapActionAtPoint = { _ in + return .waitForSingleTap + } + self.contentContainer.addGestureRecognizer(recognizer) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc private func onTapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) { + if let (gesture, _) = recognizer.lastRecognizedGestureAndLocation { + switch gesture { + case .tap: + self.component?.tapped() + case .longTap: + self.component?.longTapped() + default: + break + } + } + } + + func update(component: ChatTitleComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + let statusIconsSpacing: CGFloat = 4.0 + let leftTitleIconSpacing: CGFloat = 3.0 + let rightTitleIconSpacing: CGFloat = 3.0 + let containerSideInset: CGFloat = 14.0 + + self.component = component + self.state = state + + var titleSegments: [AnimatedTextComponent.Item] = [] + var titleLeftIcon: TitleIconComponent.Kind? + var titleRightIcon: TitleIconComponent.Kind? + var titleCredibilityIcon: ChatTitleCredibilityIcon = .none + var titleVerifiedIcon: ChatTitleCredibilityIcon = .none + var titleStatusIcon: ChatTitleCredibilityIcon = .none + var isEnabled = true + switch component.content { + case let .peer(peerView, customTitle, _, _, isScheduledMessages, isMuted, _, isEnabledValue): + if peerView.peerId.isReplies { + titleSegments = [AnimatedTextComponent.Item( + id: AnyHashable(0), + isUnbreakable: true, + content: .text(component.strings.DialogList_Replies) + )] + isEnabled = false + } else if isScheduledMessages { + if peerView.peerId == component.context.account.peerId { + titleSegments = [AnimatedTextComponent.Item( + id: AnyHashable(0), + isUnbreakable: true, + content: .text(component.strings.ScheduledMessages_RemindersTitle) + )] + } else { + titleSegments = [AnimatedTextComponent.Item( + id: AnyHashable(0), + isUnbreakable: true, + content: .text(component.strings.ScheduledMessages_Title) + )] + } + isEnabled = false + } else { + if let peer = peerView.peer { + if let customTitle { + titleSegments = [AnimatedTextComponent.Item( + id: AnyHashable(0), + isUnbreakable: true, + content: .text(customTitle) + )] + } else if peerView.peerId == component.context.account.peerId { + if peerView.isSavedMessages { + titleSegments = [AnimatedTextComponent.Item( + id: AnyHashable(0), + isUnbreakable: true, + content: .text(component.strings.Conversation_MyNotes) + )] + } else { + titleSegments = [AnimatedTextComponent.Item( + id: AnyHashable(0), + isUnbreakable: true, + content: .text(component.strings.Conversation_SavedMessages) + )] + } + } else if peerView.peerId.isAnonymousSavedMessages { + titleSegments = [AnimatedTextComponent.Item( + id: AnyHashable(0), + isUnbreakable: true, + content: .text(component.strings.ChatList_AuthorHidden) + )] + } else { + if !peerView.isContact, let user = peer as? TelegramUser, !user.flags.contains(.isSupport), user.botInfo == nil, let phone = user.phone, !phone.isEmpty { + titleSegments = [AnimatedTextComponent.Item( + id: AnyHashable(0), + isUnbreakable: true, + content: .text(formatPhoneNumber(context: component.context, number: phone)) + )] + } else { + titleSegments = [AnimatedTextComponent.Item( + id: AnyHashable(0), + isUnbreakable: true, + content: .text(EnginePeer(peer).displayTitle(strings: component.strings, displayOrder: component.nameDisplayOrder)) + )] + } + } + if peer.id != component.context.account.peerId { + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: component.context.currentAppConfiguration.with({ $0 })) + if peer.isFake { + titleCredibilityIcon = .fake + } else if peer.isScam { + titleCredibilityIcon = .scam + } else if let emojiStatus = peer.emojiStatus { + titleStatusIcon = .emojiStatus(emojiStatus) + } else if peer.isPremium && !premiumConfiguration.isPremiumDisabled { + titleCredibilityIcon = .premium + } + + if peer.isVerified { + titleCredibilityIcon = .verified + } + if let verificationIconFileId = peer.verificationIconFileId { + titleVerifiedIcon = .emojiStatus(PeerEmojiStatus(content: .emoji(fileId: verificationIconFileId), expirationDate: nil)) + } + } + } + if peerView.peerId.namespace == Namespaces.Peer.SecretChat { + titleLeftIcon = .lock + } + if let isMuted { + if isMuted { + titleRightIcon = .mute + } + } else { + if let notificationSettings = peerView.notificationSettings { + if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) { + if titleCredibilityIcon != .verified { + titleRightIcon = .mute + } + } + } + } + if peerView.peerId.isVerificationCodes { + isEnabled = false + } else { + isEnabled = isEnabledValue + } + } + case let .replyThread(type, count): + if count > 0 { + var commentsPart: String + switch type { + case .comments: + commentsPart = component.strings.Conversation_TitleComments(Int32(count)) + case .replies: + commentsPart = component.strings.Conversation_TitleReplies(Int32(count)) + } + + if commentsPart.contains("[") && commentsPart.contains("]") { + if let startIndex = commentsPart.firstIndex(of: "["), let endIndex = commentsPart.firstIndex(of: "]") { + commentsPart.removeSubrange(startIndex ... endIndex) + } + } else { + commentsPart = commentsPart.trimmingCharacters(in: CharacterSet(charactersIn: "0123456789-,.")) + } + + let rawTextAndRanges: PresentationStrings.FormattedString + switch type { + case .comments: + rawTextAndRanges = component.strings.Conversation_TitleCommentsFormat("\(count)", commentsPart) + case .replies: + rawTextAndRanges = component.strings.Conversation_TitleRepliesFormat("\(count)", commentsPart) + } + + let rawText = rawTextAndRanges.string + + var textIndex = 0 + var latestIndex = 0 + for indexAndRange in rawTextAndRanges.ranges { + let index = indexAndRange.index + let range = indexAndRange.range + + var lowerSegmentIndex = range.lowerBound + if index != 0 { + lowerSegmentIndex = min(lowerSegmentIndex, latestIndex) + } else { + if latestIndex < range.lowerBound { + let part = String(rawText[rawText.index(rawText.startIndex, offsetBy: latestIndex) ..< rawText.index(rawText.startIndex, offsetBy: range.lowerBound)]) + + titleSegments.append(AnimatedTextComponent.Item( + id: AnyHashable(textIndex), + isUnbreakable: true, + content: .text(part) + )) + textIndex += 1 + } + } + latestIndex = range.upperBound + + let part = String(rawText[rawText.index(rawText.startIndex, offsetBy: lowerSegmentIndex) ..< rawText.index(rawText.startIndex, offsetBy: min(rawText.count, range.upperBound))]) + if index == 0 { + titleSegments.append(AnimatedTextComponent.Item( + id: AnyHashable(textIndex), + isUnbreakable: false, + content: .text(part) + )) + textIndex += 1 + } else { + titleSegments.append(AnimatedTextComponent.Item( + id: AnyHashable(textIndex), + isUnbreakable: true, + content: .text(part) + )) + textIndex += 1 + } + } + if latestIndex < rawText.count { + let part = String(rawText[rawText.index(rawText.startIndex, offsetBy: latestIndex)...]) + titleSegments.append(AnimatedTextComponent.Item( + id: AnyHashable(textIndex), + isUnbreakable: true, + content: .text(part) + )) + textIndex += 1 + } + } else { + switch type { + case .comments: + titleSegments = [AnimatedTextComponent.Item( + id: AnyHashable(0), + isUnbreakable: true, + content: .text(component.strings.Conversation_TitleCommentsEmpty) + )] + case .replies: + titleSegments = [AnimatedTextComponent.Item( + id: AnyHashable(0), + isUnbreakable: true, + content: .text(component.strings.Conversation_TitleRepliesEmpty) + )] + } + } + + isEnabled = false + case let .custom(textItems, _, enabled): + titleSegments = textItems.map { item -> AnimatedTextComponent.Item in + let mappedContent: AnimatedTextComponent.Item.Content + switch item.content { + case let .number(value, minDigits): + mappedContent = .number(value, minDigits: minDigits) + case let .text(text): + mappedContent = .text(text) + } + return AnimatedTextComponent.Item( + id: item.id, + isUnbreakable: item.isUnbreakable, + content: mappedContent + ) + } + isEnabled = enabled + } + + var accessibilityText = "" + for segment in titleSegments { + switch segment.content { + case let .number(value, _): + accessibilityText.append("\(value)") + case let .text(string): + accessibilityText.append(string) + case .icon: + break + } + } + self.accessibilityLabel = accessibilityText + + var inputActivitiesAllowed = true + switch component.content { + case let .peer(peerView, _, _, _, isScheduledMessages, _, _, _): + if let peer = peerView.peer { + if peer.id == component.context.account.peerId || isScheduledMessages || peer.id.isRepliesOrVerificationCodes { + inputActivitiesAllowed = false + } + } + case .replyThread: + inputActivitiesAllowed = true + default: + inputActivitiesAllowed = false + } + + let subtitleFont = Font.regular(12.0) + var state: ChatTitleActivityNodeState = .none + switch component.networkState { + case .waitingForNetwork, .connecting, .updating: + var infoText: String + switch component.networkState { + case .waitingForNetwork: + infoText = component.strings.ChatState_WaitingForNetwork + case .connecting: + infoText = component.strings.ChatState_Connecting + case .updating: + infoText = component.strings.ChatState_Updating + case .online, .none: + infoText = "" + } + state = .info(NSAttributedString(string: infoText, font: subtitleFont, textColor: component.theme.chat.inputPanel.inputControlColor), .generic) + case .online, .none: + if let inputActivities = component.activities, !inputActivities.items.isEmpty, inputActivitiesAllowed { + var stringValue = "" + var mergedActivity = inputActivities.items[0].activity + for item in inputActivities.items { + if item.activity != mergedActivity { + mergedActivity = .typingText + break + } + } + if inputActivities.peerId.namespace == Namespaces.Peer.CloudUser || inputActivities.peerId.namespace == Namespaces.Peer.SecretChat { + switch mergedActivity { + case .typingText: + stringValue = component.strings.Conversation_typing + case .uploadingFile: + stringValue = component.strings.Activity_UploadingDocument + case .recordingVoice: + stringValue = component.strings.Activity_RecordingAudio + case .uploadingPhoto: + stringValue = component.strings.Activity_UploadingPhoto + case .uploadingVideo: + stringValue = component.strings.Activity_UploadingVideo + case .playingGame: + stringValue = component.strings.Activity_PlayingGame + case .recordingInstantVideo: + stringValue = component.strings.Activity_RecordingVideoMessage + case .uploadingInstantVideo: + stringValue = component.strings.Activity_UploadingVideoMessage + case .choosingSticker: + stringValue = component.strings.Activity_ChoosingSticker + case let .seeingEmojiInteraction(emoticon): + stringValue = component.strings.Activity_EnjoyingAnimations(emoticon).string + case .speakingInGroupCall, .interactingWithEmoji: + stringValue = "" + } + } else { + if inputActivities.items.count > 1 { + let peerTitle = inputActivities.items[0].peer.compactDisplayTitle + if inputActivities.items.count == 2 { + let secondPeerTitle = inputActivities.items[1].peer.compactDisplayTitle + stringValue = component.strings.Chat_MultipleTypingPair(peerTitle, secondPeerTitle).string + } else { + stringValue = component.strings.Chat_MultipleTypingMore(peerTitle, String(inputActivities.items.count - 1)).string + } + } else if let item = inputActivities.items.first { + stringValue = item.peer.compactDisplayTitle + } + } + let color = component.theme.rootController.navigationBar.accentTextColor + let string = NSAttributedString(string: stringValue, font: subtitleFont, textColor: color) + switch mergedActivity { + case .typingText: + state = .typingText(string, color) + case .recordingVoice: + state = .recordingVoice(string, color) + case .recordingInstantVideo: + state = .recordingVideo(string, color) + case .uploadingFile, .uploadingInstantVideo, .uploadingPhoto, .uploadingVideo: + state = .uploading(string, color) + case .playingGame: + state = .playingGame(string, color) + case .speakingInGroupCall, .interactingWithEmoji: + state = .typingText(string, color) + case .choosingSticker: + state = .choosingSticker(string, color) + case .seeingEmojiInteraction: + state = .choosingSticker(string, color) + } + } else { + switch component.content { + case let .peer(peerView, customTitle, customSubtitle, onlineMemberCount, isScheduledMessages, _, customMessageCount, _): + if let customSubtitle { + let string = NSAttributedString(string: customSubtitle, font: subtitleFont, textColor: component.theme.chat.inputPanel.inputControlColor) + state = .info(string, .generic) + } else if let customMessageCount = customMessageCount, customMessageCount != 0 { + let string = NSAttributedString(string: component.strings.Conversation_Messages(Int32(customMessageCount)), font: subtitleFont, textColor: component.theme.chat.inputPanel.inputControlColor) + state = .info(string, .generic) + } else if let peer = peerView.peer { + let servicePeer = isServicePeer(peer) + if peer.id == component.context.account.peerId || isScheduledMessages || peer.id.isRepliesOrVerificationCodes { + let string = NSAttributedString(string: "", font: subtitleFont, textColor: component.theme.chat.inputPanel.inputControlColor) + state = .info(string, .generic) + } else if let user = peer as? TelegramUser { + if user.isDeleted { + state = .none + } else if servicePeer { + let string = NSAttributedString(string: "", font: subtitleFont, textColor: component.theme.chat.inputPanel.inputControlColor) + state = .info(string, .generic) + } else if user.flags.contains(.isSupport) { + let statusText = component.strings.Bot_GenericSupportStatus + let string = NSAttributedString(string: statusText, font: subtitleFont, textColor: component.theme.chat.inputPanel.inputControlColor) + state = .info(string, .generic) + } else if let _ = user.botInfo { + let statusText: String + if let subscriberCount = user.subscriberCount { + statusText = component.strings.Conversation_StatusBotSubscribers(subscriberCount) + } else { + statusText = component.strings.Bot_GenericBotStatus + } + + let string = NSAttributedString(string: statusText, font: subtitleFont, textColor: component.theme.chat.inputPanel.inputControlColor) + state = .info(string, .generic) + } else if let peer = peerView.peer { + let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970 + let userPresence: TelegramUserPresence + if let presence = peerView.peerPresences[peer.id] as? TelegramUserPresence { + userPresence = presence + self.presenceManager?.reset(presence: EnginePeer.Presence(presence)) + } else { + userPresence = TelegramUserPresence(status: .none, lastActivity: 0) + } + let (string, activity) = stringAndActivityForUserPresence(strings: component.strings, dateTimeFormat: component.dateTimeFormat, presence: EnginePeer.Presence(userPresence), relativeTo: Int32(timestamp)) + let attributedString = NSAttributedString(string: string, font: subtitleFont, textColor: activity ? component.theme.rootController.navigationBar.accentTextColor : component.theme.chat.inputPanel.inputControlColor) + state = .info(attributedString, activity ? .online : .lastSeenTime) + } else { + let string = NSAttributedString(string: "", font: subtitleFont, textColor: component.theme.chat.inputPanel.inputControlColor) + state = .info(string, .generic) + } + } else if let group = peer as? TelegramGroup { + var onlineCount = 0 + if let cachedGroupData = peerView.cachedData as? CachedGroupData, let participants = cachedGroupData.participants { + let timestamp = CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970 + for participant in participants.participants { + if let presence = peerView.peerPresences[participant.peerId] as? TelegramUserPresence { + let relativeStatus = relativeUserPresenceStatus(EnginePeer.Presence(presence), relativeTo: Int32(timestamp)) + switch relativeStatus { + case .online: + onlineCount += 1 + default: + break + } + } + } + } + if onlineCount > 1 { + let string = NSMutableAttributedString() + + string.append(NSAttributedString(string: "\(component.strings.Conversation_StatusMembers(Int32(group.participantCount))), ", font: subtitleFont, textColor: component.theme.chat.inputPanel.inputControlColor)) + string.append(NSAttributedString(string: component.strings.Conversation_StatusOnline(Int32(onlineCount)), font: subtitleFont, textColor: component.theme.chat.inputPanel.inputControlColor)) + state = .info(string, .generic) + } else { + let string = NSAttributedString(string: component.strings.Conversation_StatusMembers(Int32(group.participantCount)), font: subtitleFont, textColor: component.theme.chat.inputPanel.inputControlColor) + state = .info(string, .generic) + } + } else if let channel = peer as? TelegramChannel { + if channel.isForumOrMonoForum, customTitle != nil { + let string = NSAttributedString(string: EnginePeer(peer).displayTitle(strings: component.strings, displayOrder: component.nameDisplayOrder), font: subtitleFont, textColor: component.theme.chat.inputPanel.inputControlColor) + state = .info(string, .generic) + } else if let cachedChannelData = peerView.cachedData as? CachedChannelData, let memberCount = onlineMemberCount.total ?? cachedChannelData.participantsSummary.memberCount { + if memberCount == 0 { + let string: NSAttributedString + if case .group = channel.info { + string = NSAttributedString(string: component.strings.Group_Status, font: subtitleFont, textColor: component.theme.chat.inputPanel.inputControlColor) + } else { + string = NSAttributedString(string: component.strings.Channel_Status, font: subtitleFont, textColor: component.theme.chat.inputPanel.inputControlColor) + } + state = .info(string, .generic) + } else { + if case .group = channel.info, let onlineMemberCount = onlineMemberCount.recent, onlineMemberCount > 1 { + let string = NSMutableAttributedString() + + string.append(NSAttributedString(string: "\(component.strings.Conversation_StatusMembers(Int32(memberCount))), ", font: subtitleFont, textColor: component.theme.chat.inputPanel.inputControlColor)) + string.append(NSAttributedString(string: component.strings.Conversation_StatusOnline(Int32(onlineMemberCount)), font: subtitleFont, textColor: component.theme.chat.inputPanel.inputControlColor)) + state = .info(string, .generic) + } else { + let membersString: String + if case .group = channel.info { + membersString = component.strings.Conversation_StatusMembers(memberCount) + } else { + membersString = component.strings.Conversation_StatusSubscribers(memberCount) + } + let string = NSAttributedString(string: membersString, font: subtitleFont, textColor: component.theme.chat.inputPanel.inputControlColor) + state = .info(string, .generic) + } + } + } else { + switch channel.info { + case .group: + let string = NSAttributedString(string: component.strings.Group_Status, font: subtitleFont, textColor: component.theme.chat.inputPanel.inputControlColor) + state = .info(string, .generic) + case .broadcast: + let string = NSAttributedString(string: component.strings.Channel_Status, font: subtitleFont, textColor: component.theme.chat.inputPanel.inputControlColor) + state = .info(string, .generic) + } + } + } + } + case let .custom(_, subtitle?, _): + let string = NSAttributedString(string: subtitle, font: subtitleFont, textColor: component.theme.chat.inputPanel.inputControlColor) + state = .info(string, .generic) + default: + break + } + + self.accessibilityValue = state.string + } + } + + var rightIconSize: CGSize? + if let titleRightIcon { + let rightIcon: ComponentView + var rightIconTransition = transition + if let current = self.rightIcon { + rightIcon = current + } else { + rightIconTransition = rightIconTransition.withAnimation(.none) + rightIcon = ComponentView() + self.rightIcon = rightIcon + } + rightIconSize = rightIcon.update( + transition: rightIconTransition, + component: AnyComponent(TitleIconComponent( + kind: titleRightIcon, + color: component.theme.chat.inputPanel.inputControlColor + )), + environment: {}, + containerSize: CGSize(width: 100.0, height: 100.0) + ) + } else if let rightIcon = self.rightIcon { + self.rightIcon = nil + if let rightIconView = rightIcon.view { + transition.setScale(view: rightIconView, scale: 0.001) + transition.setAlpha(view: rightIconView, alpha: 0.0, completion: { [weak rightIconView] _ in + rightIconView?.removeFromSuperview() + }) + } + } + + var leftIconSize: CGSize? + if let titleLeftIcon { + let leftIcon: ComponentView + var leftIconTransition = transition + if let current = self.leftIcon { + leftIcon = current + } else { + leftIconTransition = leftIconTransition.withAnimation(.none) + leftIcon = ComponentView() + self.leftIcon = leftIcon + } + leftIconSize = leftIcon.update( + transition: leftIconTransition, + component: AnyComponent(TitleIconComponent( + kind: titleLeftIcon, + color: component.theme.chat.inputPanel.panelControlColor + )), + environment: {}, + containerSize: CGSize(width: 100.0, height: 100.0) + ) + } else if let leftIcon = self.leftIcon { + self.leftIcon = nil + if let leftIconView = leftIcon.view { + transition.setScale(view: leftIconView, scale: 0.001) + transition.setAlpha(view: leftIconView, alpha: 0.0, completion: { [weak leftIconView] _ in + leftIconView?.removeFromSuperview() + }) + } + } + + let mapTitleIcon: (ChatTitleCredibilityIcon) -> EmojiStatusComponent.Content? = { value in + switch value { + case .none: + return nil + case .premium: + return .premium(color: component.theme.list.itemAccentColor) + case .verified: + return .verified(fillColor: component.theme.list.itemCheckColors.fillColor, foregroundColor: component.theme.list.itemCheckColors.foregroundColor, sizeType: .large) + case .fake: + return .text(color: component.theme.chat.message.incoming.scamColor, string: component.strings.Message_FakeAccount.uppercased()) + case .scam: + return .text(color: component.theme.chat.message.incoming.scamColor, string: component.strings.Message_ScamAccount.uppercased()) + case let .emojiStatus(emojiStatus): + return .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: component.theme.list.mediaPlaceholderColor, themeColor: component.theme.list.itemAccentColor, loopMode: .count(2)) + } + } + + var credibilityIconSize: CGSize? + if let titleCredibilityIcon = mapTitleIcon(titleCredibilityIcon) { + let credibilityIcon: ComponentView + if let current = self.credibilityIcon { + credibilityIcon = current + } else { + credibilityIcon = ComponentView() + self.credibilityIcon = credibilityIcon + } + credibilityIconSize = credibilityIcon.update( + transition: .immediate, + component: AnyComponent(EmojiStatusComponent( + context: component.context, + animationCache: component.context.animationCache, + animationRenderer: component.context.animationRenderer, + content: titleCredibilityIcon, + isVisibleForAnimations: true, + action: nil + )), + environment: {}, + containerSize: CGSize(width: 20.0, height: 20.0) + ) + } else if let credibilityIcon = self.credibilityIcon { + self.credibilityIcon = nil + if let credibilityIconView = credibilityIcon.view { + transition.setScale(view: credibilityIconView, scale: 0.001) + transition.setAlpha(view: credibilityIconView, alpha: 0.0, completion: { [weak credibilityIconView] _ in + credibilityIconView?.removeFromSuperview() + }) + } + } + + var statusIconSize: CGSize? + if let titleStatusIcon = mapTitleIcon(titleStatusIcon) { + let statusIcon: ComponentView + if let current = self.statusIcon { + statusIcon = current + } else { + statusIcon = ComponentView() + self.statusIcon = statusIcon + } + statusIconSize = statusIcon.update( + transition: .immediate, + component: AnyComponent(EmojiStatusComponent( + context: component.context, + animationCache: component.context.animationCache, + animationRenderer: component.context.animationRenderer, + content: titleStatusIcon, + isVisibleForAnimations: true, + action: nil + )), + environment: {}, + containerSize: CGSize(width: 20.0, height: 20.0) + ) + } else if let statusIcon = self.statusIcon { + self.statusIcon = nil + if let statusIconView = statusIcon.view { + transition.setScale(view: statusIconView, scale: 0.001) + transition.setAlpha(view: statusIconView, alpha: 0.0, completion: { [weak statusIconView] _ in + statusIconView?.removeFromSuperview() + }) + } + } + + var verifiedIconSize: CGSize? + if let titleVerifiedIcon = mapTitleIcon(titleVerifiedIcon) { + let verifiedIcon: ComponentView + if let current = self.verifiedIcon { + verifiedIcon = current + } else { + verifiedIcon = ComponentView() + self.verifiedIcon = verifiedIcon + } + verifiedIconSize = verifiedIcon.update( + transition: .immediate, + component: AnyComponent(EmojiStatusComponent( + context: component.context, + animationCache: component.context.animationCache, + animationRenderer: component.context.animationRenderer, + content: titleVerifiedIcon, + isVisibleForAnimations: true, + action: nil + )), + environment: {}, + containerSize: CGSize(width: 20.0, height: 20.0) + ) + } else if let verifiedIcon = self.verifiedIcon { + self.verifiedIcon = nil + if let verifiedIconView = verifiedIcon.view { + transition.setScale(view: verifiedIconView, scale: 0.001) + transition.setAlpha(view: verifiedIconView, alpha: 0.0, completion: { [weak verifiedIconView] _ in + verifiedIconView?.removeFromSuperview() + }) + } + } + + let subtitleNode: ChatTitleActivityNode + if let current = self.subtitleNode { + subtitleNode = current + } else { + subtitleNode = ChatTitleActivityNode() + self.subtitleNode = subtitleNode + subtitleNode.isUserInteractionEnabled = false + self.contentContainer.addSubview(subtitleNode.view) + } + + var titleLeftIconsWidth: CGFloat = 0.0 + if let leftIconSize { + titleLeftIconsWidth += leftIconSize.width + leftTitleIconSpacing + } + if let verifiedIconSize { + titleLeftIconsWidth += verifiedIconSize.width + statusIconsSpacing + } + + var titleRightIconsWidth: CGFloat = 0.0 + if let rightIconSize { + titleRightIconsWidth += rightIconSize.width + rightTitleIconSpacing + } + if let credibilityIconSize { + titleRightIconsWidth += credibilityIconSize.width + statusIconsSpacing + } + if let statusIconSize { + titleRightIconsWidth += statusIconSize.width + statusIconsSpacing + } + + let maxTitleWidth = availableSize.width - titleLeftIconsWidth - titleRightIconsWidth - containerSideInset * 2.0 + + let titleSize = self.title.update( + transition: transition, + component: AnyComponent(AnimatedTextComponent( + font: Font.semibold(17.0), + color: component.theme.chat.inputPanel.panelControlColor, + items: titleSegments, + noDelay: false, + animateScale: true, + animateSlide: true, + blur: true + )), + environment: {}, + containerSize: CGSize(width: maxTitleWidth, height: 100.0) + ) + + let _ = subtitleNode.transitionToState(state, animation: transition.animation.isImmediate ? .none : .slide) + let subtitleSize = subtitleNode.updateLayout(CGSize(width: availableSize.width - containerSideInset * 2.0, height: 100.0), alignment: .center) + + var minSubtitleWidth: CGFloat? + let activityMeasureSubtitleNode: ChatTitleActivityNode + if let current = self.activityMeasureSubtitleNode { + activityMeasureSubtitleNode = current + } else { + activityMeasureSubtitleNode = ChatTitleActivityNode() + self.activityMeasureSubtitleNode = activityMeasureSubtitleNode + } + let measureTypingTextString = NSAttributedString(string: component.strings.Conversation_typing, font: subtitleFont, textColor: .black) + let _ = activityMeasureSubtitleNode.transitionToState(.typingText(measureTypingTextString, .black), animation: .none) + let activityMeasureSubtitleSize = activityMeasureSubtitleNode.updateLayout(CGSize(width: availableSize.width - containerSideInset * 2.0, height: 100.0), alignment: .center) + minSubtitleWidth = activityMeasureSubtitleSize.width + + var contentSize = titleSize + contentSize.width += titleLeftIconsWidth + titleRightIconsWidth + contentSize.width = max(contentSize.width, subtitleSize.width) + if let minSubtitleWidth { + contentSize.width = max(contentSize.width, minSubtitleWidth) + } + contentSize.height += subtitleSize.height + + let containerSize = CGSize(width: contentSize.width + containerSideInset * 2.0, height: 44.0) + let containerFrame = CGRect(origin: CGPoint(x: 0.0, y: floorToScreenPixels((availableSize.height - containerSize.height) * 0.5)), size: containerSize) + + let titleFrame = CGRect(origin: CGPoint(x: titleLeftIconsWidth + floor((containerFrame.width - titleSize.width - titleLeftIconsWidth - titleRightIconsWidth) * 0.5), y: floor((containerFrame.height - contentSize.height) * 0.5)), size: titleSize) + if let titleView = self.title.view { + if titleView.superview == nil { + titleView.isUserInteractionEnabled = false + self.contentContainer.addSubview(titleView) + } + transition.setFrame(view: titleView, frame: titleFrame) + } + + let subtitleFrame = CGRect(origin: CGPoint(x: floor((containerFrame.width - subtitleSize.width) * 0.5), y: titleFrame.maxY), size: subtitleSize) + // Internally, the status view has zero width + transition.setFrame(view: subtitleNode.view, frame: CGRect(origin: CGPoint(x: subtitleFrame.midX, y: subtitleFrame.minY), size: CGSize(width: 0.0, height: subtitleFrame.height))) + + var nextLeftIconX: CGFloat = titleFrame.minX + + if let leftIconSize, let leftIconView = self.leftIcon?.view { + let leftIconFrame = CGRect(origin: CGPoint(x: nextLeftIconX - leftTitleIconSpacing - leftIconSize.width, y: titleFrame.minY + leftTitleIconSpacing), size: leftIconSize) + if leftIconView.superview == nil { + leftIconView.isUserInteractionEnabled = false + self.contentContainer.addSubview(leftIconView) + leftIconView.frame = leftIconFrame + ComponentTransition.immediate.setScale(view: leftIconView, scale: 0.001) + leftIconView.alpha = 0.0 + } + transition.setPosition(view: leftIconView, position: leftIconFrame.center) + transition.setBounds(view: leftIconView, bounds: CGRect(origin: CGPoint(), size: leftIconFrame.size)) + transition.setAlpha(view: leftIconView, alpha: 1.0) + transition.setScale(view: leftIconView, scale: 1.0) + } + + if let verifiedIconSize, let verifiedIconView = self.verifiedIcon?.view { + let verifiedIconFrame = CGRect(origin: CGPoint(x: nextLeftIconX - statusIconsSpacing - verifiedIconSize.width, y: titleFrame.minY), size: verifiedIconSize) + if verifiedIconView.superview == nil { + verifiedIconView.isUserInteractionEnabled = false + self.contentContainer.addSubview(verifiedIconView) + verifiedIconView.frame = verifiedIconFrame + ComponentTransition.immediate.setScale(view: verifiedIconView, scale: 0.001) + verifiedIconView.alpha = 0.0 + } + transition.setPosition(view: verifiedIconView, position: verifiedIconFrame.center) + transition.setBounds(view: verifiedIconView, bounds: CGRect(origin: CGPoint(), size: verifiedIconFrame.size)) + transition.setAlpha(view: verifiedIconView, alpha: 1.0) + transition.setScale(view: verifiedIconView, scale: 1.0) + nextLeftIconX -= statusIconsSpacing + verifiedIconSize.width + } + + var nextRightIconX: CGFloat = titleFrame.maxX + + if let credibilityIconSize, let credibilityIconView = self.credibilityIcon?.view { + let credibilityIconFrame = CGRect(origin: CGPoint(x: nextRightIconX + statusIconsSpacing, y: titleFrame.minY), size: credibilityIconSize) + if credibilityIconView.superview == nil { + credibilityIconView.isUserInteractionEnabled = false + self.contentContainer.addSubview(credibilityIconView) + credibilityIconView.frame = credibilityIconFrame + ComponentTransition.immediate.setScale(view: credibilityIconView, scale: 0.001) + credibilityIconView.alpha = 0.0 + } + transition.setPosition(view: credibilityIconView, position: credibilityIconFrame.center) + transition.setBounds(view: credibilityIconView, bounds: CGRect(origin: CGPoint(), size: credibilityIconFrame.size)) + transition.setAlpha(view: credibilityIconView, alpha: 1.0) + transition.setScale(view: credibilityIconView, scale: 1.0) + nextRightIconX += statusIconsSpacing + credibilityIconSize.width + } + + if let statusIconSize, let statusIconView = self.statusIcon?.view { + let statusIconFrame = CGRect(origin: CGPoint(x: nextRightIconX + statusIconsSpacing, y: titleFrame.minY), size: statusIconSize) + if statusIconView.superview == nil { + statusIconView.isUserInteractionEnabled = false + self.contentContainer.addSubview(statusIconView) + statusIconView.frame = statusIconFrame + ComponentTransition.immediate.setScale(view: statusIconView, scale: 0.001) + statusIconView.alpha = 0.0 + } + transition.setPosition(view: statusIconView, position: statusIconFrame.center) + transition.setBounds(view: statusIconView, bounds: CGRect(origin: CGPoint(), size: statusIconFrame.size)) + transition.setAlpha(view: statusIconView, alpha: 1.0) + transition.setScale(view: statusIconView, scale: 1.0) + nextRightIconX += statusIconsSpacing + statusIconSize.width + } + + if let rightIconSize, let rightIconView = self.rightIcon?.view { + let rightIconFrame = CGRect(origin: CGPoint(x: nextRightIconX + rightTitleIconSpacing, y: titleFrame.minY + 5.0), size: rightIconSize) + if rightIconView.superview == nil { + rightIconView.isUserInteractionEnabled = false + self.contentContainer.addSubview(rightIconView) + rightIconView.frame = rightIconFrame + ComponentTransition.immediate.setScale(view: rightIconView, scale: 0.001) + rightIconView.alpha = 0.0 + } + transition.setPosition(view: rightIconView, position: rightIconFrame.center) + transition.setBounds(view: rightIconView, bounds: CGRect(origin: CGPoint(), size: rightIconFrame.size)) + transition.setAlpha(view: rightIconView, alpha: 1.0) + transition.setScale(view: rightIconView, scale: 1.0) + nextRightIconX += rightTitleIconSpacing + rightIconSize.width + } + + if component.displayBackground { + let backgroundView: GlassBackgroundView + if let current = self.backgroundView { + backgroundView = current + } else { + backgroundView = GlassBackgroundView() + self.backgroundView = backgroundView + self.addSubview(backgroundView) + backgroundView.contentView.addSubview(self.contentContainer) + } + transition.setFrame(view: backgroundView, frame: containerFrame) + backgroundView.update(size: containerFrame.size, cornerRadius: containerFrame.height * 0.5, isDark: component.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: component.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: isEnabled, transition: transition) + transition.setFrame(view: self.contentContainer, frame: CGRect(origin: CGPoint(), size: containerFrame.size)) + self.contentContainer.layer.cornerRadius = containerFrame.height * 0.5 + } else { + if let backgroundView = self.backgroundView { + self.backgroundView = nil + backgroundView.removeFromSuperview() + } + if self.contentContainer.superview !== self { + self.addSubview(self.contentContainer) + } + transition.setFrame(view: self.contentContainer, frame: containerFrame) + self.contentContainer.layer.cornerRadius = 0.0 + } + + return CGSize(width: containerSize.width, height: availableSize.height) + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift b/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift index d1226299..1b2e41fd 100644 --- a/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift +++ b/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift @@ -14,7 +14,6 @@ import PeerPresenceStatusManager import ChatTitleActivityNode import LocalizedPeerData import PhoneNumberFormat -import ChatTitleActivityNode import AnimatedCountLabelNode import AccountContext import ComponentFlow @@ -22,6 +21,8 @@ import EmojiStatusComponent import AnimationCache import MultiAnimationRenderer import ComponentDisplayAdapters +import GlassBackgroundComponent +import AnimatedTextComponent private let titleFont = Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers]) private let subtitleFont = Font.regular(13.0) @@ -92,9 +93,26 @@ public enum ChatTitleContent: Equatable { case replies } + public struct TitleTextItem: Equatable { + public enum Content: Equatable { + case text(String) + case number(Int, minDigits: Int) + } + + public var id: AnyHashable + public var isUnbreakable: Bool + public var content: Content + + public init(id: AnyHashable, isUnbreakable: Bool = true, content: Content) { + self.id = id + self.isUnbreakable = isUnbreakable + self.content = content + } + } + case peer(peerView: PeerData, customTitle: String?, customSubtitle: String?, onlineMemberCount: (total: Int32?, recent: Int32?), isScheduledMessages: Bool, isMuted: Bool?, customMessageCount: Int?, isEnabled: Bool) case replyThread(type: ReplyThreadType, count: Int) - case custom(String, String?, Bool) + case custom(title: [TitleTextItem], subtitle: String?, isEnabled: Bool) public static func ==(lhs: ChatTitleContent, rhs: ChatTitleContent) -> Bool { switch lhs { @@ -144,13 +162,13 @@ public enum ChatTitleContent: Equatable { } } -private enum ChatTitleIcon { +enum ChatTitleIcon { case none case lock case mute } -private enum ChatTitleCredibilityIcon: Equatable { +enum ChatTitleCredibilityIcon: Equatable { case none case fake case scam @@ -170,7 +188,6 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { private let context: AccountContext private var theme: PresentationTheme - private var hasEmbeddedTitleContent: Bool = false private var strings: PresentationStrings private var dateTimeFormat: PresentationDateTimeFormat private var nameDisplayOrder: PresentationPersonNameOrder @@ -178,6 +195,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { private let animationRenderer: MultiAnimationRenderer private let contentContainer: ASDisplayNode + private let backgroundView: GlassBackgroundView public let titleContainerView: PortalSourceView public let titleTextNode: ImmediateAnimatedCountLabelNode public let titleLeftIconNode: ASImageNode @@ -192,7 +210,9 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { public var disableAnimations: Bool = false var manualLayout: Bool = false - private var validLayout: (CGSize, CGRect)? + private var validLayout: CGSize? + + public var requestUpdate: ((ContainedViewLayoutTransition) -> Void)? private var titleLeftIcon: ChatTitleIcon = .none private var titleRightIcon: ChatTitleIcon = .none @@ -204,7 +224,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { private var pointerInteraction: PointerInteraction? - public var inputActivities: (PeerId, [(Peer, PeerInputActivity)])? { + public var inputActivities: ChatTitleComponent.Activities? { didSet { let _ = self.updateStatus() } @@ -239,7 +259,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { public var titleContent: ChatTitleContent? { didSet { if let titleContent = self.titleContent { - let titleTheme = self.hasEmbeddedTitleContent ? defaultDarkPresentationTheme : self.theme + let titleTheme = self.theme var segments: [AnimatedCountLabelNode.Segment] = [] var titleLeftIcon: ChatTitleIcon = .none @@ -394,8 +414,17 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { } isEnabled = false - case let .custom(text, _, enabled): - segments = [.text(0, NSAttributedString(string: text, font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor))] + case let .custom(textItems, _, enabled): + var nextId = -1 + segments = textItems.map { item -> AnimatedCountLabelNode.Segment in + nextId += 1 + switch item.content { + case let .number(value, _): + return .number(nextId, NSAttributedString(string: "\(value)", font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor)) + case let .text(text): + return .text(nextId, NSAttributedString(string: text, font: titleFont, textColor: titleTheme.rootController.navigationBar.primaryTextColor)) + } + } isEnabled = enabled } @@ -461,8 +490,8 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { if !self.updateStatus(enableAnimation: enableAnimation) { if updated { - if !self.manualLayout, let (size, clearBounds) = self.validLayout { - let _ = self.updateLayout(size: size, clearBounds: clearBounds, transition: (self.disableAnimations || !enableAnimation) ? .immediate : .animated(duration: 0.2, curve: .easeInOut)) + if !self.manualLayout, let size = self.validLayout { + let _ = self.updateLayout(availableSize: size, transition: (self.disableAnimations || !enableAnimation) ? .immediate : .animated(duration: 0.2, curve: .easeInOut)) } } } @@ -487,7 +516,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { } } - let titleTheme = self.hasEmbeddedTitleContent ? defaultDarkPresentationTheme : self.theme + let titleTheme = self.theme var state = ChatTitleActivityNodeState.none switch self.networkState { @@ -505,51 +534,51 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { } state = .info(NSAttributedString(string: infoText, font: subtitleFont, textColor: titleTheme.rootController.navigationBar.secondaryTextColor), .generic) case .online: - if let (peerId, inputActivities) = self.inputActivities, !inputActivities.isEmpty, inputActivitiesAllowed { + if let inputActivities = self.inputActivities, !inputActivities.items.isEmpty, inputActivitiesAllowed { var stringValue = "" - var mergedActivity = inputActivities[0].1 - for (_, activity) in inputActivities { - if activity != mergedActivity { + var mergedActivity = inputActivities.items[0].activity + for item in inputActivities.items { + if item.activity != mergedActivity { mergedActivity = .typingText break } } - if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.SecretChat { + if inputActivities.peerId.namespace == Namespaces.Peer.CloudUser || inputActivities.peerId.namespace == Namespaces.Peer.SecretChat { switch mergedActivity { - case .typingText: - stringValue = strings.Conversation_typing - case .uploadingFile: - stringValue = strings.Activity_UploadingDocument - case .recordingVoice: - stringValue = strings.Activity_RecordingAudio - case .uploadingPhoto: - stringValue = strings.Activity_UploadingPhoto - case .uploadingVideo: - stringValue = strings.Activity_UploadingVideo - case .playingGame: - stringValue = strings.Activity_PlayingGame - case .recordingInstantVideo: - stringValue = strings.Activity_RecordingVideoMessage - case .uploadingInstantVideo: - stringValue = strings.Activity_UploadingVideoMessage - case .choosingSticker: - stringValue = strings.Activity_ChoosingSticker - case let .seeingEmojiInteraction(emoticon): - stringValue = strings.Activity_EnjoyingAnimations(emoticon).string - case .speakingInGroupCall, .interactingWithEmoji: - stringValue = "" + case .typingText: + stringValue = strings.Conversation_typing + case .uploadingFile: + stringValue = strings.Activity_UploadingDocument + case .recordingVoice: + stringValue = strings.Activity_RecordingAudio + case .uploadingPhoto: + stringValue = strings.Activity_UploadingPhoto + case .uploadingVideo: + stringValue = strings.Activity_UploadingVideo + case .playingGame: + stringValue = strings.Activity_PlayingGame + case .recordingInstantVideo: + stringValue = strings.Activity_RecordingVideoMessage + case .uploadingInstantVideo: + stringValue = strings.Activity_UploadingVideoMessage + case .choosingSticker: + stringValue = strings.Activity_ChoosingSticker + case let .seeingEmojiInteraction(emoticon): + stringValue = strings.Activity_EnjoyingAnimations(emoticon).string + case .speakingInGroupCall, .interactingWithEmoji: + stringValue = "" } } else { - if inputActivities.count > 1 { - let peerTitle = EnginePeer(inputActivities[0].0).compactDisplayTitle - if inputActivities.count == 2 { - let secondPeerTitle = EnginePeer(inputActivities[1].0).compactDisplayTitle - stringValue = strings.Chat_MultipleTypingPair(peerTitle, secondPeerTitle).string + if inputActivities.items.count > 1 { + let peerTitle = inputActivities.items[0].peer.compactDisplayTitle + if inputActivities.items.count == 2 { + let secondPeerTitle = inputActivities.items[1].peer.compactDisplayTitle + stringValue = self.strings.Chat_MultipleTypingPair(peerTitle, secondPeerTitle).string } else { - stringValue = strings.Chat_MultipleTypingMore(peerTitle, String(inputActivities.count - 1)).string + stringValue = self.strings.Chat_MultipleTypingMore(peerTitle, String(inputActivities.items.count - 1)).string } - } else if let (peer, _) = inputActivities.first { - stringValue = EnginePeer(peer).compactDisplayTitle + } else if let item = inputActivities.items.first { + stringValue = item.peer.compactDisplayTitle } } let color = titleTheme.rootController.navigationBar.accentTextColor @@ -719,8 +748,8 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { } if self.activityNode.transitionToState(state, animation: enableAnimation ? .slide : .none) { - if !self.manualLayout, let (size, clearBounds) = self.validLayout { - let _ = self.updateLayout(size: size, clearBounds: clearBounds, transition: enableAnimation ? .animated(duration: 0.3, curve: .spring) : .immediate) + if !self.manualLayout, let size = self.validLayout { + let _ = self.updateLayout(availableSize: size, transition: enableAnimation ? .animated(duration: 0.3, curve: .spring) : .immediate) } return true } else { @@ -739,6 +768,8 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { self.contentContainer = ASDisplayNode() + self.backgroundView = GlassBackgroundView() + self.titleContainerView = PortalSourceView() self.titleTextNode = ImmediateAnimatedCountLabelNode() @@ -770,6 +801,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { self.accessibilityTraits = .header self.addSubnode(self.contentContainer) + self.contentContainer.view.addSubview(self.backgroundView) self.titleContainerView.addSubnode(self.titleTextNode) self.contentContainer.view.addSubview(self.titleContainerView) self.contentContainer.addSubnode(self.activityNode) @@ -813,15 +845,14 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { override public func layoutSubviews() { super.layoutSubviews() - if !self.manualLayout, let (size, clearBounds) = self.validLayout { - let _ = self.updateLayout(size: size, clearBounds: clearBounds, transition: .immediate) + if !self.manualLayout, let size = self.validLayout { + let _ = self.updateLayout(availableSize: size, transition: .immediate) } } - public func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings, hasEmbeddedTitleContent: Bool) { - if self.theme !== theme || self.strings !== strings || self.hasEmbeddedTitleContent != hasEmbeddedTitleContent { + public func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) { + if self.theme !== theme || self.strings !== strings { self.theme = theme - self.hasEmbeddedTitleContent = hasEmbeddedTitleContent self.strings = strings let titleContent = self.titleContent @@ -830,17 +861,19 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { self.titleContent = titleContent let _ = self.updateStatus() - if !self.manualLayout, let (size, clearBounds) = self.validLayout { - let _ = self.updateLayout(size: size, clearBounds: clearBounds, transition: .immediate) + if !self.manualLayout, let size = self.validLayout { + let _ = self.updateLayout(availableSize: size, transition: .immediate) } } } - public func updateLayout(size: CGSize, clearBounds: CGRect, transition: ContainedViewLayoutTransition) -> CGRect { - self.validLayout = (size, clearBounds) + public func updateLayout(availableSize: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { + let size = availableSize - self.button.frame = clearBounds - self.contentContainer.frame = clearBounds + self.validLayout = size + + self.button.frame = CGRect(origin: CGPoint(), size: size) + self.contentContainer.frame = CGRect(origin: CGPoint(), size: size) var leftIconWidth: CGFloat = 0.0 var rightIconWidth: CGFloat = 0.0 @@ -986,108 +1019,87 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { } let statusSpacing: CGFloat = 3.0 - let titleSideInset: CGFloat = 6.0 + let titleSideInset: CGFloat = 12.0 + 8.0 var titleFrame: CGRect - if size.height > 40.0 { - var titleInsets: UIEdgeInsets = .zero - if case .emojiStatus = self.titleVerifiedIcon, verifiedIconWidth > 0.0 { - titleInsets.left = verifiedIconWidth - } - - var titleSize = self.titleTextNode.updateLayout(size: CGSize(width: clearBounds.width - leftIconWidth - credibilityIconWidth - verifiedIconWidth - statusIconWidth - rightIconWidth - titleSideInset * 2.0, height: size.height), insets: titleInsets, animated: titleTransition.isAnimated) - titleSize.width += credibilityIconWidth - titleSize.width += verifiedIconWidth - if statusIconWidth > 0.0 { - titleSize.width += statusIconWidth - if credibilityIconWidth > 0.0 { - titleSize.width += statusSpacing - } - } - - let activitySize = self.activityNode.updateLayout(CGSize(width: clearBounds.size.width - titleSideInset * 2.0, height: clearBounds.size.height), alignment: .center) - let titleInfoSpacing: CGFloat = 0.0 - - if activitySize.height.isZero { - titleFrame = CGRect(origin: CGPoint(x: floor((clearBounds.width - titleSize.width) / 2.0), y: floor((size.height - titleSize.height) / 2.0)), size: titleSize) - if titleFrame.size.width < size.width { - titleFrame.origin.x = -clearBounds.minX + floor((size.width - titleFrame.width) / 2.0) - } - titleTransition.updateFrameAdditive(view: self.titleContainerView, frame: titleFrame) - titleTransition.updateFrameAdditive(node: self.titleTextNode, frame: CGRect(origin: CGPoint(), size: titleFrame.size)) - } else { - let combinedHeight = titleSize.height + activitySize.height + titleInfoSpacing - - titleFrame = CGRect(origin: CGPoint(x: floor((clearBounds.width - titleSize.width) / 2.0), y: floor((size.height - combinedHeight) / 2.0)), size: titleSize) - if titleFrame.size.width < size.width { - titleFrame.origin.x = -clearBounds.minX + floor((size.width - titleFrame.width) / 2.0) - } - titleFrame.origin.x = max(titleFrame.origin.x, clearBounds.minX + leftIconWidth) - titleTransition.updateFrameAdditive(view: self.titleContainerView, frame: titleFrame) - titleTransition.updateFrameAdditive(node: self.titleTextNode, frame: CGRect(origin: CGPoint(), size: titleFrame.size)) - - var activityFrame = CGRect(origin: CGPoint(x: floor((clearBounds.width - activitySize.width) / 2.0), y: floor((size.height - combinedHeight) / 2.0) + titleSize.height + titleInfoSpacing), size: activitySize) - if activitySize.width < size.width { - activityFrame.origin.x = -clearBounds.minX + floor((size.width - activityFrame.width) / 2.0) - } - titleTransition.updateFrameAdditiveToCenter(node: self.activityNode, frame: activityFrame) - } - - if let image = self.titleLeftIconNode.image { - titleTransition.updateFrame(node: self.titleLeftIconNode, frame: CGRect(origin: CGPoint(x: -image.size.width - 3.0 - UIScreenPixel, y: 4.0), size: image.size)) - } - - var nextIconX: CGFloat = titleFrame.width - - titleTransition.updateFrame(view: self.titleVerifiedIconView, frame: CGRect(origin: CGPoint(x: 0.0, y: floor((titleFrame.height - titleVerifiedSize.height) / 2.0)), size: titleVerifiedSize)) - - self.titleCredibilityIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleCredibilitySize.width, y: floor((titleFrame.height - titleCredibilitySize.height) / 2.0)), size: titleCredibilitySize) - nextIconX -= titleCredibilitySize.width - if credibilityIconWidth > 0.0 { - nextIconX -= statusSpacing - } - - self.titleStatusIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleStatusSize.width, y: floor((titleFrame.height - titleStatusSize.height) / 2.0)), size: titleStatusSize) - nextIconX -= titleStatusSize.width - if let image = self.titleRightIconNode.image { - self.titleRightIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.width + 3.0 + UIScreenPixel, y: 6.0), size: image.size) + var titleInsets: UIEdgeInsets = .zero + if case .emojiStatus = self.titleVerifiedIcon, verifiedIconWidth > 0.0 { + titleInsets.left = verifiedIconWidth + } + + var titleSize = self.titleTextNode.updateLayout(size: CGSize(width: size.width - leftIconWidth - credibilityIconWidth - verifiedIconWidth - statusIconWidth - rightIconWidth - titleSideInset * 2.0, height: size.height), insets: titleInsets, animated: titleTransition.isAnimated) + titleSize.width += credibilityIconWidth + titleSize.width += verifiedIconWidth + if statusIconWidth > 0.0 { + titleSize.width += statusIconWidth + if credibilityIconWidth > 0.0 { + titleSize.width += statusSpacing } + } + + let activitySize = self.activityNode.updateLayout(CGSize(width: size.width - titleSideInset * 2.0, height: size.height), alignment: .center) + let titleInfoSpacing: CGFloat = 0.0 + + var activityFrame = CGRect() + + if activitySize.height.isZero { + titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: floor((size.height - titleSize.height) / 2.0)), size: titleSize) + if titleFrame.size.width < size.width { + titleFrame.origin.x = floor((size.width - titleFrame.width) / 2.0) + } + titleTransition.updateFrameAdditive(view: self.titleContainerView, frame: titleFrame) + titleTransition.updateFrameAdditive(node: self.titleTextNode, frame: CGRect(origin: CGPoint(), size: titleFrame.size)) } else { - let titleSize = self.titleTextNode.updateLayout(size: CGSize(width: floor(clearBounds.width / 2.0 - leftIconWidth - credibilityIconWidth - verifiedIconWidth - statusIconWidth - rightIconWidth - titleSideInset * 2.0), height: size.height), animated: titleTransition.isAnimated) - let activitySize = self.activityNode.updateLayout(CGSize(width: floor(clearBounds.width / 2.0), height: size.height), alignment: .center) + let combinedHeight = titleSize.height + activitySize.height + titleInfoSpacing - let titleInfoSpacing: CGFloat = 8.0 - let combinedWidth = titleSize.width + leftIconWidth + credibilityIconWidth + verifiedIconWidth + statusIconWidth + rightIconWidth + activitySize.width + titleInfoSpacing + let contentWidth = max(titleSize.width + rightIconWidth, activitySize.width) + var contentX = floor((size.width - contentWidth) / 2.0) + contentX = max(contentX, 20.0) - titleFrame = CGRect(origin: CGPoint(x: leftIconWidth + floor((clearBounds.width - combinedWidth) / 2.0), y: floor((size.height - titleSize.height) / 2.0)), size: titleSize) + titleFrame = CGRect(origin: CGPoint(x: contentX + floor((contentWidth - titleSize.width) / 2.0), y: floor((size.height - combinedHeight) / 2.0)), size: titleSize) - titleTransition.updateFrameAdditiveToCenter(view: self.titleContainerView, frame: titleFrame) - titleTransition.updateFrameAdditiveToCenter(node: self.titleTextNode, frame: CGRect(origin: CGPoint(), size: titleFrame.size)) + titleFrame.origin.x = max(titleFrame.origin.x, leftIconWidth) + titleTransition.updateFrameAdditive(view: self.titleContainerView, frame: titleFrame) + titleTransition.updateFrameAdditive(node: self.titleTextNode, frame: CGRect(origin: CGPoint(), size: titleFrame.size)) - titleTransition.updateFrameAdditiveToCenter(node: self.activityNode, frame: CGRect(origin: CGPoint(x: floor((clearBounds.width - combinedWidth) / 2.0 + titleSize.width + leftIconWidth + credibilityIconWidth + verifiedIconWidth + statusIconWidth + rightIconWidth + titleInfoSpacing), y: floor((size.height - activitySize.height) / 2.0)), size: activitySize)) - - if let image = self.titleLeftIconNode.image { - self.titleLeftIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.minX, y: titleFrame.minY + 4.0), size: image.size) - } - - var nextIconX: CGFloat = titleFrame.maxX - - self.titleVerifiedIconView.frame = CGRect(origin: CGPoint(x: 0.0, y: floor((titleFrame.height - titleVerifiedSize.height) / 2.0)), size: titleVerifiedSize) - - self.titleCredibilityIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleCredibilitySize.width, y: floor((titleFrame.height - titleCredibilitySize.height) / 2.0)), size: titleCredibilitySize) - nextIconX -= titleCredibilitySize.width - - titleTransition.updateFrame(view: self.titleStatusIconView, frame: CGRect(origin: CGPoint(x: nextIconX - titleStatusSize.width, y: floor((titleFrame.height - titleStatusSize.height) / 2.0)), size: titleStatusSize)) - nextIconX -= titleStatusSize.width - - if let image = self.titleRightIconNode.image { - titleTransition.updateFrame(node: self.titleRightIconNode, frame: CGRect(origin: CGPoint(x: titleFrame.maxX - image.size.width, y: titleFrame.minY + 6.0), size: image.size)) - } + activityFrame = CGRect(origin: CGPoint(x: titleFrame.minX + floor((titleFrame.width - activitySize.width) / 2.0), y: floor((size.height - combinedHeight) / 2.0) + titleSize.height + titleInfoSpacing), size: activitySize) + titleTransition.updateFrameAdditiveToCenter(node: self.activityNode, frame: activityFrame.offsetBy(dx: activitySize.width * 0.5, dy: 0.0)) + } + + if let image = self.titleLeftIconNode.image { + titleTransition.updateFrame(node: self.titleLeftIconNode, frame: CGRect(origin: CGPoint(x: -image.size.width - 3.0 - UIScreenPixel, y: 4.0), size: image.size)) + } + + var nextIconX: CGFloat = titleFrame.width + + titleTransition.updateFrame(view: self.titleVerifiedIconView, frame: CGRect(origin: CGPoint(x: 0.0, y: floor((titleFrame.height - titleVerifiedSize.height) / 2.0)), size: titleVerifiedSize)) + + self.titleCredibilityIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleCredibilitySize.width, y: floor((titleFrame.height - titleCredibilitySize.height) / 2.0)), size: titleCredibilitySize) + nextIconX -= titleCredibilitySize.width + if credibilityIconWidth > 0.0 { + nextIconX -= statusSpacing + } + + self.titleStatusIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleStatusSize.width, y: floor((titleFrame.height - titleStatusSize.height) / 2.0)), size: titleStatusSize) + nextIconX -= titleStatusSize.width + + if let image = self.titleRightIconNode.image { + self.titleRightIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.width + 3.0 + UIScreenPixel, y: 6.0), size: image.size) } self.pointerInteraction = PointerInteraction(view: self, style: .rectangle(CGSize(width: titleFrame.width + 16.0, height: 40.0))) - return titleFrame + var backgroundFrame = CGRect(origin: CGPoint(x: titleFrame.minX, y: 6.0), size: CGSize(width: titleFrame.width, height: 44.0)) + if !activityFrame.isEmpty { + backgroundFrame.origin.x = min(backgroundFrame.minX, activityFrame.minX) + backgroundFrame.size.width = max(backgroundFrame.maxX, activityFrame.maxX) - backgroundFrame.minX + } + backgroundFrame = backgroundFrame.insetBy(dx: -12.0, dy: 0.0) + let componentTransition = ComponentTransition(transition) + componentTransition.setFrame(view: self.backgroundView, frame: backgroundFrame) + self.backgroundView.update(size: backgroundFrame.size, cornerRadius: backgroundFrame.height * 0.5, isDark: self.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: false, transition: componentTransition) + + return availableSize } @objc private func buttonPressed() { @@ -1162,122 +1174,3 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { snapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: -offset.x, y: -offset.y), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true) } } - -public final class ChatTitleComponent: Component { - public let context: AccountContext - public let theme: PresentationTheme - public let strings: PresentationStrings - public let dateTimeFormat: PresentationDateTimeFormat - public let nameDisplayOrder: PresentationPersonNameOrder - public let content: ChatTitleContent - public let tapped: () -> Void - public let longTapped: () -> Void - - public init( - context: AccountContext, - theme: PresentationTheme, - strings: PresentationStrings, - dateTimeFormat: PresentationDateTimeFormat, - nameDisplayOrder: PresentationPersonNameOrder, - content: ChatTitleContent, - tapped: @escaping () -> Void, - longTapped: @escaping () -> Void - ) { - self.context = context - self.theme = theme - self.strings = strings - self.dateTimeFormat = dateTimeFormat - self.nameDisplayOrder = nameDisplayOrder - self.content = content - self.tapped = tapped - self.longTapped = longTapped - } - - public static func ==(lhs: ChatTitleComponent, rhs: ChatTitleComponent) -> Bool { - if lhs.context !== rhs.context { - return false - } - if lhs.theme !== rhs.theme { - return false - } - if lhs.strings !== rhs.strings { - return false - } - if lhs.dateTimeFormat != rhs.dateTimeFormat { - return false - } - if lhs.nameDisplayOrder != rhs.nameDisplayOrder { - return false - } - if lhs.content != rhs.content { - return false - } - return true - } - - public final class View: UIView { - public private(set) var contentView: ChatTitleView? - - private var component: ChatTitleComponent? - - override init(frame: CGRect) { - super.init(frame: frame) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func update(component: ChatTitleComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { - self.component = component - - let contentView: ChatTitleView - if let current = self.contentView { - contentView = current - } else { - contentView = ChatTitleView( - context: component.context, - theme: component.theme, - strings: component.strings, - dateTimeFormat: component.dateTimeFormat, - nameDisplayOrder: component.nameDisplayOrder, - animationCache: component.context.animationCache, - animationRenderer: component.context.animationRenderer - ) - contentView.pressed = { [weak self] in - guard let self else { - return - } - self.component?.tapped() - } - contentView.longPressed = { [weak self] in - guard let self else { - return - } - self.component?.longTapped() - } - contentView.manualLayout = true - self.contentView = contentView - self.addSubview(contentView) - } - - if contentView.titleContent != component.content { - contentView.titleContent = component.content - } - contentView.updateThemeAndStrings(theme: component.theme, strings: component.strings, hasEmbeddedTitleContent: false) - - let _ = contentView.updateLayout(size: availableSize, clearBounds: CGRect(origin: CGPoint(), size: availableSize), transition: transition.containedViewLayoutTransition) - transition.setFrame(view: contentView, frame: CGRect(origin: CGPoint(), size: availableSize)) - - return availableSize - } - } - - public func makeView() -> View { - return View(frame: CGRect()) - } - - public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { - return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) - } -} diff --git a/submodules/TelegramUI/Components/ChatTitleView/Sources/TitleIconComponent.swift b/submodules/TelegramUI/Components/ChatTitleView/Sources/TitleIconComponent.swift new file mode 100644 index 00000000..53f34d7f --- /dev/null +++ b/submodules/TelegramUI/Components/ChatTitleView/Sources/TitleIconComponent.swift @@ -0,0 +1,96 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import TelegramPresentationData + +final class TitleIconComponent: Component { + enum Kind { + case mute + case lock + } + + let kind: Kind + let color: UIColor + + init( + kind: Kind, + color: UIColor + ) { + self.kind = kind + self.color = color + } + + static func ==(lhs: TitleIconComponent, rhs: TitleIconComponent) -> Bool { + if lhs.kind != rhs.kind { + return false + } + if lhs.color != rhs.color { + return false + } + return true + } + + final class View: UIView { + let iconView: UIImageView + + var component: TitleIconComponent? + + override init(frame: CGRect) { + self.iconView = UIImageView() + + super.init(frame: frame) + + self.addSubview(self.iconView) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(component: TitleIconComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + if self.component?.kind != component.kind { + switch component.kind { + case .mute: + self.iconView.image = generateImage(CGSize(width: 9.0, height: 9.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setFillColor(UIColor.white.cgColor) + + let _ = try? drawSvgPath(context, path: "M2.97607626,2.27306995 L5.1424026,0.18411241 C5.25492443,0.0756092198 5.40753677,0.0146527621 5.56666667,0.0146527621 C5.89803752,0.0146527621 6.16666667,0.273688014 6.16666667,0.593224191 L6.16666667,5.47790407 L8.86069303,8.18395735 C9.05193038,8.37604845 9.04547086,8.68126082 8.84626528,8.86566828 C8.6470597,9.05007573 8.33054317,9.0438469 8.13930581,8.85175581 L0.139306972,0.816042647 C-0.0519303838,0.623951552 -0.0454708626,0.318739175 0.153734717,0.134331724 C0.352940296,-0.0500757275 0.669456833,-0.0438469035 0.860694189,0.148244192 L2.97607626,2.27306995 Z M0.933196438,2.75856564 L6.16666667,8.01539958 L6.16666667,8.40677707 C6.16666667,8.56022375 6.10345256,8.70738566 5.99093074,8.81588885 C5.75661616,9.04183505 5.37671717,9.04183505 5.1424026,8.81588885 L2.59763107,6.36200202 C2.53511895,6.30172247 2.45033431,6.26785777 2.36192881,6.26785777 L1.16666667,6.26785777 C0.614381917,6.26785777 0.166666667,5.83613235 0.166666667,5.30357206 L0.166666667,3.6964292 C0.166666667,3.24138962 0.493527341,2.85996592 0.933196438,2.75856564 Z ") + })?.withRenderingMode(.alwaysTemplate) + case .lock: + self.iconView.image = generateImage(CGSize(width: 9.0, height: 13.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + context.translateBy(x: 0.0, y: 1.0) + + context.setFillColor(UIColor.white.cgColor) + context.setStrokeColor(UIColor.white.cgColor) + context.setLineWidth(1.32) + + let _ = try? drawSvgPath(context, path: "M4.5,0.600000024 C5.88071187,0.600000024 7,1.88484952 7,3.46979169 L7,7.39687502 C7,8.9818172 5.88071187,10.2666667 4.5,10.2666667 C3.11928813,10.2666667 2,8.9818172 2,7.39687502 L2,3.46979169 C2,1.88484952 3.11928813,0.600000024 4.5,0.600000024 S ") + let _ = try? drawSvgPath(context, path: "M1.32,5.65999985 L7.68,5.65999985 C8.40901587,5.65999985 9,6.25098398 9,6.97999985 L9,10.6733332 C9,11.4023491 8.40901587,11.9933332 7.68,11.9933332 L1.32,11.9933332 C0.59098413,11.9933332 1.11022302e-16,11.4023491 0,10.6733332 L2.22044605e-16,6.97999985 C1.11022302e-16,6.25098398 0.59098413,5.65999985 1.32,5.65999985 Z ") + })?.withRenderingMode(.alwaysTemplate) + } + } + + let size = CGSize(width: 14.0, height: 14.0) + + if let image = self.iconView.image { + let iconFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - image.size.width) * 0.5), y: floorToScreenPixels((size.height - image.size.height) * 0.5)), size: image.size) + self.iconView.frame = iconFrame + } + self.iconView.tintColor = component.color + + return size + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/CocoonInfoScreen/BUILD b/submodules/TelegramUI/Components/CocoonInfoScreen/BUILD new file mode 100644 index 00000000..014f566a --- /dev/null +++ b/submodules/TelegramUI/Components/CocoonInfoScreen/BUILD @@ -0,0 +1,37 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "CocoonInfoScreen", + module_name = "CocoonInfoScreen", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/ComponentFlow", + "//submodules/Postbox", + "//submodules/TelegramCore", + "//submodules/TelegramPresentationData", + "//submodules/TelegramUIPreferences", + "//submodules/AccountContext", + "//submodules/TelegramStringFormatting", + "//submodules/TextFormat", + "//submodules/PresentationDataUtils", + "//submodules/Components/ViewControllerComponent", + "//submodules/Components/SheetComponent", + "//submodules/Components/BundleIconComponent", + "//submodules/Components/MultilineTextComponent", + "//submodules/Components/BalancedTextComponent", + "//submodules/TelegramUI/Components/ButtonComponent", + "//submodules/TelegramUI/Components/LottieComponent", + "//submodules/TelegramUI/Components/GlassBarButtonComponent", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/CocoonInfoScreen/Sources/CocoonInfoScreen.swift b/submodules/TelegramUI/Components/CocoonInfoScreen/Sources/CocoonInfoScreen.swift new file mode 100644 index 00000000..61d04d1e --- /dev/null +++ b/submodules/TelegramUI/Components/CocoonInfoScreen/Sources/CocoonInfoScreen.swift @@ -0,0 +1,669 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import Postbox +import TelegramCore +import SwiftSignalKit +import AccountContext +import TelegramPresentationData +import PresentationDataUtils +import ComponentFlow +import ViewControllerComponent +import SheetComponent +import MultilineTextComponent +import BalancedTextComponent +import BundleIconComponent +import Markdown +import TextFormat +import TelegramStringFormatting +import GlassBarButtonComponent +import ButtonComponent +import LottieComponent + +private final class CocoonInfoSheetContent: CombinedComponent { + typealias EnvironmentType = ViewControllerComponentContainer.Environment + + let context: AccountContext + let animateOut: ActionSlot> + let getController: () -> ViewController? + + init( + context: AccountContext, + animateOut: ActionSlot>, + getController: @escaping () -> ViewController? + ) { + self.context = context + self.animateOut = animateOut + self.getController = getController + } + + static func ==(lhs: CocoonInfoSheetContent, rhs: CocoonInfoSheetContent) -> Bool { + if lhs.context !== rhs.context { + return false + } + return true + } + + final class State: ComponentState { + private let context: AccountContext + private let animateOut: ActionSlot> + private let getController: () -> ViewController? + + fileprivate let playButtonAnimation = ActionSlot() + private var didPlayAnimation = false + + init( + context: AccountContext, + animateOut: ActionSlot>, + getController: @escaping () -> ViewController? + ) { + self.context = context + self.animateOut = animateOut + self.getController = getController + + super.init() + } + + func playAnimationIfNeeded() { + if !self.didPlayAnimation { + self.didPlayAnimation = true + self.playButtonAnimation.invoke(Void()) + } + } + + func dismiss(animated: Bool) { + guard let controller = self.getController() as? CocoonInfoScreen else { + return + } + if animated { + self.animateOut.invoke(Action { [weak controller] _ in + controller?.dismiss(completion: nil) + }) + } else { + controller.dismiss(animated: false) + } + } + } + + func makeState() -> State { + return State(context: self.context, animateOut: self.animateOut, getController: self.getController) + } + + static var body: Body { + let closeButton = Child(GlassBarButtonComponent.self) + let icon = Child(BundleIconComponent.self) + let title = Child(BalancedTextComponent.self) + let text = Child(BalancedTextComponent.self) + let list = Child(List.self) + let additionalText = Child(MultilineTextComponent.self) + let button = Child(ButtonComponent.self) + + let navigateDisposable = MetaDisposable() + + return { context in + let component = context.component + let environment = context.environment[ViewControllerComponentContainer.Environment.self].value + let state = context.state + + let theme = environment.theme + let strings = environment.strings + + let sideInset: CGFloat = 30.0 + environment.safeInsets.left + let textSideInset: CGFloat = 30.0 + environment.safeInsets.left + + let titleFont = Font.bold(24.0) + let textFont = Font.regular(15.0) + let boldTextFont = Font.semibold(15.0) + + let textColor = theme.actionSheet.primaryTextColor + let secondaryTextColor = theme.actionSheet.secondaryTextColor + let linkColor = theme.actionSheet.controlAccentColor + + let spacing: CGFloat = 16.0 + var contentSize = CGSize(width: context.availableSize.width, height: 28.0) + + let icon = icon.update( + component: BundleIconComponent( + name: "Premium/Cocoon", tintColor: nil + ), + availableSize: context.availableSize, + transition: context.transition + ) + context.add(icon + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + icon.size.height / 2.0)) + ) + contentSize.height += icon.size.height + contentSize.height += 14.0 + + let title = title.update( + component: BalancedTextComponent( + text: .plain(NSAttributedString(string: strings.CocoonInfo_Title, font: titleFont, textColor: textColor)), + horizontalAlignment: .center, + maximumNumberOfLines: 0, + lineSpacing: 0.1 + ), + availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height), + transition: .immediate + ) + context.add(title + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + title.size.height / 2.0)) + ) + contentSize.height += title.size.height + contentSize.height += spacing - 8.0 + + let attributedText = parseMarkdownIntoAttributedString( + strings.CocoonInfo_Description, + attributes: MarkdownAttributes( + body: MarkdownAttributeSet(font: textFont, textColor: textColor), + bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), + link: MarkdownAttributeSet(font: textFont, textColor: linkColor), + linkAttribute: { _ in return nil } + ) + ) + let text = text.update( + component: BalancedTextComponent( + text: .plain(attributedText), + horizontalAlignment: .center, + maximumNumberOfLines: 0, + lineSpacing: 0.2 + ), + availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height), + transition: .immediate + ) + context.add(text + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + text.size.height / 2.0)) + ) + contentSize.height += text.size.height + contentSize.height += spacing + 9.0 + + var items: [AnyComponentWithIdentity] = [] + items.append( + AnyComponentWithIdentity( + id: "private", + component: AnyComponent(ParagraphComponent( + title: strings.CocoonInfo_Private_Title, + titleColor: textColor, + text: strings.CocoonInfo_Private_Text, + textColor: secondaryTextColor, + accentColor: linkColor, + iconName: "Ads/Privacy", + iconColor: linkColor, + action: { _, _ in + } + )) + ) + ) + items.append( + AnyComponentWithIdentity( + id: "efficient", + component: AnyComponent(ParagraphComponent( + title: strings.CocoonInfo_Efficient_Title, + titleColor: textColor, + text: strings.CocoonInfo_Efficient_Text, + textColor: secondaryTextColor, + accentColor: linkColor, + iconName: "Premium/Stats", + iconColor: linkColor, + action: { _, _ in + } + )) + ) + ) + items.append( + AnyComponentWithIdentity( + id: "for_everyone", + component: AnyComponent(ParagraphComponent( + title: strings.CocoonInfo_ForEveryone_Title, + titleColor: textColor, + text: strings.CocoonInfo_ForEveryone_Text, + textColor: secondaryTextColor, + accentColor: linkColor, + iconName: "Chat/Input/Accessory Panels/Gift", + iconColor: linkColor, + action: { attributes, _ in + guard let link = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String, let controller = environment.controller() else { + return + } + switch link { + case "telegram": + component.context.sharedContext.handleTextLinkAction(context: component.context, peerId: nil, navigateDisposable: navigateDisposable, controller: controller, action: .tap, itemLink: .url(url: "https://t.me/cocoon", concealed: false)) + case "web": + component.context.sharedContext.openExternalUrl(context: component.context, urlContext: .generic, url: "https://cocoon.org", forceExternal: true, presentationData: component.context.sharedContext.currentPresentationData.with { $0 }, navigationController: nil, dismissInput: {}) + default: + break + } + } + )) + ) + ) + + let list = list.update( + component: List(items), + availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 10000.0), + transition: context.transition + ) + context.add(list + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + list.size.height / 2.0)) + ) + contentSize.height += list.size.height + contentSize.height += spacing - 6.0 + + let attributedAdditionalText = parseMarkdownIntoAttributedString( + strings.CocoonInfo_IntergrateInfo, + attributes: MarkdownAttributes( + body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: secondaryTextColor), + bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: secondaryTextColor), + link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: linkColor), + linkAttribute: { contents in + return (TelegramTextAttributes.URL, contents) + } + ) + ) + let additionalText = additionalText.update( + component: MultilineTextComponent( + text: .plain(attributedAdditionalText), + horizontalAlignment: .center, + maximumNumberOfLines: 0, + lineSpacing: 0.2, + highlightColor: linkColor.withAlphaComponent(0.1), + highlightAction: { attributes in + if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] { + return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL) + } else { + return nil + } + }, + tapAction: { _, _ in + guard let controller = environment.controller() else { + return + } + component.context.sharedContext.handleTextLinkAction(context: component.context, peerId: nil, navigateDisposable: navigateDisposable, controller: controller, action: .tap, itemLink: .url(url: "https://t.me/cocoon?direct", concealed: false)) + } + ), + availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height), + transition: .immediate + ) + context.add(additionalText + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + additionalText.size.height / 2.0)) + ) + contentSize.height += additionalText.size.height + contentSize.height += spacing + 6.0 + + let closeButton = closeButton.update( + component: GlassBarButtonComponent( + size: CGSize(width: 40.0, height: 40.0), + backgroundColor: theme.rootController.navigationBar.glassBarButtonBackgroundColor, + isDark: theme.overallDarkAppearance, + state: .generic, + component: AnyComponentWithIdentity(id: "close", component: AnyComponent( + BundleIconComponent( + name: "Navigation/Close", + tintColor: theme.chat.inputPanel.panelControlColor + ) + )), + action: { [weak state] _ in + guard let state else { + return + } + state.dismiss(animated: true) + } + ), + availableSize: CGSize(width: 40.0, height: 40.0), + transition: .immediate + ) + context.add(closeButton + .position(CGPoint(x: 16.0 + closeButton.size.width / 2.0, y: 16.0 + closeButton.size.height / 2.0)) + ) + + + var buttonTitle: [AnyComponentWithIdentity] = [] + buttonTitle.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(LottieComponent( + content: LottieComponent.AppBundleContent(name: "anim_ok"), + color: theme.list.itemCheckColors.foregroundColor, + startingPosition: .begin, + size: CGSize(width: 28.0, height: 28.0), + playOnce: state.playButtonAnimation + )))) + buttonTitle.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(ButtonTextContentComponent( + text: strings.CocoonInfo_Understood, + badge: 0, + textColor: theme.list.itemCheckColors.foregroundColor, + badgeBackground: theme.list.itemCheckColors.foregroundColor, + badgeForeground: theme.list.itemCheckColors.fillColor + )))) + + let button = button.update( + component: ButtonComponent( + background: ButtonComponent.Background( + style: .glass, + color: theme.list.itemCheckColors.fillColor, + foreground: theme.list.itemCheckColors.foregroundColor, + pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9) + ), + content: AnyComponentWithIdentity( + id: AnyHashable(0), + component: AnyComponent(HStack(buttonTitle, spacing: 2.0)) + ), + isEnabled: true, + displaysProgress: false, + action: { [weak state] in + guard let state else { + return + } + state.dismiss(animated: true) + } + ), + availableSize: CGSize(width: context.availableSize.width - 30.0 * 2.0, height: 52.0), + transition: .immediate + ) + context.add(button + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + button.size.height / 2.0)) + ) + contentSize.height += button.size.height + + contentSize.height += 30.0 + + state.playAnimationIfNeeded() + + return contentSize + } + } +} + +final class CocoonInfoSheetComponent: CombinedComponent { + typealias EnvironmentType = ViewControllerComponentContainer.Environment + + let context: AccountContext + + init( + context: AccountContext + ) { + self.context = context + } + + static func ==(lhs: CocoonInfoSheetComponent, rhs: CocoonInfoSheetComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + return true + } + + static var body: Body { + let sheet = Child(SheetComponent.self) + let animateOut = StoredActionSlot(Action.self) + + let sheetExternalState = SheetComponent.ExternalState() + + return { context in + let environment = context.environment[EnvironmentType.self] + let controller = environment.controller + + let sheet = sheet.update( + component: SheetComponent( + content: AnyComponent(CocoonInfoSheetContent( + context: context.component.context, + animateOut: animateOut, + getController: controller + )), + style: .glass, + backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor), + followContentSizeChanges: true, + clipsContent: true, + autoAnimateOut: false, + externalState: sheetExternalState, + animateOut: animateOut, + onPan: { + }, + willDismiss: { + } + ), + environment: { + environment + SheetComponentEnvironment( + isDisplaying: environment.value.isVisible, + isCentered: environment.metrics.widthClass == .regular, + hasInputHeight: !environment.inputHeight.isZero, + regularMetricsSize: CGSize(width: 430.0, height: 900.0), + dismiss: { animated in + if animated { + if let controller = controller() as? CocoonInfoScreen { + animateOut.invoke(Action { _ in + controller.dismiss(completion: nil) + }) + } + } else { + if let controller = controller() as? CocoonInfoScreen { + controller.dismiss(completion: nil) + } + } + } + ) + }, + availableSize: context.availableSize, + transition: context.transition + ) + + context.add(sheet + .position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0)) + ) + + if let controller = controller(), !controller.automaticallyControlPresentationContextLayout { + var sideInset: CGFloat = 0.0 + var bottomInset: CGFloat = max(environment.safeInsets.bottom, sheetExternalState.contentHeight) + if case .regular = environment.metrics.widthClass { + sideInset = floor((context.availableSize.width - 430.0) / 2.0) - 12.0 + bottomInset = (context.availableSize.height - sheetExternalState.contentHeight) / 2.0 + sheetExternalState.contentHeight + } + + let layout = ContainerViewLayout( + size: context.availableSize, + metrics: environment.metrics, + deviceMetrics: environment.deviceMetrics, + intrinsicInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: bottomInset, right: 0.0), + safeInsets: UIEdgeInsets(top: 0.0, left: max(sideInset, environment.safeInsets.left), bottom: 0.0, right: max(sideInset, environment.safeInsets.right)), + additionalInsets: .zero, + statusBarHeight: environment.statusBarHeight, + inputHeight: nil, + inputHeightIsInteractivellyChanging: false, + inVoiceOver: false + ) + controller.presentationContext.containerLayoutUpdated(layout, transition: context.transition.containedViewLayoutTransition) + } + + return context.availableSize + } + } +} + +public final class CocoonInfoScreen: ViewControllerComponentContainer { + private let context: AccountContext + + public init( + context: AccountContext + ) { + self.context = context + + super.init( + context: context, + component: CocoonInfoSheetComponent( + context: context + ), + navigationBarAppearance: .none, + statusBarStyle: .ignore, + theme: .default + ) + + self.navigationPresentation = .flatModal + self.automaticallyControlPresentationContextLayout = false + } + + required public init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + } + + public override func viewDidLoad() { + super.viewDidLoad() + + self.view.disablesInteractiveModalDismiss = true + } + + public override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + } + + public func dismissAnimated() { + if let view = self.node.hostView.findTaggedView(tag: SheetComponent.View.Tag()) as? SheetComponent.View { + view.dismissAnimated() + } + } +} + +private final class ParagraphComponent: CombinedComponent { + let title: String + let titleColor: UIColor + let text: String + let textColor: UIColor + let accentColor: UIColor + let iconName: String + let iconColor: UIColor + let action: (([NSAttributedString.Key: Any], Int) -> Void)? + + public init( + title: String, + titleColor: UIColor, + text: String, + textColor: UIColor, + accentColor: UIColor, + iconName: String, + iconColor: UIColor, + action: (([NSAttributedString.Key: Any], Int) -> Void)? + ) { + self.title = title + self.titleColor = titleColor + self.text = text + self.textColor = textColor + self.accentColor = accentColor + self.iconName = iconName + self.iconColor = iconColor + self.action = action + } + + static func ==(lhs: ParagraphComponent, rhs: ParagraphComponent) -> Bool { + if lhs.title != rhs.title { + return false + } + if lhs.titleColor != rhs.titleColor { + return false + } + if lhs.text != rhs.text { + return false + } + if lhs.textColor != rhs.textColor { + return false + } + if lhs.accentColor != rhs.accentColor { + return false + } + if lhs.iconName != rhs.iconName { + return false + } + if lhs.iconColor != rhs.iconColor { + return false + } + return true + } + + static var body: Body { + let title = Child(MultilineTextComponent.self) + let text = Child(MultilineTextComponent.self) + let icon = Child(BundleIconComponent.self) + + return { context in + let component = context.component + + let leftInset: CGFloat = 32.0 + let rightInset: CGFloat = 24.0 + let textSideInset: CGFloat = leftInset + 8.0 + let spacing: CGFloat = 5.0 + + let textTopInset: CGFloat = 9.0 + + let title = title.update( + component: MultilineTextComponent( + text: .plain(NSAttributedString( + string: component.title, + font: Font.semibold(15.0), + textColor: component.titleColor, + paragraphAlignment: .natural + )), + horizontalAlignment: .center, + maximumNumberOfLines: 1 + ), + availableSize: CGSize(width: context.availableSize.width - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude), + transition: .immediate + ) + + let textFont = Font.regular(15.0) + let boldTextFont = Font.semibold(15.0) + let textColor = component.textColor + let accentColor = component.accentColor + let markdownAttributes = MarkdownAttributes( + body: MarkdownAttributeSet(font: textFont, textColor: textColor), + bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), + link: MarkdownAttributeSet(font: textFont, textColor: accentColor), + linkAttribute: { contents in + return (TelegramTextAttributes.URL, contents) + } + ) + + let text = text.update( + component: MultilineTextComponent( + text: .markdown(text: component.text, attributes: markdownAttributes), + horizontalAlignment: .natural, + maximumNumberOfLines: 0, + lineSpacing: 0.2, + highlightColor: accentColor.withAlphaComponent(0.1), + highlightAction: { attributes in + if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] { + return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL) + } else { + return nil + } + }, + tapAction: { attributes, index in + component.action?(attributes, index) + } + ), + availableSize: CGSize(width: context.availableSize.width - leftInset - rightInset, height: context.availableSize.height), + transition: .immediate + ) + + let icon = icon.update( + component: BundleIconComponent( + name: component.iconName, + tintColor: component.iconColor + ), + availableSize: CGSize(width: context.availableSize.width, height: context.availableSize.height), + transition: .immediate + ) + + context.add(title + .position(CGPoint(x: textSideInset + title.size.width / 2.0, y: textTopInset + title.size.height / 2.0)) + ) + + context.add(text + .position(CGPoint(x: textSideInset + text.size.width / 2.0, y: textTopInset + title.size.height + spacing + text.size.height / 2.0)) + ) + + context.add(icon + .position(CGPoint(x: 15.0, y: textTopInset + 18.0)) + ) + + return CGSize(width: context.availableSize.width, height: textTopInset + title.size.height + text.size.height + 20.0) + } + } +} diff --git a/submodules/TelegramUI/Components/ComposeTodoScreen/BUILD b/submodules/TelegramUI/Components/ComposeTodoScreen/BUILD index 691a1891..f10ddb89 100644 --- a/submodules/TelegramUI/Components/ComposeTodoScreen/BUILD +++ b/submodules/TelegramUI/Components/ComposeTodoScreen/BUILD @@ -44,7 +44,6 @@ swift_library( "//submodules/TelegramUI/Components/ListComposePollOptionComponent", "//submodules/ComposePollUI", "//submodules/Markdown", - "//submodules/TelegramUI/Components/EdgeEffect", "//submodules/TelegramUI/Components/GlassBarButtonComponent", ], visibility = [ diff --git a/submodules/TelegramUI/Components/ComposeTodoScreen/Sources/ComposeTodoScreen.swift b/submodules/TelegramUI/Components/ComposeTodoScreen/Sources/ComposeTodoScreen.swift index 2a8d1fa1..0f9c4740 100644 --- a/submodules/TelegramUI/Components/ComposeTodoScreen/Sources/ComposeTodoScreen.swift +++ b/submodules/TelegramUI/Components/ComposeTodoScreen/Sources/ComposeTodoScreen.swift @@ -29,24 +29,26 @@ import TextFieldComponent import ListComposePollOptionComponent import Markdown import PresentationDataUtils -import EdgeEffect import GlassBarButtonComponent final class ComposeTodoScreenComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment let context: AccountContext + let overNavigationContainer: UIView let peer: EnginePeer let initialData: ComposeTodoScreen.InitialData let completion: (TelegramMediaTodo) -> Void init( context: AccountContext, + overNavigationContainer: UIView, peer: EnginePeer, initialData: ComposeTodoScreen.InitialData, completion: @escaping (TelegramMediaTodo) -> Void ) { self.context = context + self.overNavigationContainer = overNavigationContainer self.peer = peer self.initialData = initialData self.completion = completion @@ -69,7 +71,6 @@ final class ComposeTodoScreenComponent: Component { final class View: UIView, UIScrollViewDelegate { private let scrollView: UIScrollView - private let edgeEffectView: EdgeEffectView private let todoTextSection = ComponentView() @@ -131,8 +132,6 @@ final class ComposeTodoScreenComponent: Component { self.scrollView.contentInsetAdjustmentBehavior = .never self.scrollView.alwaysBounceVertical = true - self.edgeEffectView = EdgeEffectView() - self.todoItemsSectionContainer = ListSectionContentView(frame: CGRect()) self.todoItemsSectionContainer.automaticallyLayoutExternalContentBackgroundView = false @@ -141,8 +140,6 @@ final class ComposeTodoScreenComponent: Component { self.scrollView.delegate = self self.addSubview(self.scrollView) - self.addSubview(self.edgeEffectView) - let reorderRecognizer = ReorderGestureRecognizer( shouldBegin: { [weak self] point in guard let self, let (id, item) = self.item(at: point) else { @@ -468,7 +465,6 @@ final class ComposeTodoScreenComponent: Component { pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, - importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, @@ -1539,11 +1535,6 @@ final class ComposeTodoScreenComponent: Component { } } } - - let edgeEffectHeight: CGFloat = 80.0 - let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: edgeEffectHeight)) - transition.setFrame(view: self.edgeEffectView, frame: edgeEffectFrame) - self.edgeEffectView.update(content: theme.list.blocksBackgroundColor, blur: true, alpha: 1.0, rect: edgeEffectFrame, edge: .top, edgeSize: edgeEffectFrame.height, transition: transition) let title: String if !component.initialData.canEdit && component.initialData.existingTodo != nil { @@ -1571,23 +1562,23 @@ final class ComposeTodoScreenComponent: Component { let titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - titleSize.width) / 2.0), y: floorToScreenPixels((environment.navigationHeight - titleSize.height) / 2.0) + 3.0), size: titleSize) if let titleView = self.title.view { if titleView.superview == nil { - self.addSubview(titleView) + component.overNavigationContainer.addSubview(titleView) } transition.setFrame(view: titleView, frame: titleFrame) } - let barButtonSize = CGSize(width: 40.0, height: 40.0) + let barButtonSize = CGSize(width: 44.0, height: 44.0) let cancelButtonSize = self.cancelButton.update( transition: transition, component: AnyComponent(GlassBarButtonComponent( size: barButtonSize, - backgroundColor: environment.theme.rootController.navigationBar.glassBarButtonBackgroundColor, + backgroundColor: nil, isDark: environment.theme.overallDarkAppearance, - state: .generic, + state: .glass, component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: environment.theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: environment.theme.chat.inputPanel.panelControlColor ) )), action: { [weak self] _ in @@ -1603,7 +1594,7 @@ final class ComposeTodoScreenComponent: Component { let cancelButtonFrame = CGRect(origin: CGPoint(x: environment.safeInsets.left + 16.0, y: 16.0), size: cancelButtonSize) if let cancelButtonView = self.cancelButton.view { if cancelButtonView.superview == nil { - self.addSubview(cancelButtonView) + component.overNavigationContainer.addSubview(cancelButtonView) } transition.setFrame(view: cancelButtonView, frame: cancelButtonFrame) } @@ -1639,7 +1630,7 @@ final class ComposeTodoScreenComponent: Component { let doneButtonFrame = CGRect(origin: CGPoint(x: availableSize.width - environment.safeInsets.right - 16.0 - doneButtonSize.width, y: 16.0), size: doneButtonSize) if let doneButtonView = self.doneButton.view { if doneButtonView.superview == nil { - self.addSubview(doneButtonView) + component.overNavigationContainer.addSubview(doneButtonView) } transition.setFrame(view: doneButtonView, frame: doneButtonFrame) } @@ -1704,6 +1695,8 @@ public class ComposeTodoScreen: ViewControllerComponentContainer, AttachmentCont fileprivate let completion: (TelegramMediaTodo) -> Void private var isDismissed: Bool = false + private let overNavigationContainer: UIView + fileprivate private(set) var sendButtonItem: UIBarButtonItem? public var isMinimized: Bool = false @@ -1747,12 +1740,15 @@ public class ComposeTodoScreen: ViewControllerComponentContainer, AttachmentCont self.context = context self.completion = completion + self.overNavigationContainer = SparseContainerView() + super.init(context: context, component: ComposeTodoScreenComponent( context: context, + overNavigationContainer: self.overNavigationContainer, peer: peer, initialData: initialData, completion: completion - ), navigationBarAppearance: .transparent, theme: .default) + ), navigationBarAppearance: .default, theme: .default) self._hasGlassStyle = true @@ -1787,6 +1783,10 @@ public class ComposeTodoScreen: ViewControllerComponentContainer, AttachmentCont return componentView.attemptNavigation(complete: complete) } + + if let navigationBar = self.navigationBar { + navigationBar.customOverBackgroundContentView.insertSubview(self.overNavigationContainer, at: 0) + } } required public init(coder aDecoder: NSCoder) { diff --git a/submodules/TelegramUI/Components/Contacts/NewContactScreen/Sources/NewContactScreen.swift b/submodules/TelegramUI/Components/Contacts/NewContactScreen/Sources/NewContactScreen.swift index ae98d94b..47447151 100644 --- a/submodules/TelegramUI/Components/Contacts/NewContactScreen/Sources/NewContactScreen.swift +++ b/submodules/TelegramUI/Components/Contacts/NewContactScreen/Sources/NewContactScreen.swift @@ -897,7 +897,7 @@ final class NewContactScreenComponent: Component { component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: environment.theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: environment.theme.chat.inputPanel.panelControlColor ) )), action: { [weak self] _ in diff --git a/submodules/TelegramUI/Components/Contacts/NewContactScreen/Sources/PhoneInputItem.swift b/submodules/TelegramUI/Components/Contacts/NewContactScreen/Sources/PhoneInputItem.swift index bfb74ff6..b87ce202 100644 --- a/submodules/TelegramUI/Components/Contacts/NewContactScreen/Sources/PhoneInputItem.swift +++ b/submodules/TelegramUI/Components/Contacts/NewContactScreen/Sources/PhoneInputItem.swift @@ -175,7 +175,7 @@ final class PhoneInputItemNode: ListViewItemNode, ItemListItemNode { self.checkNode.displaysAsynchronously = false self.checkNode.isLayerBacked = true - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.phoneBackground) self.addSubnode(self.countryButton) diff --git a/submodules/TelegramUI/Components/ContentReportScreen/Sources/ContentReportScreen.swift b/submodules/TelegramUI/Components/ContentReportScreen/Sources/ContentReportScreen.swift index ae83820e..e7da15e9 100644 --- a/submodules/TelegramUI/Components/ContentReportScreen/Sources/ContentReportScreen.swift +++ b/submodules/TelegramUI/Components/ContentReportScreen/Sources/ContentReportScreen.swift @@ -529,7 +529,7 @@ private final class SheetContent: CombinedComponent { component: AnyComponentWithIdentity(id: isBack ? "back" : "close", component: AnyComponent( BundleIconComponent( name: isBack ? "Navigation/Back" : "Navigation/Close", - tintColor: environment.theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: environment.theme.chat.inputPanel.panelControlColor ) )), action: { [weak state] _ in diff --git a/submodules/TelegramUI/Components/EdgeEffect/BUILD b/submodules/TelegramUI/Components/EdgeEffect/BUILD index 62ff9343..e5bc97de 100644 --- a/submodules/TelegramUI/Components/EdgeEffect/BUILD +++ b/submodules/TelegramUI/Components/EdgeEffect/BUILD @@ -12,6 +12,7 @@ swift_library( deps = [ "//submodules/Display", "//submodules/ComponentFlow", + "//submodules/Components/ComponentDisplayAdapters", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/EdgeEffect/Sources/EdgeEffect.swift b/submodules/TelegramUI/Components/EdgeEffect/Sources/EdgeEffect.swift index e48426b0..ecd4bc66 100644 --- a/submodules/TelegramUI/Components/EdgeEffect/Sources/EdgeEffect.swift +++ b/submodules/TelegramUI/Components/EdgeEffect/Sources/EdgeEffect.swift @@ -2,6 +2,7 @@ import Foundation import UIKit import Display import ComponentFlow +import ComponentDisplayAdapters public class EdgeEffectView: UIView { public enum Edge { @@ -27,7 +28,13 @@ public class EdgeEffectView: UIView { fatalError("init(coder:) has not been implemented") } - public func update(content: UIColor, blur: Bool = false, alpha: CGFloat = 0.65, rect: CGRect, edge: Edge, edgeSize: CGFloat, transition: ComponentTransition) { + public func update(content: UIColor, blur: Bool = false, alpha: CGFloat = 0.75, rect: CGRect, edge: Edge, edgeSize: CGFloat, transition: ComponentTransition) { + #if DEBUG && false + let content: UIColor = .blue + let blur: Bool = !"".isEmpty + self.backgroundColor = .blue + #endif + transition.setBackgroundColor(view: self.contentView, color: content) switch edge { @@ -76,38 +83,39 @@ public class EdgeEffectView: UIView { } if blur { - let gradientMaskLayer = SimpleGradientLayer() - let baseGradientAlpha: CGFloat = 1.0 - let numSteps = 8 - let firstStep = 1 - let firstLocation = 0.8 - gradientMaskLayer.colors = (0 ..< numSteps).map { i in - if i < firstStep { - return UIColor(white: 1.0, alpha: 1.0).cgColor - } else { - let step: CGFloat = CGFloat(i - firstStep) / CGFloat(numSteps - firstStep - 1) - let value: CGFloat = 1.0 - bezierPoint(0.42, 0.0, 0.58, 1.0, step) - return UIColor(white: 1.0, alpha: baseGradientAlpha * value).cgColor - } - } - gradientMaskLayer.locations = (0 ..< numSteps).map { i -> NSNumber in - if i < firstStep { - return 0.0 as NSNumber - } else { - let step: CGFloat = CGFloat(i - firstStep) / CGFloat(numSteps - firstStep - 1) - return (firstLocation + (1.0 - firstLocation) * step) as NSNumber - } - } - let blurView: VariableBlurView if let current = self.blurView { blurView = current } else { + let gradientMaskLayer = SimpleGradientLayer() + let baseGradientAlpha: CGFloat = 1.0 + let numSteps = 8 + let firstStep = 1 + let firstLocation = 0.8 + gradientMaskLayer.colors = (0 ..< numSteps).map { i in + if i < firstStep { + return UIColor(white: 1.0, alpha: 1.0).cgColor + } else { + let step: CGFloat = CGFloat(i - firstStep) / CGFloat(numSteps - firstStep - 1) + let value: CGFloat = 1.0 - bezierPoint(0.42, 0.0, 0.58, 1.0, step) + return UIColor(white: 1.0, alpha: baseGradientAlpha * value).cgColor + } + } + gradientMaskLayer.locations = (0 ..< numSteps).map { i -> NSNumber in + if i < firstStep { + return 0.0 as NSNumber + } else { + let step: CGFloat = CGFloat(i - firstStep) / CGFloat(numSteps - firstStep - 1) + return (firstLocation + (1.0 - firstLocation) * step) as NSNumber + } + } + blurView = VariableBlurView(gradientMask: self.contentMaskView.image ?? UIImage(), maxBlurRadius: 8.0) blurView.layer.mask = gradientMaskLayer self.insertSubview(blurView, at: 0) self.blurView = blurView } + blurView.update(size: bounds.size, transition: transition.containedViewLayoutTransition) transition.setFrame(view: blurView, frame: bounds) if let maskLayer = blurView.layer.mask { transition.setFrame(layer: maskLayer, frame: bounds) @@ -271,4 +279,10 @@ public final class VariableBlurView: UIVisualEffectView { backdropLayer?.filters = [variableBlur] backdropLayer?.setValue(UIScreenScale, forKey: "scale") } + + public func update(size: CGSize, transition: ContainedViewLayoutTransition) { + for layer in self.layer.sublayers ?? [] { + transition.updateFrame(layer: layer, frame: CGRect(origin: CGPoint(), size: size)) + } + } } diff --git a/submodules/TelegramUI/Components/EmojiGameStakeScreen/BUILD b/submodules/TelegramUI/Components/EmojiGameStakeScreen/BUILD new file mode 100644 index 00000000..0200885e --- /dev/null +++ b/submodules/TelegramUI/Components/EmojiGameStakeScreen/BUILD @@ -0,0 +1,55 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "EmojiGameStakeScreen", + module_name = "EmojiGameStakeScreen", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/Postbox", + "//submodules/TelegramCore", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/ComponentFlow", + "//submodules/Components/ViewControllerComponent", + "//submodules/Components/ComponentDisplayAdapters", + "//submodules/Components/MultilineTextComponent", + "//submodules/Components/BalancedTextComponent", + "//submodules/TelegramPresentationData", + "//submodules/AccountContext", + "//submodules/AppBundle", + "//submodules/ItemListUI", + "//submodules/TelegramStringFormatting", + "//submodules/PresentationDataUtils", + "//submodules/Components/SheetComponent", + "//submodules/UndoUI", + "//submodules/TextFormat", + "//submodules/TelegramUI/Components/ListSectionComponent", + "//submodules/TelegramUI/Components/ListActionItemComponent", + "//submodules/TelegramUI/Components/ScrollComponent", + "//submodules/TelegramUI/Components/Premium/PremiumStarComponent", + "//submodules/TelegramUI/Components/ButtonComponent", + "//submodules/Components/BundleIconComponent", + "//submodules/PasswordSetupUI", + "//submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController", + "//submodules/TelegramUI/Components/ChatScheduleTimeController", + "//submodules/TelegramUI/Components/TabSelectorComponent", + "//submodules/TelegramUI/Components/Stars/BalanceNeededScreen", + "//submodules/TelegramUI/Components/GlassBarButtonComponent", + "//submodules/TelegramUI/Components/GlassBackgroundComponent", + "//submodules/TelegramUI/Components/Stars/StarsBalanceOverlayComponent", + "//submodules/TelegramUI/Components/LottieComponent", + "//submodules/TelegramUI/Components/LottieComponentResourceContent", + "//submodules/TelegramUI/Components/EdgeEffect", + "//submodules/Components/MultilineTextWithEntitiesComponent", + "//submodules/TelegramUI/Components/PlainButtonComponent", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/EmojiGameStakeScreen/Sources/EmojiGameStakeScreen.swift b/submodules/TelegramUI/Components/EmojiGameStakeScreen/Sources/EmojiGameStakeScreen.swift new file mode 100644 index 00000000..59a534c9 --- /dev/null +++ b/submodules/TelegramUI/Components/EmojiGameStakeScreen/Sources/EmojiGameStakeScreen.swift @@ -0,0 +1,2220 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import ComponentFlow +import SwiftSignalKit +import Postbox +import TelegramCore +import Markdown +import TextFormat +import TelegramPresentationData +import ViewControllerComponent +import SheetComponent +import BalancedTextComponent +import MultilineTextComponent +import MultilineTextWithEntitiesComponent +import BundleIconComponent +import ButtonComponent +import AccountContext +import PresentationDataUtils +import ListSectionComponent +import TelegramStringFormatting +import UndoUI +import ListActionItemComponent +import PresentationDataUtils +import BalanceNeededScreen +import GlassBarButtonComponent +import GlassBackgroundComponent +import StarsBalanceOverlayComponent +import LottieComponent +import LottieComponentResourceContent +import EdgeEffect +import PlainButtonComponent + +private let amountTag = GenericComponentViewTag() + +private final class SheetContent: CombinedComponent { + typealias EnvironmentType = ViewControllerComponentContainer.Environment + + let context: AccountContext + let gameInfo: EmojiGameInfo.Info + let controller: () -> ViewController? + let dismiss: () -> Void + + init( + context: AccountContext, + gameInfo: EmojiGameInfo.Info, + controller: @escaping () -> ViewController?, + dismiss: @escaping () -> Void + ) { + self.context = context + self.gameInfo = gameInfo + self.controller = controller + self.dismiss = dismiss + } + + static func ==(lhs: SheetContent, rhs: SheetContent) -> Bool { + return true + } + + static var body: (CombinedComponentContext) -> CGSize { + let description = Child(BalancedTextComponent.self) + let resultsTitle = Child(MultilineTextComponent.self) + let results = Child(VStack.self) + let resultsFooter = Child(MultilineTextWithEntitiesComponent.self) + let amountSection = Child(ListSectionComponent.self) + let button = Child(ButtonComponent.self) + + let body: (CombinedComponentContext) -> CGSize = { (context: CombinedComponentContext) -> CGSize in + let environment = context.environment[EnvironmentType.self] + let component = context.component + let state = context.state + + state.component = component + + let controller = environment.controller + + let theme = environment.theme.withModalBlocksBackground() + let strings = environment.strings + let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } + + let sideInset: CGFloat = 16.0 + environment.safeInsets.left + let textSideInset: CGFloat = 32.0 + environment.safeInsets.left + var contentSize = CGSize(width: context.availableSize.width, height: 75.0) + + let textFont = Font.regular(15.0) + let boldTextFont = Font.semibold(15.0) + let textColor = theme.actionSheet.primaryTextColor + let linkColor = theme.actionSheet.controlAccentColor + let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in + return (TelegramTextAttributes.URL, contents) + }) + + let description = description.update( + component: BalancedTextComponent( + text: .markdown(text: strings.EmojiStake_Description, attributes: markdownAttributes), + horizontalAlignment: .center, + maximumNumberOfLines: 0, + lineSpacing: 0.2 + ), + availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height), + transition: .immediate + ) + context.add(description + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + description.size.height * 0.5)) + ) + contentSize.height += description.size.height + contentSize.height += 32.0 + + let resultsTitle = resultsTitle.update( + component: MultilineTextComponent(text: .plain(NSAttributedString( + string: strings.EmojiStake_ResultsTitle.uppercased(), + font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize), + textColor: theme.list.freeTextColor + ))), + availableSize: context.availableSize, + transition: .immediate + ) + context.add(resultsTitle + .position(CGPoint(x: textSideInset + resultsTitle.size.width * 0.5, y: contentSize.height + resultsTitle.size.height * 0.5)) + ) + contentSize.height += resultsTitle.size.height + contentSize.height += 6.0 + + let resultSpacing: CGFloat = 8.0 + let resultSize = CGSize(width: (context.availableSize.width - sideInset * 2.0 - resultSpacing * 3.0) / 4.0, height: 64.0) + let doubleResultSize = CGSize(width: resultSize.width * 2.0 + resultSpacing, height: resultSize.height) + + var resultValue1: Int32 = 0 + var resultValue2: Int32 = 300 + var resultValue3: Int32 = 600 + var resultValue4: Int32 = 1300 + var resultValue5: Int32 = 1600 + var resultValue6: Int32 = 2000 + var resultValue7: Int32 = 20000 + if context.component.gameInfo.parameters.count == 7 { + resultValue1 = context.component.gameInfo.parameters[0] + resultValue2 = context.component.gameInfo.parameters[1] + resultValue3 = context.component.gameInfo.parameters[2] + resultValue4 = context.component.gameInfo.parameters[3] + resultValue5 = context.component.gameInfo.parameters[4] + resultValue6 = context.component.gameInfo.parameters[5] + resultValue7 = context.component.gameInfo.parameters[6] + } + + let results = results.update( + component: VStack([ + AnyComponentWithIdentity(id: "first", component: AnyComponent( + HStack([ + AnyComponentWithIdentity(id: 1, component: AnyComponent( + ResultCellComponent(context: component.context, theme: environment.theme, files: state.emojiFiles.flatMap { [$0[1]] }, value: resultValue1, size: resultSize) + )), + AnyComponentWithIdentity(id: 2, component: AnyComponent( + ResultCellComponent(context: component.context, theme: environment.theme, files: state.emojiFiles.flatMap { [$0[2]] }, value: resultValue2, size: resultSize) + )), + AnyComponentWithIdentity(id: 3, component: AnyComponent( + ResultCellComponent(context: component.context, theme: environment.theme, files: state.emojiFiles.flatMap { [$0[3]] }, value: resultValue3, size: resultSize) + )), + AnyComponentWithIdentity(id: 4, component: AnyComponent( + ResultCellComponent(context: component.context, theme: environment.theme, files: state.emojiFiles.flatMap { [$0[4]] }, value: resultValue4, size: resultSize) + )) + ], spacing: resultSpacing) + )), + AnyComponentWithIdentity(id: "second", component: AnyComponent( + HStack([ + AnyComponentWithIdentity(id: 5, component: AnyComponent( + ResultCellComponent(context: component.context, theme: environment.theme, files: state.emojiFiles.flatMap { [$0[5]] }, value: resultValue5, size: resultSize) + )), + AnyComponentWithIdentity(id: 6, component: AnyComponent( + ResultCellComponent(context: component.context, theme: environment.theme, files: state.emojiFiles.flatMap { [$0[6]] }, value: resultValue6, size: resultSize) + )), + AnyComponentWithIdentity(id: 7, component: AnyComponent( + ResultCellComponent(context: component.context, theme: environment.theme, files: state.emojiFiles.flatMap { [$0[6], $0[6], $0[6]] }, value: resultValue7, size: doubleResultSize) + )), + ], spacing: resultSpacing) + )) + ], spacing: resultSpacing), + availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: context.availableSize.height), + transition: context.transition + ) + context.add(results + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + results.size.height * 0.5)) + ) + contentSize.height += results.size.height + contentSize.height += 7.0 + + let resultsFooterAttributedText = NSMutableAttributedString( + string: strings.EmojiStake_StreakInfo, + font: Font.regular(13.0), + textColor: theme.list.freeTextColor + ) + if let emojiFile = state.emojiFiles?[6] { + let range = (resultsFooterAttributedText.string as NSString).range(of: "#") + if range.location != NSNotFound { + resultsFooterAttributedText.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: emojiFile, custom: .dice, enableAnimation: false), range: range) + } + } + + let resultsFooter = resultsFooter.update( + component: MultilineTextWithEntitiesComponent( + context: component.context, + animationCache: component.context.animationCache, + animationRenderer: component.context.animationRenderer, + placeholderColor: .clear, + text: .plain(resultsFooterAttributedText), + maximumNumberOfLines: 0, + enableLooping: true + ), + availableSize: context.availableSize, + transition: .immediate + ) + context.add(resultsFooter + .position(CGPoint(x: textSideInset + resultsFooter.size.width * 0.5, y: contentSize.height + resultsFooter.size.height * 0.5)) + ) + contentSize.height += resultsFooter.size.height + contentSize.height += 39.0 + + if state.cachedChevronImage == nil || state.cachedChevronImage?.1 !== environment.theme { + state.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Contact List/SubtitleArrow"), color: environment.theme.list.itemAccentColor)!, environment.theme) + } + + let configuration = EmojiGameStakeConfiguration.with(appConfiguration: component.context.currentAppConfiguration.with { $0 }) + var amountLabel = "" + if let tonUsdRate = configuration.tonUsdRate, let value = state.amount?.value, value > 0 { + amountLabel = "~\(formatTonUsdValue(value, divide: true, rate: tonUsdRate, dateTimeFormat: environment.dateTimeFormat))" + } + + let amountItems: [AnyComponentWithIdentity] = [ + AnyComponentWithIdentity( + id: "amount", + component: AnyComponent( + AmountFieldComponent( + textColor: theme.list.itemPrimaryTextColor, + secondaryColor: theme.list.itemSecondaryTextColor, + placeholderColor: theme.list.itemPlaceholderTextColor, + accentColor: theme.list.itemAccentColor, + value: state.amount?.value, + minValue: 0, + forceMinValue: false, + allowZero: true, + maxValue: nil, + placeholderText: strings.EmojiStake_StakePlaceholder, + labelText: amountLabel, + currency: .ton, + dateTimeFormat: presentationData.dateTimeFormat, + amountUpdated: { [weak state] amount in + state?.amount = amount.flatMap { StarsAmount(value: $0, nanos: 0) } + state?.updated() + }, + tag: amountTag + ) + ) + ), + AnyComponentWithIdentity(id: "presets", component: AnyComponent( + AmountPresetsListItemComponent( + context: component.context, + theme: theme, + values: configuration.suggestedAmounts, + valueSelected: { [weak state] value in + guard let state else { + return + } + state.amount = StarsAmount(value: value, nanos: 0) + if let controller = controller() as? EmojiGameStakeScreen { + controller.dismissInput() + state.updated() + controller.resetValue() + } + } + ) + )) + ] + + let amountSection = amountSection.update( + component: ListSectionComponent( + theme: theme, + style: .glass, + header: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString( + string: strings.EmojiStake_StakeTitle.uppercased(), + font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize), + textColor: theme.list.freeTextColor + )), + maximumNumberOfLines: 0 + )), + footer: nil, + items: amountItems + ), + environment: {}, + availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: .greatestFiniteMagnitude), + transition: .immediate + ) + context.add(amountSection + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + amountSection.size.height / 2.0)) + .clipsToBounds(true) + .cornerRadius(10.0) + ) + contentSize.height += amountSection.size.height + contentSize.height += 24.0 + + + var buttonItems: [AnyComponentWithIdentity] = [] + buttonItems.append(AnyComponentWithIdentity(id: "icon", component: AnyComponent(BundleIconComponent(name: "Premium/Dice", tintColor: theme.list.itemCheckColors.foregroundColor)))) + buttonItems.append(AnyComponentWithIdentity(id: "label", component: AnyComponent(Text(text: environment.strings.EmojiStake_Roll, font: Font.semibold(17.0), color: theme.list.itemCheckColors.foregroundColor)))) + + let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: environment.safeInsets.bottom, innerDiameter: 52.0, sideInset: 30.0) + let button = button.update( + component: ButtonComponent( + background: ButtonComponent.Background( + style: .glass, + color: theme.list.itemCheckColors.fillColor, + foreground: theme.list.itemCheckColors.foregroundColor, + pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9) + ), + content: AnyComponentWithIdentity( + id: AnyHashable(0), + component: AnyComponent(HStack(buttonItems, spacing: 7.0)) + ), + action: { [weak state] in + if let state, let amount = state.amount, let controller = controller() as? EmojiGameStakeScreen { + controller.complete(amount: amount) + } + } + ), + availableSize: CGSize(width: context.availableSize.width - buttonInsets.left - buttonInsets.right, height: 52.0), + transition: .immediate + ) + context.add(button + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + button.size.height / 2.0)) + ) + contentSize.height += button.size.height + + if environment.inputHeight > 0.0 { + contentSize.height += 15.0 + contentSize.height += max(environment.inputHeight, environment.safeInsets.bottom) + } else { + contentSize.height += buttonInsets.bottom + } + + return contentSize + } + + return body + } + + final class State: ComponentState { + fileprivate let context: AccountContext + + fileprivate var component: SheetContent + + fileprivate var forceUpdateAmount = false + fileprivate var amount: StarsAmount? + fileprivate var currency: CurrencyAmount.Currency = .ton + + var cachedChevronImage: (UIImage, PresentationTheme)? + + var emojiFiles: [TelegramMediaFile]? + var emojiFilesDisposable: Disposable? + + init(component: SheetContent) { + self.context = component.context + self.component = component + + let amount: StarsAmount? = StarsAmount(value: component.gameInfo.previousStake, nanos: 0) + let currency: CurrencyAmount.Currency = .ton + + self.currency = currency + self.amount = amount + + super.init() + + self.emojiFilesDisposable = (self.context.engine.stickers.loadedStickerPack(reference: .dice("🎲"), forceActualized: false) + |> mapToSignal { stickerPack -> Signal<[TelegramMediaFile], NoError> in + switch stickerPack { + case let .result(_, items, _): + var emojiStickers: [TelegramMediaFile] = [] + for item in items { + emojiStickers.append(item.file._parse()) + } + return .single(emojiStickers) + default: + return .complete() + } + } + |> deliverOnMainQueue).start(next: { [weak self] files in + guard let self else { + return + } + self.emojiFiles = files + self.updated() + }) + } + + deinit { + self.emojiFilesDisposable?.dispose() + } + } + + func makeState() -> State { + return State(component: self) + } +} + +private final class EmojiGameStakeSheetComponent: CombinedComponent { + typealias EnvironmentType = ViewControllerComponentContainer.Environment + + private let context: AccountContext + private let gameInfo: EmojiGameInfo.Info + + init( + context: AccountContext, + gameInfo: EmojiGameInfo.Info + ) { + self.context = context + self.gameInfo = gameInfo + } + + static func ==(lhs: EmojiGameStakeSheetComponent, rhs: EmojiGameStakeSheetComponent) -> Bool { + return true + } + + static var body: Body { + let sheet = Child(ResizableSheetComponent<(EnvironmentType)>.self) + let animateOut = StoredActionSlot(Action.self) + + return { context in + let environment = context.environment[EnvironmentType.self] + + let controller = environment.controller + + let dismiss: (Bool) -> Void = { animated in + if animated { + animateOut.invoke(Action { _ in + if let controller = controller() { + controller.dismiss(completion: nil) + } + }) + } else { + if let controller = controller() { + controller.dismiss(completion: nil) + } + } + } + + let theme = environment.theme.withModalBlocksBackground() + + var buttonItems: [AnyComponentWithIdentity] = [] + buttonItems.append(AnyComponentWithIdentity(id: "icon", component: AnyComponent(Image(image: PresentationResourcesItemList.itemListRoundTopupIcon(environment.theme), tintColor: theme.list.itemCheckColors.foregroundColor, size: CGSize(width: 16.0, height: 18.0))))) + buttonItems.append(AnyComponentWithIdentity(id: "label", component: AnyComponent(Text(text: environment.strings.EmojiStake_Roll, font: Font.semibold(17.0), color: theme.list.itemCheckColors.foregroundColor)))) + + let sheet = sheet.update( + component: ResizableSheetComponent( + content: AnyComponent(SheetContent( + context: context.component.context, + gameInfo: context.component.gameInfo, + controller: { + return controller() + }, + dismiss: { + dismiss(true) + } + )), + titleItem: AnyComponent( + Text(text: environment.strings.EmojiStake_Title, font: Font.bold(17.0), color: theme.list.itemPrimaryTextColor) + ), + leftItem: AnyComponent( + GlassBarButtonComponent( + size: CGSize(width: 40.0, height: 40.0), + backgroundColor: theme.rootController.navigationBar.glassBarButtonBackgroundColor, + isDark: theme.overallDarkAppearance, + state: .generic, + component: AnyComponentWithIdentity(id: "close", component: AnyComponent( + BundleIconComponent( + name: "Navigation/Close", + tintColor: theme.chat.inputPanel.panelControlColor + ) + )), + action: { _ in + dismiss(true) + } + ) + ), + bottomItem: AnyComponent( + ButtonComponent( + background: ButtonComponent.Background( + style: .glass, + color: theme.list.itemCheckColors.fillColor, + foreground: theme.list.itemCheckColors.foregroundColor, + pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9) + ), + content: AnyComponentWithIdentity( + id: AnyHashable(0), + component: AnyComponent(HStack(buttonItems, spacing: 7.0)) + ), + isEnabled: true, + displaysProgress: false, + action: { + dismiss(true) + } + ) + ), + backgroundColor: .color(theme.list.blocksBackgroundColor), + animateOut: animateOut + ), + environment: { + environment + ResizableSheetComponentEnvironment( + theme: theme, + statusBarHeight: environment.statusBarHeight, + safeInsets: environment.safeInsets, + metrics: environment.metrics, + deviceMetrics: environment.deviceMetrics, + isDisplaying: environment.value.isVisible, + isCentered: environment.metrics.widthClass == .regular, + regularMetricsSize: CGSize(width: 430.0, height: 900.0), + dismiss: { animated in + dismiss(animated) + } + ) + }, + availableSize: context.availableSize, + transition: context.transition + ) + + context.add(sheet + .position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0)) + ) + + return context.availableSize + } + } +} + +public final class EmojiGameStakeScreen: ViewControllerComponentContainer { + private let context: AccountContext + fileprivate let completion: (StarsAmount) -> Void + + fileprivate let balanceOverlay = ComponentView() + private var showBalance = true + + public init( + context: AccountContext, + gameInfo: EmojiGameInfo.Info, + completion: @escaping (StarsAmount) -> Void + ) { + self.context = context + self.completion = completion + + super.init( + context: context, + component: EmojiGameStakeSheetComponent( + context: context, + gameInfo: gameInfo + ), + navigationBarAppearance: .none, + statusBarStyle: .ignore, + theme: .default + ) + + self.navigationPresentation = .flatModal + + self.context.tonContext?.load(force: true) + } + + required public init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + fileprivate func dismissInput() { + if let view = self.node.hostView.findTaggedView(tag: amountTag) as? AmountFieldComponent.View { + view.deactivateInput() + } + } + + fileprivate func resetValue() { + if let view = self.node.hostView.findTaggedView(tag: amountTag) as? AmountFieldComponent.View { + view.resetValue() + } + } + + public func dismissAnimated() { + if let view = self.node.hostView.findTaggedView(tag: ResizableSheetComponent.View.Tag()) as? ResizableSheetComponent.View { + view.dismissAnimated() + } + } + + func complete(amount: StarsAmount) { + if let tonState = self.context.tonContext?.currentState, tonState.balance < amount { + let needed = amount - tonState.balance + var fragmentUrl = "https://fragment.com/ads/topup" + if let data = self.context.currentAppConfiguration.with({ $0 }).data, let value = data["ton_topup_url"] as? String { + fragmentUrl = value + } + self.push(BalanceNeededScreen( + context: self.context, + amount: needed, + buttonAction: { [weak self] in + self?.context.sharedContext.applicationBindings.openUrl(fragmentUrl) + } + )) + } else { + self.completion(amount) + self.dismissAnimated() + } + } + + func dismissBalanceOverlay() { + if let view = self.balanceOverlay.view, view.superview != nil { + view.alpha = 0.0 + view.layer.animateScale(from: 1.0, to: 0.8, duration: 0.4) + view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, completion: { _ in + view.removeFromSuperview() + view.alpha = 1.0 + }) + } + } + + public override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { + super.containerLayoutUpdated(layout, transition: transition) + + if self.showBalance { + let context = self.context + let insets = layout.insets(options: .statusBar) + let balanceSize = self.balanceOverlay.update( + transition: .immediate, + component: AnyComponent( + StarsBalanceOverlayComponent( + context: context, + peerId: context.account.peerId, + theme: context.sharedContext.currentPresentationData.with { $0 }.theme, + currency: .ton, + action: { [weak self] in + guard let self else { + return + } + var fragmentUrl = "https://fragment.com/ads/topup" + if let data = context.currentAppConfiguration.with({ $0 }).data, let value = data["ton_topup_url"] as? String { + fragmentUrl = value + } + context.sharedContext.applicationBindings.openUrl(fragmentUrl) + + self.dismissAnimated() + } + ) + ), + environment: {}, + containerSize: layout.size + ) + if let view = self.balanceOverlay.view { + if view.superview == nil { + self.view.addSubview(view) + + view.layer.animatePosition(from: CGPoint(x: 0.0, y: -64.0), to: .zero, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, additive: true) + view.layer.animateSpring(from: 0.8 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.5, initialVelocity: 0.0, removeOnCompletion: true, additive: false, completion: nil) + view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) + } + view.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - balanceSize.width) / 2.0), y: insets.top + 5.0), size: balanceSize) + } + } else if let view = self.balanceOverlay.view, view.superview != nil { + view.alpha = 0.0 + view.layer.animateScale(from: 1.0, to: 0.8, duration: 0.4) + view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, completion: { _ in + view.removeFromSuperview() + view.alpha = 1.0 + }) + } + } +} + +private final class AmountFieldStarsFormatter: NSObject, UITextFieldDelegate { + private let currency: CurrencyAmount.Currency + private let dateTimeFormat: PresentationDateTimeFormat + + private let textField: UITextField + private let minValue: Int64 + private let forceMinValue: Bool + private let allowZero: Bool + private let maxValue: Int64 + private let updated: (Int64) -> Void + private let isEmptyUpdated: (Bool) -> Void + private let animateError: () -> Void + private let focusUpdated: (Bool) -> Void + + init?(textField: UITextField, currency: CurrencyAmount.Currency, dateTimeFormat: PresentationDateTimeFormat, minValue: Int64, forceMinValue: Bool, allowZero: Bool, maxValue: Int64, updated: @escaping (Int64) -> Void, isEmptyUpdated: @escaping (Bool) -> Void, animateError: @escaping () -> Void, focusUpdated: @escaping (Bool) -> Void) { + self.textField = textField + self.currency = currency + self.dateTimeFormat = dateTimeFormat + self.minValue = minValue + self.forceMinValue = forceMinValue + self.allowZero = allowZero + self.maxValue = maxValue + self.updated = updated + self.isEmptyUpdated = isEmptyUpdated + self.animateError = animateError + self.focusUpdated = focusUpdated + + super.init() + } + + func amountFrom(text: String) -> Int64 { + var amount: Int64? + if !text.isEmpty { + switch self.currency { + case .stars: + if let value = Int64(text) { + amount = value + } + case .ton: + let scale: Int64 = 1_000_000_000 // 10⁹ (one “nano”) + if let decimalSeparator = self.dateTimeFormat.decimalSeparator.first, let dot = text.firstIndex(of: decimalSeparator) { + // Slices for the parts on each side of the dot + var wholeSlice = String(text[.. 9 { + fractionStr = String(fractionStr.prefix(9)) // trim extra digits + } else { + fractionStr = fractionStr.padding( + toLength: 9, withPad: "0", startingAt: 0) // pad with zeros + } + + // Convert and combine + if let whole = Int64(wholeSlice), + let frac = Int64(fractionStr) { + + let whole = min(whole, Int64.max / scale) + + amount = whole * scale + frac + } + } else if let whole = Int64(text) { // string had no dot at all + let whole = min(whole, Int64.max / scale) + + amount = whole * scale + } + } + } + return amount ?? 0 + } + + func onTextChanged(text: String) { + self.updated(self.amountFrom(text: text)) + self.isEmptyUpdated(text.isEmpty) + } + + func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { + var acceptZero = false + if case .ton = self.currency, self.minValue < 1_000_000_000 { + acceptZero = true + } + + var newText = ((textField.text ?? "") as NSString).replacingCharacters(in: range, with: string) + if newText.contains(where: { c in + switch c { + case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9": + return false + default: + if case .ton = self.currency { + if let decimalSeparator = self.dateTimeFormat.decimalSeparator.first, c == decimalSeparator { + return false + } + } + return true + } + }) { + return false + } + if let decimalSeparator = self.dateTimeFormat.decimalSeparator.first, newText.count(where: { $0 == decimalSeparator }) > 1 { + return false + } + + switch self.currency { + case .stars: + if (newText == "0" && !acceptZero) || (newText.count > 1 && newText.hasPrefix("0")) { + newText.removeFirst() + textField.text = newText + self.onTextChanged(text: newText) + return false + } + case .ton: + var fixedText = false + if let decimalSeparator = self.dateTimeFormat.decimalSeparator.first, let index = newText.firstIndex(of: decimalSeparator) { + let fractionalString = newText[newText.index(after: index)...] + if fractionalString.count > 2 { + newText = String(newText[newText.startIndex ..< newText.index(index, offsetBy: 3)]) + fixedText = true + } + } + + if newText == self.dateTimeFormat.decimalSeparator { + if !acceptZero { + newText.removeFirst() + } else { + newText = "0\(newText)" + } + fixedText = true + } + + if (newText == "0" && !acceptZero) || (newText.count > 1 && newText.hasPrefix("0") && !newText.hasPrefix("0\(self.dateTimeFormat.decimalSeparator)")) { + newText.removeFirst() + fixedText = true + } + + if fixedText { + textField.text = newText + self.onTextChanged(text: newText) + return false + } + } + + let amount: Int64 = self.amountFrom(text: newText) + if self.forceMinValue && amount < self.minValue { + switch self.currency { + case .stars: + textField.text = "\(self.minValue)" + case .ton: + textField.text = "\(formatTonAmountText(self.minValue, dateTimeFormat: PresentationDateTimeFormat(timeFormat: self.dateTimeFormat.timeFormat, dateFormat: self.dateTimeFormat.dateFormat, dateSeparator: "", dateSuffix: "", requiresFullYear: false, decimalSeparator: self.dateTimeFormat.decimalSeparator, groupingSeparator: ""), maxDecimalPositions: nil))" + } + self.onTextChanged(text: self.textField.text ?? "") + self.animateError() + return false + } else if amount > self.maxValue { + switch self.currency { + case .stars: + textField.text = "\(self.maxValue)" + case .ton: + textField.text = "\(formatTonAmountText(self.maxValue, dateTimeFormat: PresentationDateTimeFormat(timeFormat: self.dateTimeFormat.timeFormat, dateFormat: self.dateTimeFormat.dateFormat, dateSeparator: "", dateSuffix: "", requiresFullYear: false, decimalSeparator: self.dateTimeFormat.decimalSeparator, groupingSeparator: ""), maxDecimalPositions: nil))" + } + self.onTextChanged(text: self.textField.text ?? "") + self.animateError() + return false + } + + self.onTextChanged(text: newText) + + return true + } +} + +public final class AmountFieldComponent: Component { + public typealias EnvironmentType = Empty + + let textColor: UIColor + let secondaryColor: UIColor + let placeholderColor: UIColor + let accentColor: UIColor + let value: Int64? + let minValue: Int64? + let forceMinValue: Bool + let allowZero: Bool + let maxValue: Int64? + let placeholderText: String + let textFieldOffset: CGPoint + let labelText: String? + let currency: CurrencyAmount.Currency + let dateTimeFormat: PresentationDateTimeFormat + let amountUpdated: (Int64?) -> Void + let tag: AnyObject? + + public init( + textColor: UIColor, + secondaryColor: UIColor, + placeholderColor: UIColor, + accentColor: UIColor, + value: Int64?, + minValue: Int64?, + forceMinValue: Bool, + allowZero: Bool, + maxValue: Int64?, + placeholderText: String, + textFieldOffset: CGPoint = .zero, + labelText: String?, + currency: CurrencyAmount.Currency, + dateTimeFormat: PresentationDateTimeFormat, + amountUpdated: @escaping (Int64?) -> Void, + tag: AnyObject? = nil + ) { + self.textColor = textColor + self.secondaryColor = secondaryColor + self.placeholderColor = placeholderColor + self.accentColor = accentColor + self.value = value + self.minValue = minValue + self.forceMinValue = forceMinValue + self.allowZero = allowZero + self.maxValue = maxValue + self.placeholderText = placeholderText + self.textFieldOffset = textFieldOffset + self.labelText = labelText + self.currency = currency + self.dateTimeFormat = dateTimeFormat + self.amountUpdated = amountUpdated + self.tag = tag + } + + public static func ==(lhs: AmountFieldComponent, rhs: AmountFieldComponent) -> Bool { + if lhs.textColor != rhs.textColor { + return false + } + if lhs.secondaryColor != rhs.secondaryColor { + return false + } + if lhs.placeholderColor != rhs.placeholderColor { + return false + } + if lhs.accentColor != rhs.accentColor { + return false + } + if lhs.value != rhs.value { + return false + } + if lhs.minValue != rhs.minValue { + return false + } + if lhs.allowZero != rhs.allowZero { + return false + } + if lhs.maxValue != rhs.maxValue { + return false + } + if lhs.placeholderText != rhs.placeholderText { + return false + } + if lhs.labelText != rhs.labelText { + return false + } + if lhs.currency != rhs.currency { + return false + } + return true + } + + public final class View: UIView, ListSectionComponent.ChildView, UITextFieldDelegate, ComponentTaggedView { + public func matches(tag: Any) -> Bool { + if let component = self.component, let componentTag = component.tag { + let tag = tag as AnyObject + if componentTag === tag { + return true + } + } + return false + } + + private let placeholderView: ComponentView + private let icon = ComponentView() + private let textField: TextFieldNodeView + private var starsFormatter: AmountFieldStarsFormatter? + private var tonFormatter: AmountFieldStarsFormatter? + private let labelView: ComponentView + + private var component: AmountFieldComponent? + private weak var state: EmptyComponentState? + private var isUpdating: Bool = false + + private var didSetValueOnce = false + + public var customUpdateIsHighlighted: ((Bool) -> Void)? + public var enumerateSiblings: (((UIView) -> Void) -> Void)? + public let separatorInset: CGFloat = 16.0 + + public override init(frame: CGRect) { + self.placeholderView = ComponentView() + self.textField = TextFieldNodeView(frame: .zero) + self.labelView = ComponentView() + + super.init(frame: frame) + + self.addSubview(self.textField) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public func activateInput() { + self.textField.becomeFirstResponder() + } + + public func deactivateInput() { + self.textField.resignFirstResponder() + } + + public func selectAll() { + self.textField.selectAll(nil) + } + + public func animateError() { + self.textField.layer.addShakeAnimation() + let hapticFeedback = HapticFeedback() + hapticFeedback.error() + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1.0, execute: { + let _ = hapticFeedback + }) + } + + public func resetValue() { + guard let component = self.component, let value = component.value else { + return + } + var text = "" + switch component.currency { + case .stars: + text = "\(value)" + case .ton: + text = "\(formatTonAmountText(value, dateTimeFormat: PresentationDateTimeFormat(timeFormat: component.dateTimeFormat.timeFormat, dateFormat: component.dateTimeFormat.dateFormat, dateSeparator: "", dateSuffix: "", requiresFullYear: false, decimalSeparator: ".", groupingSeparator: ""), maxDecimalPositions: nil))" + } + self.textField.text = text + self.placeholderView.view?.isHidden = !(self.textField.text ?? "").isEmpty + } + + func update(component: AmountFieldComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.isUpdating = true + defer { + self.isUpdating = false + } + + self.textField.textColor = component.textColor + if self.component?.currency != component.currency || ((self.textField.text ?? "").isEmpty && !self.didSetValueOnce) { + if let value = component.value, value != .zero { + var text = "" + switch component.currency { + case .stars: + text = "\(value)" + case .ton: + text = "\(formatTonAmountText(value, dateTimeFormat: PresentationDateTimeFormat(timeFormat: component.dateTimeFormat.timeFormat, dateFormat: component.dateTimeFormat.dateFormat, dateSeparator: "", dateSuffix: "", requiresFullYear: false, decimalSeparator: ".", groupingSeparator: ""), maxDecimalPositions: nil))" + } + self.textField.text = text + self.placeholderView.view?.isHidden = !text.isEmpty + } else { + self.textField.text = "" + } + self.didSetValueOnce = true + } + self.textField.font = Font.regular(17.0) + + self.textField.returnKeyType = .done + self.textField.autocorrectionType = .no + self.textField.autocapitalizationType = .none + + if self.component?.currency != component.currency { + switch component.currency { + case .stars: + self.textField.delegate = self + self.textField.keyboardType = .numberPad + if self.starsFormatter == nil { + self.starsFormatter = AmountFieldStarsFormatter( + textField: self.textField, + currency: component.currency, + dateTimeFormat: component.dateTimeFormat, + minValue: component.minValue ?? 0, + forceMinValue: component.forceMinValue, + allowZero: component.allowZero, + maxValue: component.maxValue ?? Int64.max, + updated: { [weak self] value in + guard let self, let component = self.component else { + return + } + if !self.isUpdating { + component.amountUpdated(value == 0 ? nil : value) + } + }, + isEmptyUpdated: { [weak self] isEmpty in + guard let self else { + return + } + self.placeholderView.view?.isHidden = !isEmpty + }, + animateError: { [weak self] in + guard let self else { + return + } + self.animateError() + }, + focusUpdated: { _ in + } + ) + } + self.tonFormatter = nil + self.textField.delegate = self.starsFormatter + case .ton: + self.textField.keyboardType = .decimalPad + if self.tonFormatter == nil { + self.tonFormatter = AmountFieldStarsFormatter( + textField: self.textField, + currency: component.currency, + dateTimeFormat: component.dateTimeFormat, + minValue: component.minValue ?? 0, + forceMinValue: component.forceMinValue, + allowZero: component.allowZero, + maxValue: component.maxValue ?? Int64.max, + updated: { [weak self] value in + guard let self, let component = self.component else { + return + } + if !self.isUpdating { + component.amountUpdated(value == 0 ? nil : value) + } + }, + isEmptyUpdated: { [weak self] isEmpty in + guard let self else { + return + } + self.placeholderView.view?.isHidden = !isEmpty + }, + animateError: { [weak self] in + guard let self else { + return + } + self.animateError() + }, + focusUpdated: { _ in + } + ) + } + self.starsFormatter = nil + self.textField.delegate = self.tonFormatter + } + self.textField.reloadInputViews() + } + + self.component = component + self.state = state + + let size = CGSize(width: availableSize.width, height: 52.0) + + let sideInset: CGFloat = 16.0 + var leftInset: CGFloat = 16.0 + + let iconName: String + var iconTintColor: UIColor? + let iconMaxSize: CGSize? + var iconOffset = CGPoint() + switch component.currency { + case .stars: + iconName = "Premium/Stars/StarLarge" + iconMaxSize = CGSize(width: 22.0, height: 22.0) + case .ton: + iconName = "Ads/TonBig" + iconTintColor = component.accentColor + iconMaxSize = CGSize(width: 18.0, height: 18.0) + iconOffset = CGPoint(x: 3.0, y: 1.0) + } + let iconSize = self.icon.update( + transition: .immediate, + component: AnyComponent(BundleIconComponent( + name: iconName, + tintColor: iconTintColor, + maxSize: iconMaxSize + )), + environment: {}, + containerSize: CGSize(width: 100.0, height: 100.0) + ) + + if let iconView = self.icon.view { + if iconView.superview == nil { + self.addSubview(iconView) + } + iconView.frame = CGRect(origin: CGPoint(x: iconOffset.x + 15.0, y: iconOffset.y - 1.0 + floorToScreenPixels((size.height - iconSize.height) / 2.0)), size: iconSize) + } + + leftInset += 24.0 + 6.0 + + let placeholderSize = self.placeholderView.update( + transition: .easeInOut(duration: 0.2), + component: AnyComponent( + Text( + text: component.placeholderText, + font: Font.regular(17.0), + color: component.placeholderColor + ) + ), + environment: {}, + containerSize: availableSize + ) + + if let placeholderComponentView = self.placeholderView.view { + if placeholderComponentView.superview == nil { + self.insertSubview(placeholderComponentView, at: 0) + } + + placeholderComponentView.frame = CGRect(origin: CGPoint(x: leftInset, y: -1.0 + floorToScreenPixels((size.height - placeholderSize.height) / 2.0) + 1.0 - UIScreenPixel), size: placeholderSize) + placeholderComponentView.isHidden = !(self.textField.text ?? "").isEmpty + } + + if let labelText = component.labelText { + let labelSize = self.labelView.update( + transition: .immediate, + component: AnyComponent( + Text( + text: labelText, + font: Font.regular(17.0), + color: component.secondaryColor + ) + ), + environment: {}, + containerSize: availableSize + ) + + if let labelView = self.labelView.view { + if labelView.superview == nil { + self.insertSubview(labelView, at: 0) + } + + labelView.frame = CGRect(origin: CGPoint(x: size.width - sideInset - labelSize.width, y: floorToScreenPixels((size.height - labelSize.height) / 2.0) + 1.0 - UIScreenPixel), size: labelSize) + } + } else if let labelView = self.labelView.view, labelView.superview != nil { + labelView.removeFromSuperview() + } + + self.textField.frame = CGRect(x: leftInset + component.textFieldOffset.x, y: 4.0 + component.textFieldOffset.y, width: size.width - 30.0, height: 44.0) + + return size + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + +private final class ResultCellComponent: Component { + let context: AccountContext + let theme: PresentationTheme + let files: [TelegramMediaFile]? + let value: Int32 + let size: CGSize + + init( + context: AccountContext, + theme: PresentationTheme, + files: [TelegramMediaFile]?, + value: Int32, + size: CGSize + ) { + self.context = context + self.theme = theme + self.files = files + self.value = value + self.size = size + } + + static func ==(lhs: ResultCellComponent, rhs: ResultCellComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.theme !== rhs.theme { + return false + } + if lhs.files != rhs.files { + return false + } + if lhs.value != rhs.value { + return false + } + if lhs.size != rhs.size { + return false + } + return true + } + + final class View: UIView { + private var component: ResultCellComponent? + + private let background = ComponentView() + private let emoji = ComponentView() + private let title = ComponentView() + + override init(frame: CGRect) { + + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(component: ResultCellComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + + let size = component.size + + let backgroundSize = self.background.update( + transition: transition, + component: AnyComponent(FilledRoundedRectangleComponent(color: component.theme.list.itemBlocksBackgroundColor, cornerRadius: .value(22.0), smoothCorners: true)), + environment: {}, + containerSize: size + ) + let backgroundFrame = CGRect(origin: .zero, size: backgroundSize) + if let backgroundView = self.background.view { + if backgroundView.superview == nil { + self.addSubview(backgroundView) + } + transition.setFrame(view: backgroundView, frame: backgroundFrame) + } + + let value = Double(component.value) / 1000.0 + let titleString = String(format: "%0.1f", value).replacingOccurrences(of: ".0", with: "").replacingOccurrences(of: ",0", with: "") + + var items: [AnyComponentWithIdentity] = [] + if let files = component.files { + for file in files { + items.append(AnyComponentWithIdentity(id: items.count, component: AnyComponent( + LottieComponent( + content: LottieComponent.ResourceContent( + context: component.context, + file: file, + attemptSynchronously: true, + providesPlaceholder: true + ), + placeholderColor: component.theme.list.mediaPlaceholderColor, + startingPosition: .end, + size: CGSize(width: 50.0, height: 50.0), + loop: false + ) + ))) + } + } + let emojiSize = self.emoji.update( + transition: transition, + component: AnyComponent( + HStack(items, spacing: -18.0) + ), + environment: {}, + containerSize: availableSize + ) + let emojiFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - emojiSize.width) / 2.0), y: -9.0), size: emojiSize) + if let emojiView = self.emoji.view { + if emojiView.superview == nil { + self.addSubview(emojiView) + } + transition.setFrame(view: emojiView, frame: emojiFrame) + } + + let titleSize = self.title.update( + transition: transition, + component: AnyComponent( + MultilineTextComponent( + text: .plain(NSAttributedString(string: "×\(titleString)", font: Font.semibold(11.0), textColor: component.theme.list.itemPrimaryTextColor)), + horizontalAlignment: .center, + maximumNumberOfLines: 1 + ) + ), + environment: {}, + containerSize: availableSize + ) + let titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: size.height - titleSize.height - 10.0), size: titleSize) + if let titleView = self.title.view { + if titleView.superview == nil { + self.addSubview(titleView) + } + transition.setFrame(view: titleView, frame: titleFrame) + } + + return size + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + +private final class AmountPresetsListItemComponent: Component { + let context: AccountContext + let theme: PresentationTheme + let values: [Int64] + let valueSelected: (Int64) -> Void + + init( + context: AccountContext, + theme: PresentationTheme, + values: [Int64], + valueSelected: @escaping (Int64) -> Void + ) { + self.context = context + self.theme = theme + self.values = values + self.valueSelected = valueSelected + } + + static func ==(lhs: AmountPresetsListItemComponent, rhs: AmountPresetsListItemComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.theme !== rhs.theme { + return false + } + if lhs.values != rhs.values { + return false + } + return true + } + + final class View: UIView { + private var component: AmountPresetsListItemComponent? + + private var itemViews: [Int64: ComponentView] = [:] + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(component: AmountPresetsListItemComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + + + let sideInset: CGFloat = 16.0 + let spacing: CGFloat = 6.0 + let itemSize = CGSize(width: floorToScreenPixels((availableSize.width - sideInset * 2.0 - spacing * 2.0) / 3.0), height: 28.0) + + var itemOrigin = CGPoint(x: sideInset, y: sideInset) + for value in component.values { + let itemView: ComponentView + if let current = self.itemViews[value] { + itemView = current + } else { + itemView = ComponentView() + self.itemViews[value] = itemView + } + let _ = itemView.update( + transition: .immediate, + component: AnyComponent(PlainButtonComponent( + content: AnyComponent(AmountPresetComponent( + context: component.context, + theme: component.theme, + value: value + )), + action: { + component.valueSelected(value) + }, + animateScale: false + )), + environment: {}, + containerSize: itemSize + ) + var itemFrame = CGRect(origin: itemOrigin, size: itemSize) + if itemFrame.maxX > availableSize.width { + itemOrigin = CGPoint(x: sideInset, y: itemOrigin.y + itemSize.height + spacing) + itemFrame.origin = itemOrigin + } + if let itemView = itemView.view { + if itemView.superview == nil { + self.addSubview(itemView) + } + transition.setFrame(view: itemView, frame: itemFrame) + } + itemOrigin.x += itemSize.width + spacing + } + + let size = CGSize(width: availableSize.width, height: itemOrigin.y + itemSize.height + sideInset) + return size + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + +private final class AmountPresetComponent: Component { + let context: AccountContext + let theme: PresentationTheme + let value: Int64 + + init( + context: AccountContext, + theme: PresentationTheme, + value: Int64 + ) { + self.context = context + self.theme = theme + self.value = value + } + + static func ==(lhs: AmountPresetComponent, rhs: AmountPresetComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.theme !== rhs.theme { + return false + } + if lhs.value != rhs.value { + return false + } + return true + } + + final class View: UIView { + private var component: AmountPresetComponent? + + private let background = ComponentView() + private let title = ComponentView() + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(component: AmountPresetComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + + let size = availableSize + + let backgroundSize = self.background.update( + transition: transition, + component: AnyComponent(FilledRoundedRectangleComponent(color: component.theme.list.itemAccentColor.withMultipliedAlpha(0.1), cornerRadius: .minEdge, smoothCorners: false)), + environment: {}, + containerSize: size + ) + let backgroundFrame = CGRect(origin: .zero, size: backgroundSize) + if let backgroundView = self.background.view { + if backgroundView.superview == nil { + self.addSubview(backgroundView) + } + transition.setFrame(view: backgroundView, frame: backgroundFrame) + } + + let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } + + let attributedText = NSMutableAttributedString(string: "$ \(formatTonAmountText(component.value, dateTimeFormat: presentationData.dateTimeFormat))", font: Font.semibold(14.0), textColor: component.theme.list.itemAccentColor) + if let range = attributedText.string.range(of: "$") { + attributedText.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .ton(tinted: true)), range: NSRange(range, in: attributedText.string)) + attributedText.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: attributedText.string)) + } + + let titleSize = self.title.update( + transition: transition, + component: AnyComponent( + MultilineTextWithEntitiesComponent( + context: component.context, + animationCache: component.context.animationCache, + animationRenderer: component.context.animationRenderer, + placeholderColor: .clear, + text: .plain(attributedText) + ) + ), + environment: {}, + containerSize: availableSize + ) + let titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: floorToScreenPixels((size.height - titleSize.height) / 2.0)), size: titleSize) + if let titleView = self.title.view { + if titleView.superview == nil { + self.addSubview(titleView) + } + transition.setFrame(view: titleView, frame: titleFrame) + } + + return size + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + +private struct EmojiGameStakeConfiguration { + static var defaultValue: EmojiGameStakeConfiguration { + return EmojiGameStakeConfiguration( + tonUsdRate: nil, + minStakeAmount: nil, + maxStakeAmount: nil, + suggestedAmounts: [ + 100000000, + 1000000000, + 2000000000, + 5000000000, + 10000000000, + 20000000000 + ] + ) + } + + let tonUsdRate: Double? + let minStakeAmount: Int64? + let maxStakeAmount: Int64? + let suggestedAmounts: [Int64] + + fileprivate init( + tonUsdRate: Double?, + minStakeAmount: Int64?, + maxStakeAmount: Int64?, + suggestedAmounts: [Int64] + ) { + self.tonUsdRate = tonUsdRate + self.minStakeAmount = minStakeAmount + self.maxStakeAmount = maxStakeAmount + self.suggestedAmounts = suggestedAmounts + } + + static func with(appConfiguration: AppConfiguration) -> EmojiGameStakeConfiguration { + if let data = appConfiguration.data { + var tonUsdRate: Double? + if let value = data["ton_usd_rate"] as? Double { + tonUsdRate = value + } + + var minStakeAmount: Int64? + if let value = data["ton_stakedice_stake_amount_min"] as? Double { + minStakeAmount = Int64(value) + } + + var maxStakeAmount: Int64? + if let value = data["ton_stakedice_stake_amount_max"] as? Double { + maxStakeAmount = Int64(value) + } + + var suggestedAmounts: [Int64] = [] + if let value = data["ton_stakedice_stake_suggested_amounts"] as? [Double] { + suggestedAmounts = value.map { Int64($0) } + } else { + suggestedAmounts = EmojiGameStakeConfiguration.defaultValue.suggestedAmounts + } + + return EmojiGameStakeConfiguration( + tonUsdRate: tonUsdRate, + minStakeAmount: minStakeAmount, + maxStakeAmount: maxStakeAmount, + suggestedAmounts: suggestedAmounts + ) + } else { + return .defaultValue + } + } +} + + + + + + + + + + +public final class ResizableSheetComponentEnvironment: Equatable { + public let theme: PresentationTheme + public let statusBarHeight: CGFloat + public let safeInsets: UIEdgeInsets + public let metrics: LayoutMetrics + public let deviceMetrics: DeviceMetrics + public let isDisplaying: Bool + public let isCentered: Bool + public let regularMetricsSize: CGSize? + public let dismiss: (Bool) -> Void + + public init( + theme: PresentationTheme, + statusBarHeight: CGFloat, + safeInsets: UIEdgeInsets, + metrics: LayoutMetrics, + deviceMetrics: DeviceMetrics, + isDisplaying: Bool, + isCentered: Bool, + regularMetricsSize: CGSize?, + dismiss: @escaping (Bool) -> Void + ) { + self.theme = theme + self.statusBarHeight = statusBarHeight + self.safeInsets = safeInsets + self.metrics = metrics + self.deviceMetrics = deviceMetrics + self.isDisplaying = isDisplaying + self.isCentered = isCentered + self.regularMetricsSize = regularMetricsSize + self.dismiss = dismiss + } + + public static func ==(lhs: ResizableSheetComponentEnvironment, rhs: ResizableSheetComponentEnvironment) -> Bool { + if lhs.theme != rhs.theme { + return false + } + if lhs.statusBarHeight != rhs.statusBarHeight { + return false + } + if lhs.safeInsets != rhs.safeInsets { + return false + } + if lhs.metrics != rhs.metrics { + return false + } + if lhs.deviceMetrics != rhs.deviceMetrics { + return false + } + if lhs.isDisplaying != rhs.isDisplaying { + return false + } + if lhs.isCentered != rhs.isCentered { + return false + } + if lhs.regularMetricsSize != rhs.regularMetricsSize { + return false + } + return true + } +} + +public final class ResizableSheetComponent: Component { + public typealias EnvironmentType = (ChildEnvironmentType, ResizableSheetComponentEnvironment) + + public class ExternalState { + public fileprivate(set) var contentHeight: CGFloat + + public init() { + self.contentHeight = 0.0 + } + } + + public enum BackgroundColor: Equatable { + case color(UIColor) + } + + public let content: AnyComponent + public let titleItem: AnyComponent? + public let leftItem: AnyComponent? + public let rightItem: AnyComponent? + public let bottomItem: AnyComponent? + public let backgroundColor: BackgroundColor + public let externalState: ExternalState? + public let animateOut: ActionSlot> + + public init( + content: AnyComponent, + titleItem: AnyComponent? = nil, + leftItem: AnyComponent? = nil, + rightItem: AnyComponent? = nil, + bottomItem: AnyComponent? = nil, + backgroundColor: BackgroundColor, + externalState: ExternalState? = nil, + animateOut: ActionSlot>, + ) { + self.content = content + self.titleItem = titleItem + self.leftItem = leftItem + self.rightItem = rightItem + self.bottomItem = bottomItem + self.backgroundColor = backgroundColor + self.externalState = externalState + self.animateOut = animateOut + } + + public static func ==(lhs: ResizableSheetComponent, rhs: ResizableSheetComponent) -> Bool { + if lhs.content != rhs.content { + return false + } + if lhs.titleItem != rhs.titleItem { + return false + } + if lhs.leftItem != rhs.leftItem { + return false + } + if lhs.rightItem != rhs.rightItem { + return false + } + if lhs.bottomItem != rhs.bottomItem { + return false + } + if lhs.backgroundColor != rhs.backgroundColor { + return false + } + if lhs.animateOut != rhs.animateOut { + return false + } + return true + } + + private struct ItemLayout: Equatable { + var containerSize: CGSize + var containerInset: CGFloat + var containerCornerRadius: CGFloat + var bottomInset: CGFloat + var topInset: CGFloat + + init(containerSize: CGSize, containerInset: CGFloat, containerCornerRadius: CGFloat, bottomInset: CGFloat, topInset: CGFloat) { + self.containerSize = containerSize + self.containerInset = containerInset + self.containerCornerRadius = containerCornerRadius + self.bottomInset = bottomInset + self.topInset = topInset + } + } + + private final class ScrollView: UIScrollView { + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + return super.hitTest(point, with: event) + } + } + + public final class View: UIView, UIScrollViewDelegate, ComponentTaggedView { + public final class Tag { + public init() { + } + } + + public func matches(tag: Any) -> Bool { + if let _ = tag as? Tag { + return true + } + return false + } + + private let dimView: UIView + private let containerView: UIView + private let backgroundLayer: SimpleLayer + private let navigationBarContainer: SparseContainerView + private let scrollView: ScrollView + private let scrollContentClippingView: SparseContainerView + private let scrollContentView: UIView + + private let topEdgeEffectView: EdgeEffectView + private let contentView: ComponentView + + private var titleItemView: ComponentView? + private var leftItemView: ComponentView? + private var rightItemView: ComponentView? + private var bottomItemView: ComponentView? + + private let backgroundHandleView: UIImageView + + private var ignoreScrolling: Bool = false + + private var component: ResizableSheetComponent? + private weak var state: EmptyComponentState? + private var isUpdating: Bool = false + private var environment: ResizableSheetComponentEnvironment? + private var itemLayout: ItemLayout? + + override init(frame: CGRect) { + self.dimView = UIView() + self.containerView = UIView() + + self.containerView.clipsToBounds = true + self.containerView.layer.cornerRadius = 40.0 + self.containerView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner] + + self.backgroundLayer = SimpleLayer() + self.backgroundLayer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] + self.backgroundLayer.cornerRadius = 40.0 + + self.backgroundHandleView = UIImageView() + + self.navigationBarContainer = SparseContainerView() + + self.scrollView = ScrollView() + + self.scrollContentClippingView = SparseContainerView() + self.scrollContentClippingView.clipsToBounds = true + + self.scrollContentView = UIView() + + self.topEdgeEffectView = EdgeEffectView() + self.topEdgeEffectView.clipsToBounds = true + self.topEdgeEffectView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] + self.topEdgeEffectView.layer.cornerRadius = 40.0 + + self.contentView = ComponentView() + + super.init(frame: frame) + + self.addSubview(self.dimView) + self.addSubview(self.containerView) + self.containerView.layer.addSublayer(self.backgroundLayer) + + self.scrollView.delaysContentTouches = true + self.scrollView.canCancelContentTouches = true + self.scrollView.clipsToBounds = false + self.scrollView.contentInsetAdjustmentBehavior = .never + self.scrollView.automaticallyAdjustsScrollIndicatorInsets = false + self.scrollView.showsVerticalScrollIndicator = false + self.scrollView.showsHorizontalScrollIndicator = false + self.scrollView.alwaysBounceHorizontal = false + self.scrollView.alwaysBounceVertical = true + self.scrollView.scrollsToTop = false + self.scrollView.delegate = self + self.scrollView.clipsToBounds = true + + self.containerView.addSubview(self.scrollContentClippingView) + self.scrollContentClippingView.addSubview(self.scrollView) + + self.scrollView.addSubview(self.scrollContentView) + + self.containerView.addSubview(self.navigationBarContainer) + + self.dimView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:)))) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public func scrollViewDidScroll(_ scrollView: UIScrollView) { + if !self.ignoreScrolling { + self.updateScrolling(transition: .immediate) + } + } + + public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if !self.bounds.contains(point) { + return nil + } + if !self.backgroundLayer.frame.contains(point) { + return self.dimView + } + + if let result = self.navigationBarContainer.hitTest(self.convert(point, to: self.navigationBarContainer), with: event) { + return result + } + let result = super.hitTest(point, with: event) + return result + } + + @objc private func dimTapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + self.dismissAnimated() + } + } + + func dismissAnimated() { + guard let environment = self.environment else { + return + } + self.endEditing(true) + environment.dismiss(true) + } + + private func updateScrolling(transition: ComponentTransition) { + guard let itemLayout = self.itemLayout else { + return + } + var topOffset = -self.scrollView.bounds.minY + itemLayout.topInset + topOffset = max(0.0, topOffset) + transition.setTransform(layer: self.backgroundLayer, transform: CATransform3DMakeTranslation(0.0, topOffset + itemLayout.containerInset, 0.0)) + + transition.setPosition(view: self.navigationBarContainer, position: CGPoint(x: 0.0, y: topOffset + itemLayout.containerInset)) + + var topOffsetFraction = self.scrollView.bounds.minY / 100.0 + topOffsetFraction = max(0.0, min(1.0, topOffsetFraction)) + + let minScale: CGFloat = (itemLayout.containerSize.width - 6.0 * 2.0) / itemLayout.containerSize.width + let minScaledTranslation: CGFloat = (itemLayout.containerSize.height - itemLayout.containerSize.height * minScale) * 0.5 - 6.0 + let minScaledCornerRadius: CGFloat = itemLayout.containerCornerRadius + + let scale = minScale * (1.0 - topOffsetFraction) + 1.0 * topOffsetFraction + let scaledTranslation = minScaledTranslation * (1.0 - topOffsetFraction) + let scaledCornerRadius = minScaledCornerRadius * (1.0 - topOffsetFraction) + itemLayout.containerCornerRadius * topOffsetFraction + + var containerTransform = CATransform3DIdentity + containerTransform = CATransform3DTranslate(containerTransform, 0.0, scaledTranslation, 0.0) + containerTransform = CATransform3DScale(containerTransform, scale, scale, scale) + transition.setTransform(view: self.containerView, transform: containerTransform) + transition.setCornerRadius(layer: self.containerView.layer, cornerRadius: scaledCornerRadius) + } + + private var didPlayAppearanceAnimation = false + func animateIn() { + self.didPlayAppearanceAnimation = true + + self.dimView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + let animateOffset: CGFloat = self.bounds.height - self.backgroundLayer.frame.minY + self.scrollContentClippingView.layer.animatePosition(from: CGPoint(x: 0.0, y: animateOffset), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true) + self.backgroundLayer.animatePosition(from: CGPoint(x: 0.0, y: animateOffset), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true) + self.navigationBarContainer.layer.animatePosition(from: CGPoint(x: 0.0, y: animateOffset), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true) + } + + func animateOut(completion: @escaping () -> Void) { + let animateOffset: CGFloat = self.bounds.height - self.backgroundLayer.frame.minY + + self.dimView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false) + self.scrollContentClippingView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: animateOffset), duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true, completion: { _ in + completion() + }) + self.backgroundLayer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: animateOffset), duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true) + self.navigationBarContainer.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: animateOffset), duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true) + } + + func update(component: ResizableSheetComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.isUpdating = true + defer { + self.isUpdating = false + } + + let sheetEnvironment = environment[ResizableSheetComponentEnvironment.self].value + component.animateOut.connect { [weak self] completion in + guard let self else { + return + } + self.animateOut { + completion(Void()) + } + } + + let themeUpdated = self.environment?.theme !== sheetEnvironment.theme + + let resetScrolling = self.scrollView.bounds.width != availableSize.width + + let fillingSize: CGFloat + if case .regular = sheetEnvironment.metrics.widthClass { + fillingSize = min(availableSize.width, 414.0) - sheetEnvironment.safeInsets.left * 2.0 + } else { + fillingSize = min(availableSize.width, sheetEnvironment.deviceMetrics.screenSize.width) - sheetEnvironment.safeInsets.left * 2.0 + } + let rawSideInset: CGFloat = floor((availableSize.width - fillingSize) * 0.5) + + self.component = component + self.state = state + self.environment = sheetEnvironment + + let theme = sheetEnvironment.theme.withModalBlocksBackground() + if themeUpdated { + self.dimView.backgroundColor = UIColor(white: 0.0, alpha: 0.5) + self.backgroundLayer.backgroundColor = theme.list.blocksBackgroundColor.cgColor + } + + transition.setFrame(view: self.dimView, frame: CGRect(origin: CGPoint(), size: availableSize)) + + var containerSize: CGSize + if !"".isEmpty, sheetEnvironment.isCentered { + let verticalInset: CGFloat = 44.0 + let maxSide = max(availableSize.width, availableSize.height) + let minSide = min(availableSize.width, availableSize.height) + containerSize = CGSize(width: min(availableSize.width - 20.0, floor(maxSide / 2.0)), height: min(availableSize.height, minSide) - verticalInset * 2.0) + if let regularMetricsSize = sheetEnvironment.regularMetricsSize { + containerSize = regularMetricsSize + } + } else { + containerSize = CGSize(width: fillingSize, height: .greatestFiniteMagnitude) + } + + let containerInset: CGFloat = sheetEnvironment.statusBarHeight + 10.0 + let clippingY: CGFloat + + self.contentView.parentState = state + let contentViewSize = self.contentView.update( + transition: transition, + component: component.content, + environment: { + environment[ChildEnvironmentType.self] + }, + containerSize: containerSize + ) + component.externalState?.contentHeight = contentViewSize.height + + if let contentView = self.contentView.view { + if contentView.superview == nil { + self.scrollContentView.addSubview(contentView) + } + transition.setFrame(view: contentView, frame: CGRect(origin: CGPoint(x: rawSideInset, y: 0.0), size: contentViewSize)) + } + + let contentHeight = contentViewSize.height + let initialContentHeight = contentHeight + + let edgeEffectHeight: CGFloat = 80.0 + let edgeEffectFrame = CGRect(origin: CGPoint(x: rawSideInset, y: 0.0), size: CGSize(width: fillingSize, height: edgeEffectHeight)) + transition.setFrame(view: self.topEdgeEffectView, frame: edgeEffectFrame) + self.topEdgeEffectView.update(content: theme.actionSheet.opaqueItemBackgroundColor, blur: true, alpha: 1.0, rect: edgeEffectFrame, edge: .top, edgeSize: edgeEffectFrame.height, transition: transition) + if self.topEdgeEffectView.superview == nil { + self.navigationBarContainer.insertSubview(self.topEdgeEffectView, at: 0) + } + + if let titleItem = component.titleItem { + let titleItemView: ComponentView + if let current = self.titleItemView { + titleItemView = current + } else { + titleItemView = ComponentView() + self.titleItemView = titleItemView + } + + let titleItemSize = titleItemView.update( + transition: transition, + component: titleItem, + environment: {}, + containerSize: CGSize(width: containerSize.width - 66.0 * 2.0, height: 66.0) + ) + let titleItemFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((containerSize.width - titleItemSize.width)) / 2.0, y: floorToScreenPixels(36.0 - titleItemSize.height * 0.5)), size: titleItemSize) + if let view = titleItemView.view { + if view.superview == nil { + self.navigationBarContainer.addSubview(view) + } + transition.setFrame(view: view, frame: titleItemFrame) + } + } else if let titleItemView = self.titleItemView { + self.titleItemView = nil + titleItemView.view?.removeFromSuperview() + } + + + if let leftItem = component.leftItem { + let leftItemView: ComponentView + if let current = self.leftItemView { + leftItemView = current + } else { + leftItemView = ComponentView() + self.leftItemView = leftItemView + } + + let leftItemSize = leftItemView.update( + transition: transition, + component: leftItem, + environment: {}, + containerSize: CGSize(width: 66.0, height: 66.0) + ) + let leftItemFrame = CGRect(origin: CGPoint(x: 16.0, y: 16.0), size: leftItemSize) + if let view = leftItemView.view { + if view.superview == nil { + self.navigationBarContainer.addSubview(view) + } + transition.setFrame(view: view, frame: leftItemFrame) + } + } else if let leftItemView = self.leftItemView { + self.leftItemView = nil + leftItemView.view?.removeFromSuperview() + } + + if let rightItem = component.rightItem { + let rightItemView: ComponentView + if let current = self.rightItemView { + rightItemView = current + } else { + rightItemView = ComponentView() + self.rightItemView = rightItemView + } + + let rightItemSize = rightItemView.update( + transition: transition, + component: rightItem, + environment: {}, + containerSize: CGSize(width: 66.0, height: 66.0) + ) + let rightItemFrame = CGRect(origin: CGPoint(x: containerSize.width - 16.0 - rightItemSize.width, y: 16.0), size: rightItemSize) + if let view = rightItemView.view { + if view.superview == nil { + self.navigationBarContainer.addSubview(view) + } + transition.setFrame(view: view, frame: rightItemFrame) + } + } else if let rightItemView = self.rightItemView { + self.rightItemView = nil + rightItemView.view?.removeFromSuperview() + } + + + clippingY = availableSize.height + + let topInset: CGFloat = max(0.0, availableSize.height - containerInset - initialContentHeight) + + let scrollContentHeight = max(topInset + contentHeight + containerInset, availableSize.height - containerInset) + + self.scrollContentClippingView.layer.cornerRadius = 38.0 + + self.itemLayout = ItemLayout(containerSize: availableSize, containerInset: containerInset, containerCornerRadius: sheetEnvironment.deviceMetrics.screenCornerRadius, bottomInset: sheetEnvironment.safeInsets.bottom, topInset: topInset) + + transition.setFrame(view: self.scrollContentView, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset + containerInset), size: CGSize(width: availableSize.width, height: contentHeight))) + + transition.setPosition(layer: self.backgroundLayer, position: CGPoint(x: availableSize.width / 2.0, y: availableSize.height / 2.0)) + transition.setBounds(layer: self.backgroundLayer, bounds: CGRect(origin: CGPoint(), size: CGSize(width: fillingSize, height: availableSize.height))) + + let scrollClippingFrame = CGRect(origin: CGPoint(x: 0.0, y: containerInset), size: CGSize(width: availableSize.width, height: clippingY - containerInset)) + transition.setPosition(view: self.scrollContentClippingView, position: scrollClippingFrame.center) + transition.setBounds(view: self.scrollContentClippingView, bounds: CGRect(origin: CGPoint(x: scrollClippingFrame.minX, y: scrollClippingFrame.minY), size: scrollClippingFrame.size)) + + self.ignoreScrolling = true + transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: availableSize.height))) + let contentSize = CGSize(width: availableSize.width, height: scrollContentHeight) + if contentSize != self.scrollView.contentSize { + self.scrollView.contentSize = contentSize + } + if resetScrolling { + self.scrollView.bounds = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: availableSize) + } + self.ignoreScrolling = false + self.updateScrolling(transition: transition) + + transition.setPosition(view: self.containerView, position: CGRect(origin: CGPoint(), size: availableSize).center) + transition.setBounds(view: self.containerView, bounds: CGRect(origin: CGPoint(), size: availableSize)) + + if sheetEnvironment.isDisplaying && !self.didPlayAppearanceAnimation { + self.animateIn() + } + + return availableSize + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift b/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift index 8bcb4abb..344e3fc4 100644 --- a/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift +++ b/submodules/TelegramUI/Components/EmojiTextAttachmentView/Sources/EmojiTextAttachmentView.swift @@ -19,6 +19,8 @@ import TelegramUIPreferences import GenerateStickerPlaceholderImage import UIKitRuntimeUtils import ComponentFlow +import RLottieBinding +import GZip public func generateTopicIcon(title: String, backgroundColors: [UIColor], strokeColors: [UIColor], size: CGSize) -> UIImage? { let realSize = size @@ -497,6 +499,10 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { case .verification: self.updateVerification() self.updateTintColor() + case .dice: + if let file { + self.updateDice(file: file, attemptSynchronousLoad: attemptSynchronousLoad) + } } } else if let file = file { self.updateFile(file: file, attemptSynchronousLoad: attemptSynchronousLoad) @@ -720,6 +726,44 @@ public final class InlineStickerItemLayer: MultiAnimationRenderTarget { } } + private func updateDice(file: TelegramMediaFile, attemptSynchronousLoad: Bool) { + guard let arguments = self.arguments else { + return + } + let _ = (arguments.context.postbox.mediaBox.resourceData(file.resource) + |> filter { resource in + return resource.complete + } + |> map { resource -> UIImage? in + guard var data = try? Data(contentsOf: URL(fileURLWithPath: resource.path)) else { + return nil + } + if let unpackedData = TGGUnzipData(data, 5 * 1024 * 1024) { + data = unpackedData + } + guard let instance = LottieInstance(data: data, fitzModifier: .none, colorReplacements: nil, cacheKey: "") else { + return nil + } + let size = CGSize(width: 128.0, height: 128.0) + if let diceContext = DrawingContext(size: size, scale: 1.0, opaque: false, clear: true) { + instance.renderFrame(with: instance.frameCount - 1, into: diceContext.bytes.assumingMemoryBound(to: UInt8.self), width: Int32(diceContext.scaledSize.width), height: Int32(diceContext.scaledSize.height), bytesPerRow: Int32(diceContext.bytesPerRow)) + if let diceImage = diceContext.generateImage() { + let drawingContext = DrawingContext(size: size, scale: 1.0, opaque: false, clear: true) + drawingContext?.withFlippedContext { context in + if let cgImage = diceImage.cgImage { + context.draw(cgImage, in: CGRect(origin: CGPoint(x: -30.0, y: 5.0), size: CGSize(width: 180.0, height: 180.0))) + } + } + return drawingContext?.generateImage() + } + } + return nil + } + |> deliverOnMainQueue).start(next: { image in + self.contents = image?.cgImage + }) + } + private func loadLocalAnimation() { guard let arguments = self.arguments else { return diff --git a/submodules/TelegramUI/Components/EntityKeyboard/BUILD b/submodules/TelegramUI/Components/EntityKeyboard/BUILD index 4aa7f411..0ca5c0ae 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/BUILD +++ b/submodules/TelegramUI/Components/EntityKeyboard/BUILD @@ -53,6 +53,9 @@ swift_library( "//submodules/TelegramUI/Components/BatchVideoRendering", "//submodules/TelegramUI/Components/GifVideoLayer", "//submodules/TelegramUI/Components/GlassBackgroundComponent", + "//submodules/TelegramUI/Components/EdgeEffect", + "//submodules/TelegramUI/Components/LiquidLens", + "//submodules/TelegramUI/Components/TabSelectionRecognizer", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiKeyboardItemLayer.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiKeyboardItemLayer.swift index 5028d618..2762c21f 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiKeyboardItemLayer.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiKeyboardItemLayer.swift @@ -419,13 +419,13 @@ public final class EmojiKeyboardItemLayer: MultiAnimationRenderTarget { context.setFillColor(color.withMultipliedAlpha(0.2).cgColor) - context.addPath(UIBezierPath(roundedRect: CGRect(origin: .zero, size: size), cornerRadius: 21.0).cgPath) + context.addPath(UIBezierPath(roundedRect: CGRect(origin: .zero, size: size), cornerRadius: 30.0).cgPath) context.fillPath() context.setFillColor(color.cgColor) - let plusSize = CGSize(width: 3.5, height: 28.0) - context.addPath(UIBezierPath(roundedRect: CGRect(x: floorToScreenPixels((size.width - plusSize.width) / 2.0), y: floorToScreenPixels((size.height - plusSize.height) / 2.0), width: plusSize.width, height: plusSize.height).offsetBy(dx: 0.0, dy: -17.0), cornerRadius: plusSize.width / 2.0).cgPath) - context.addPath(UIBezierPath(roundedRect: CGRect(x: floorToScreenPixels((size.width - plusSize.height) / 2.0), y: floorToScreenPixels((size.height - plusSize.width) / 2.0), width: plusSize.height, height: plusSize.width).offsetBy(dx: 0.0, dy: -17.0), cornerRadius: plusSize.width / 2.0).cgPath) + let plusSize = CGSize(width: 4.0, height: 27.0) + context.addPath(UIBezierPath(roundedRect: CGRect(x: floorToScreenPixels((size.width - plusSize.width) / 2.0), y: floorToScreenPixels((size.height - plusSize.height) / 2.0), width: plusSize.width, height: plusSize.height).offsetBy(dx: 0.0, dy: -18.0), cornerRadius: plusSize.width / 2.0).cgPath) + context.addPath(UIBezierPath(roundedRect: CGRect(x: floorToScreenPixels((size.width - plusSize.height) / 2.0), y: floorToScreenPixels((size.height - plusSize.width) / 2.0), width: plusSize.height, height: plusSize.width).offsetBy(dx: 0.0, dy: -18.0), cornerRadius: plusSize.width / 2.0).cgPath) context.fillPath() context.translateBy(x: size.width / 2.0, y: size.height / 2.0) @@ -437,7 +437,7 @@ public final class EmojiKeyboardItemLayer: MultiAnimationRenderTarget { let components = string.components(separatedBy: "\n") for component in components { context.saveGState() - let attributedString = NSAttributedString(string: component, attributes: [NSAttributedString.Key.font: Font.medium(17.0), NSAttributedString.Key.foregroundColor: color]) + let attributedString = NSAttributedString(string: component, attributes: [NSAttributedString.Key.font: Font.medium(16.0), NSAttributedString.Key.foregroundColor: color]) let line = CTLineCreateWithAttributedString(attributedString) let lineBounds = CTLineGetBoundsWithOptions(line, .useGlyphPathBounds) diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift index 3f94209d..2ab6b15e 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift @@ -4026,7 +4026,7 @@ public final class EmojiPagerContentComponent: Component { private func updateTopPanelSeparator(transition: ComponentTransition) { if let topPanelSeparator = self.topPanelSeparator { var offset = self.scrollView.contentOffset.y - let startOffset: CGFloat = 40.0 - self.topPanelHeight + let startOffset: CGFloat = 46.0 - self.topPanelHeight let endOffset: CGFloat = startOffset + 10.0 offset = min(max(offset, startOffset), endOffset) diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift index aa672304..12ec0cfb 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift @@ -403,7 +403,7 @@ public final class EntityKeyboardComponent: Component { if let _ = component.maskContent?.inputInteractionHolder.inputInteraction?.openStickerSettings { contentAccessoryRightButtons.append(AnyComponentWithIdentity(id: "masks", component: AnyComponent(EntityKeyboardBottomPanelButton( icon: "Chat/Input/Media/EntityInputSettingsIcon", - color: component.theme.chat.inputPanel.inputControlColor, + theme: component.theme, action: { maskContent.inputInteractionHolder.inputInteraction?.openStickerSettings?() } @@ -417,7 +417,7 @@ public final class EntityKeyboardComponent: Component { if let addImage = component.stickerContent?.inputInteractionHolder.inputInteraction?.addImage { contentAccessoryLeftButtons.append(AnyComponentWithIdentity(id: "gifs", component: AnyComponent(EntityKeyboardBottomPanelButton( icon: "Media Editor/AddImage", - color: component.theme.chat.inputPanel.inputControlColor, + theme: component.theme, action: { addImage() } @@ -537,7 +537,7 @@ public final class EntityKeyboardComponent: Component { if let _ = component.stickerContent?.inputInteractionHolder.inputInteraction?.openStickerSettings { contentAccessoryRightButtons.append(AnyComponentWithIdentity(id: "stickers", component: AnyComponent(EntityKeyboardBottomPanelButton( icon: "Chat/Input/Media/EntityInputSettingsIcon", - color: component.theme.chat.inputPanel.inputControlColor, + theme: component.theme, action: { stickerContent.inputInteractionHolder.inputInteraction?.openStickerSettings?() } @@ -546,7 +546,7 @@ public final class EntityKeyboardComponent: Component { if let addImage = component.stickerContent?.inputInteractionHolder.inputInteraction?.addImage { contentAccessoryLeftButtons.append(AnyComponentWithIdentity(id: "stickers", component: AnyComponent(EntityKeyboardBottomPanelButton( icon: "Media Editor/AddImage", - color: component.theme.chat.inputPanel.inputControlColor, + theme: component.theme, action: { addImage() } @@ -649,7 +649,7 @@ public final class EntityKeyboardComponent: Component { if let _ = deleteBackwards { contentAccessoryLeftButtons.append(AnyComponentWithIdentity(id: "emoji", component: AnyComponent(EntityKeyboardBottomPanelButton( icon: "Chat/Input/Media/EntityInputGlobeIcon", - color: component.theme.chat.inputPanel.inputControlColor, + theme: component.theme, action: { [weak self] in guard let strongSelf = self, let component = strongSelf.component else { return @@ -660,7 +660,7 @@ public final class EntityKeyboardComponent: Component { } else if let addImage = component.emojiContent?.inputInteractionHolder.inputInteraction?.addImage { contentAccessoryLeftButtons.append(AnyComponentWithIdentity(id: "emoji", component: AnyComponent(EntityKeyboardBottomPanelButton( icon: "Media Editor/AddImage", - color: component.theme.chat.inputPanel.inputControlColor, + theme: component.theme, action: { addImage() } @@ -671,7 +671,7 @@ public final class EntityKeyboardComponent: Component { if let _ = deleteBackwards { contentAccessoryRightButtons.append(AnyComponentWithIdentity(id: "emoji", component: AnyComponent(EntityKeyboardBottomPanelButton( icon: "Chat/Input/Media/EntityInputClearIcon", - color: component.theme.chat.inputPanel.inputControlColor, + theme: component.theme, action: { deleteBackwards?() AudioServicesPlaySystemSound(1155) @@ -721,7 +721,8 @@ public final class EntityKeyboardComponent: Component { topPanel: AnyComponent(EntityKeyboardTopContainerPanelComponent( theme: component.theme, overflowHeight: component.hiddenInputHeight, - topInset: component.externalTopPanelContainer == nil ? 6.0 : 0.0, + topInset: component.externalTopPanelContainer == nil ? 8.0 : 0.0, + height: component.externalTopPanelContainer == nil ? 40.0 : 34.0, displayBackground: component.externalTopPanelContainer != nil ? .none : component.displayTopPanelBackground )), externalTopPanelContainer: component.externalTopPanelContainer, diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardBottomPanelButton.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardBottomPanelButton.swift index faa4d87d..c73235e7 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardBottomPanelButton.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardBottomPanelButton.swift @@ -2,6 +2,7 @@ import Foundation import UIKit import Display import ComponentFlow +import TelegramPresentationData import PagerComponent import ComponentDisplayAdapters import BundleIconComponent @@ -10,18 +11,18 @@ import AppBundle final class EntityKeyboardBottomPanelButton: Component { let icon: String - let color: UIColor + let theme: PresentationTheme let action: () -> Void let holdAction: (() -> Void)? init( icon: String, - color: UIColor, + theme: PresentationTheme, action: @escaping () -> Void, holdAction: (() -> Void)? = nil ) { self.icon = icon - self.color = color + self.theme = theme self.action = action self.holdAction = holdAction } @@ -30,7 +31,7 @@ final class EntityKeyboardBottomPanelButton: Component { if lhs.icon != rhs.icon { return false } - if lhs.color != rhs.color { + if lhs.theme !== rhs.theme { return false } if (lhs.holdAction == nil) != (rhs.holdAction == nil) { @@ -39,7 +40,9 @@ final class EntityKeyboardBottomPanelButton: Component { return true } - final class View: HighlightTrackingButton { + final class View: UIView { + private let backgroundView: GlassBackgroundView + let buttonView: HighlightTrackingButton let iconView: GlassBackgroundView.ContentImageView let tintMaskContainer: UIView @@ -48,15 +51,19 @@ final class EntityKeyboardBottomPanelButton: Component { var component: EntityKeyboardBottomPanelButton? - private var currentIsHighlighted: Bool = false { - didSet { - if self.currentIsHighlighted != oldValue { - self.updateAlpha(transition: .immediate) - } - } - } +// private var currentIsHighlighted: Bool = false { +// didSet { +// if self.currentIsHighlighted != oldValue { +// self.updateAlpha(transition: .immediate) +// } +// } +// } override init(frame: CGRect) { + self.backgroundView = GlassBackgroundView() + + self.buttonView = HighlightTrackingButton() + self.iconView = GlassBackgroundView.ContentImageView() self.iconView.isUserInteractionEnabled = false @@ -65,9 +72,11 @@ final class EntityKeyboardBottomPanelButton: Component { super.init(frame: frame) - self.addSubview(self.iconView) + self.addSubview(self.backgroundView) + self.backgroundView.contentView.addSubview(self.iconView) + self.backgroundView.contentView.addSubview(self.buttonView) - self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside) + self.buttonView.addTarget(self, action: #selector(self.pressed), for: .touchUpInside) } required init?(coder: NSCoder) { @@ -86,65 +95,65 @@ final class EntityKeyboardBottomPanelButton: Component { } } - override public func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { - self.currentIsHighlighted = true - - self.holdActionTriggerred = false - - if self.component?.holdAction != nil { - self.holdActionTriggerred = true - self.component?.action() - - self.holdActionTimer?.invalidate() - let holdActionTimer = Timer(timeInterval: 0.5, repeats: false, block: { [weak self] _ in - guard let strongSelf = self else { - return - } - strongSelf.holdActionTimer?.invalidate() - strongSelf.component?.holdAction?() - strongSelf.beginExecuteHoldActionTimer() - }) - self.holdActionTimer = holdActionTimer - RunLoop.main.add(holdActionTimer, forMode: .common) - } - - return super.beginTracking(touch, with: event) - } +// override public func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { +// self.currentIsHighlighted = true +// +// self.holdActionTriggerred = false +// +// if self.component?.holdAction != nil { +// self.holdActionTriggerred = true +// self.component?.action() +// +// self.holdActionTimer?.invalidate() +// let holdActionTimer = Timer(timeInterval: 0.5, repeats: false, block: { [weak self] _ in +// guard let strongSelf = self else { +// return +// } +// strongSelf.holdActionTimer?.invalidate() +// strongSelf.component?.holdAction?() +// strongSelf.beginExecuteHoldActionTimer() +// }) +// self.holdActionTimer = holdActionTimer +// RunLoop.main.add(holdActionTimer, forMode: .common) +// } +// +// return super.beginTracking(touch, with: event) +// } - private func beginExecuteHoldActionTimer() { - self.holdActionTimer?.invalidate() - let holdActionTimer = Timer(timeInterval: 0.1, repeats: true, block: { [weak self] _ in - guard let strongSelf = self else { - return - } - strongSelf.component?.holdAction?() - }) - self.holdActionTimer = holdActionTimer - RunLoop.main.add(holdActionTimer, forMode: .common) - } +// private func beginExecuteHoldActionTimer() { +// self.holdActionTimer?.invalidate() +// let holdActionTimer = Timer(timeInterval: 0.1, repeats: true, block: { [weak self] _ in +// guard let strongSelf = self else { +// return +// } +// strongSelf.component?.holdAction?() +// }) +// self.holdActionTimer = holdActionTimer +// RunLoop.main.add(holdActionTimer, forMode: .common) +// } - override public func endTracking(_ touch: UITouch?, with event: UIEvent?) { - self.currentIsHighlighted = false - - self.holdActionTimer?.invalidate() - self.holdActionTimer = nil - - super.endTracking(touch, with: event) - } - - override public func cancelTracking(with event: UIEvent?) { - self.currentIsHighlighted = false - - self.holdActionTimer?.invalidate() - self.holdActionTimer = nil - - super.cancelTracking(with: event) - } +// override public func endTracking(_ touch: UITouch?, with event: UIEvent?) { +// self.currentIsHighlighted = false +// +// self.holdActionTimer?.invalidate() +// self.holdActionTimer = nil +// +// super.endTracking(touch, with: event) +// } +// +// override public func cancelTracking(with event: UIEvent?) { +// self.currentIsHighlighted = false +// +// self.holdActionTimer?.invalidate() +// self.holdActionTimer = nil +// +// super.cancelTracking(with: event) +// } - private func updateAlpha(transition: ComponentTransition) { - let alpha: CGFloat = self.currentIsHighlighted ? 0.6 : 1.0 - transition.setAlpha(view: self.iconView, alpha: alpha) - } +// private func updateAlpha(transition: ComponentTransition) { +// let alpha: CGFloat = self.currentIsHighlighted ? 0.6 : 1.0 +// transition.setAlpha(view: self.iconView, alpha: alpha) +// } func update(component: EntityKeyboardBottomPanelButton, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { if self.component?.icon != component.icon { @@ -153,15 +162,21 @@ final class EntityKeyboardBottomPanelButton: Component { self.component = component - self.iconView.tintColor = component.color + self.iconView.tintColor = component.theme.chat.inputPanel.panelControlColor - let size = CGSize(width: 38.0, height: 38.0) + let size = CGSize(width: 40.0, height: 40.0) if let image = self.iconView.image { let iconFrame = CGRect(origin: CGPoint(x: floor((size.width - image.size.width) * 0.5), y: floor((size.height - image.size.height) * 0.5)), size: image.size) self.iconView.frame = iconFrame } + let tintColor: GlassBackgroundView.TintColor = .init(kind: .panel, color: component.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)) + transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: size)) + self.backgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: component.theme.overallDarkAppearance, tintColor: tintColor, isInteractive: true, transition: transition) + + self.buttonView.frame = CGRect(origin: .zero, size: size) + return size } } diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardBottomPanelComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardBottomPanelComponent.swift index 5db313c3..9a480929 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardBottomPanelComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardBottomPanelComponent.swift @@ -8,6 +8,9 @@ import TelegramCore import ComponentDisplayAdapters import BundleIconComponent import GlassBackgroundComponent +import EdgeEffect +import LiquidLens +import TabSelectionRecognizer private final class BottomPanelIconComponent: Component { let title: String @@ -57,22 +60,15 @@ private final class BottomPanelIconComponent: Component { super.init(frame: frame) self.addSubview(self.contentView) - self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { - if case .ended = recognizer.state { - self.component?.action() - } - } - func update(component: BottomPanelIconComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { if self.component?.title != component.title { - let text = NSAttributedString(string: component.title, font: Font.medium(15.0), textColor: .white) + let text = NSAttributedString(string: component.title, font: Font.medium(14.0), textColor: .white) let textBounds = text.boundingRect(with: CGSize(width: 120.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil) self.contentView.image = generateImage(CGSize(width: ceil(textBounds.width), height: ceil(textBounds.height)), rotatedContext: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) @@ -89,19 +85,9 @@ private final class BottomPanelIconComponent: Component { let textSize = self.contentView.image?.size ?? CGSize() let size = CGSize(width: textSize.width + textInset * 2.0, height: 28.0) - let color = component.theme.chat.inputPanel.inputControlColor + self.contentView.tintColor = component.theme.chat.inputPanel.panelControlColor - if self.contentView.tintColor != color { - if !transition.animation.isImmediate { - UIView.animate(withDuration: 0.15, delay: 0.0, options: [], animations: { - self.contentView.tintColor = color - }, completion: nil) - } else { - self.contentView.tintColor = color - } - } - - transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(x: floor((size.width - textSize.width) / 2.0), y: (size.height - textSize.height) / 2.0 - 1.0), size: textSize)) + transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(x: floor((size.width - textSize.width) / 2.0), y: (size.height - textSize.height) / 2.0), size: textSize)) return size } @@ -163,17 +149,27 @@ final class EntityKeyboardBottomPanelComponent: Component { private var leftAccessoryButton: AccessoryButtonView? private var rightAccessoryButton: AccessoryButtonView? - private var iconViews: [AnyHashable: ComponentHostView] = [:] - private var highlightedIconBackgroundView: UIView - private var highlightedTintIconBackgroundView: UIView + private let edgeEffectView: EdgeEffectView + private let backgroundContainer: GlassBackgroundContainerView + private let liquidLensView: LiquidLensView + private var itemViews: [AnyHashable: ComponentHostView] = [:] + private var selectedItemViews: [AnyHashable: ComponentHostView] = [:] + + private var tabSelectionRecognizer: TabSelectionRecognizer? + private var selectionGestureState: (startX: CGFloat, currentX: CGFloat, itemId: AnyHashable)? + let tintContentMask: UIView private var component: EntityKeyboardBottomPanelComponent? + private var state: EmptyComponentState? + private var environment: PagerComponentPanelEnvironment? override init(frame: CGRect) { self.tintContentMask = UIView() + self.edgeEffectView = EdgeEffectView() + self.backgroundView = BlurredBackgroundView(color: .clear, enableBlur: true, customBlurRadius: 10.0) self.separatorView = UIView() @@ -182,51 +178,102 @@ final class EntityKeyboardBottomPanelComponent: Component { self.tintSeparatorView.isUserInteractionEnabled = false self.tintSeparatorView.backgroundColor = UIColor(white: 0.0, alpha: 0.7) - self.tintContentMask.addSubview(self.tintSeparatorView) - - self.highlightedIconBackgroundView = UIView() - self.highlightedIconBackgroundView.isUserInteractionEnabled = false - self.highlightedIconBackgroundView.layer.cornerRadius = 10.0 - self.highlightedIconBackgroundView.clipsToBounds = true - - self.highlightedTintIconBackgroundView = UIView() - self.highlightedTintIconBackgroundView.isUserInteractionEnabled = false - self.highlightedTintIconBackgroundView.layer.cornerRadius = 10.0 - self.highlightedTintIconBackgroundView.clipsToBounds = true - self.highlightedTintIconBackgroundView.backgroundColor = UIColor(white: 0.0, alpha: 0.1) - - self.tintContentMask.addSubview(self.highlightedTintIconBackgroundView) - + self.backgroundContainer = GlassBackgroundContainerView() + self.liquidLensView = LiquidLensView(kind: .externalContainer) + super.init(frame: frame) - self.addSubview(self.backgroundView) - self.addSubview(self.highlightedIconBackgroundView) - self.addSubview(self.separatorView) + self.addSubview(self.edgeEffectView) + + self.addSubview(self.backgroundContainer) + self.backgroundContainer.contentView.addSubview(self.liquidLensView) + + let tabSelectionRecognizer = TabSelectionRecognizer(target: self, action: #selector(self.onTabSelectionGesture(_:))) + self.tabSelectionRecognizer = tabSelectionRecognizer + self.liquidLensView.addGestureRecognizer(tabSelectionRecognizer) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + private func item(at point: CGPoint) -> AnyHashable? { + var closestItem: (AnyHashable, CGFloat)? + for (id, itemView) in self.itemViews { + if itemView.frame.contains(point) { + return id + } else { + let distance = abs(point.x - itemView.center.x) + if let closestItemValue = closestItem { + if closestItemValue.1 > distance { + closestItem = (id, distance) + } + } else { + closestItem = (id, distance) + } + } + } + return closestItem?.0 + } + + @objc private func onTabSelectionGesture(_ recognizer: TabSelectionRecognizer) { + guard let environment = self.environment else { + return + } + let location = recognizer.location(in: self.liquidLensView.contentView) + switch recognizer.state { + case .began: + if let itemId = self.item(at: location), let itemView = self.itemViews[itemId] { + let startX = itemView.frame.minX - 4.0 + self.selectionGestureState = (startX, startX, itemId) + self.state?.updated(transition: .spring(duration: 0.4), isLocal: true) + } + case .changed: + if var selectionGestureState = self.selectionGestureState { + selectionGestureState.currentX = selectionGestureState.startX + recognizer.translation(in: self).x + if let itemId = self.item(at: location) { + selectionGestureState.itemId = itemId + } + self.selectionGestureState = selectionGestureState + self.state?.updated(transition: .immediate, isLocal: true) + } + case .ended, .cancelled: + if let selectionGestureState = self.selectionGestureState { + self.selectionGestureState = nil + if case .ended = recognizer.state { + guard let item = environment.contentIcons.first(where: { $0.id == selectionGestureState.itemId }) else { + return + } + environment.navigateToContentId(item.id) + } + self.state?.updated(transition: .spring(duration: 0.4), isLocal: true) + } + default: + break + } + } + func update(component: EntityKeyboardBottomPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { if self.component?.theme !== component.theme { self.separatorView.backgroundColor = component.theme.list.itemPlainSeparatorColor.withMultipliedAlpha(0.5) self.backgroundView.updateColor(color: component.theme.chat.inputPanel.panelBackgroundColor.withMultipliedAlpha(1.0), transition: .immediate) - self.highlightedIconBackgroundView.backgroundColor = component.theme.chat.inputMediaPanel.panelHighlightedIconBackgroundColor } let intrinsicHeight: CGFloat = 34.0 - let height = intrinsicHeight + component.containerInsets.bottom + let height = intrinsicHeight + component.containerInsets.bottom + 20.0 let accessoryButtonOffset: CGFloat if component.containerInsets.bottom > 0.0 { - accessoryButtonOffset = 2.0 + accessoryButtonOffset = 0.0 } else { accessoryButtonOffset = -2.0 } + self.component = component + self.state = state let panelEnvironment = environment[PagerComponentPanelEnvironment.self].value + self.environment = panelEnvironment let activeContentId = panelEnvironment.activeContentId var leftAccessoryButtonComponent: AnyComponentWithIdentity? @@ -257,7 +304,7 @@ final class EntityKeyboardBottomPanelComponent: Component { environment: {}, containerSize: CGSize(width: .greatestFiniteMagnitude, height: intrinsicHeight) ) - let leftAccessoryButtonFrame = CGRect(origin: CGPoint(x: component.containerInsets.left + 2.0, y: accessoryButtonOffset), size: leftAccessoryButtonSize) + let leftAccessoryButtonFrame = CGRect(origin: CGPoint(x: component.containerInsets.left + 18.0, y: accessoryButtonOffset), size: leftAccessoryButtonSize) leftAccessoryButtonTransition.setFrame(view: leftAccessoryButton.view, frame: leftAccessoryButtonFrame) if let leftAccessoryButtonView = leftAccessoryButton.view.componentView as? PagerTopPanelView { if leftAccessoryButtonView.tintContentMask.superview == nil { @@ -328,7 +375,7 @@ final class EntityKeyboardBottomPanelComponent: Component { containerSize: CGSize(width: .greatestFiniteMagnitude, height: intrinsicHeight) ) - let rightAccessoryButtonFrame = CGRect(origin: CGPoint(x: availableSize.width - component.containerInsets.right - 2.0 - rightAccessoryButtonSize.width, y: accessoryButtonOffset), size: rightAccessoryButtonSize) + let rightAccessoryButtonFrame = CGRect(origin: CGPoint(x: availableSize.width - component.containerInsets.right - 18.0 - rightAccessoryButtonSize.width, y: accessoryButtonOffset), size: rightAccessoryButtonSize) rightAccessoryButtonTransition.setFrame(view: rightAccessoryButton.view, frame: rightAccessoryButtonFrame) if let rightAccessoryButtonView = rightAccessoryButton.view.componentView as? PagerTopPanelView { if rightAccessoryButtonView.tintContentMask.superview == nil { @@ -374,23 +421,32 @@ final class EntityKeyboardBottomPanelComponent: Component { var iconInfos: [AnyHashable: (size: CGSize, transition: ComponentTransition)] = [:] var iconTotalSize = CGSize() - let iconSpacing: CGFloat = 4.0 + let iconSpacing: CGFloat = 0.0 let navigateToContentId = panelEnvironment.navigateToContentId + var lensSelection: (x: CGFloat, width: CGFloat) = (0.0, 0.0) + if panelEnvironment.contentIcons.count > 1 { for icon in panelEnvironment.contentIcons { validIconIds.append(icon.id) var iconTransition = transition let iconView: ComponentHostView - if let current = self.iconViews[icon.id] { + let selectedIconView: ComponentHostView + if let current = self.itemViews[icon.id], let currentSelected = self.selectedItemViews[icon.id] { iconView = current + selectedIconView = currentSelected } else { iconTransition = .immediate iconView = ComponentHostView() - self.iconViews[icon.id] = iconView - self.addSubview(iconView) + iconView.isUserInteractionEnabled = false + selectedIconView = ComponentHostView() + selectedIconView.isUserInteractionEnabled = false + self.itemViews[icon.id] = iconView + self.selectedItemViews[icon.id] = selectedIconView + self.liquidLensView.contentView.addSubview(iconView) + self.liquidLensView.selectedContentView.addSubview(selectedIconView) } let iconSize = iconView.update( @@ -407,65 +463,70 @@ final class EntityKeyboardBottomPanelComponent: Component { containerSize: CGSize(width: 28.0, height: 28.0) ) + let _ = selectedIconView.update( + transition: iconTransition, + component: AnyComponent(BottomPanelIconComponent( + title: icon.title, + isHighlighted: icon.id == activeContentId, + theme: component.theme, + action: { + navigateToContentId(icon.id) + } + )), + environment: {}, + containerSize: CGSize(width: 28.0, height: 28.0) + ) + iconInfos[icon.id] = (size: iconSize, transition: iconTransition) if !iconTotalSize.width.isZero { - iconTotalSize.width += iconSpacing + iconTotalSize.width += iconSpacing - 8.0 } iconTotalSize.width += iconSize.width iconTotalSize.height = max(iconTotalSize.height, iconSize.height) } } - - var nextIconOrigin = CGPoint(x: floor((availableSize.width - iconTotalSize.width) / 2.0), y: floor((intrinsicHeight - iconTotalSize.height) / 2.0)) - if component.containerInsets.bottom > 0.0 { - nextIconOrigin.y += 3.0 - } + + let tabsSize = CGSize(width: iconTotalSize.width, height: 40.0) + var nextIconOrigin = CGPoint(x: floor((tabsSize.width - iconTotalSize.width) / 2.0), y: floor((tabsSize.height - iconTotalSize.height) / 2.0)) + + transition.setFrame(view: self.backgroundContainer, frame: CGRect(origin: .zero, size: availableSize)) + self.backgroundContainer.update(size: availableSize, isDark: component.theme.overallDarkAppearance, transition: transition) if panelEnvironment.contentIcons.count > 1 { for icon in panelEnvironment.contentIcons { - guard let iconInfo = iconInfos[icon.id], let iconView = self.iconViews[icon.id] else { + guard let iconInfo = iconInfos[icon.id], let iconView = self.itemViews[icon.id], let selectedIconView = self.selectedItemViews[icon.id] else { continue } let iconFrame = CGRect(origin: nextIconOrigin, size: iconInfo.size) iconInfo.transition.setFrame(view: iconView, frame: iconFrame, completion: nil) - - if let iconView = iconView.componentView as? BottomPanelIconComponent.View { - if iconView.tintMaskContainer.superview == nil { - self.tintContentMask.addSubview(iconView.tintMaskContainer) - } - iconInfo.transition.setFrame(view: iconView.tintMaskContainer, frame: iconFrame, completion: nil) - } + iconInfo.transition.setFrame(view: selectedIconView, frame: iconFrame, completion: nil) if let activeContentId = activeContentId, activeContentId == icon.id { - self.highlightedIconBackgroundView.isHidden = false - self.highlightedTintIconBackgroundView.isHidden = false - transition.setFrame(view: self.highlightedIconBackgroundView, frame: iconFrame) - transition.setFrame(view: self.highlightedTintIconBackgroundView, frame: iconFrame) - - let cornerRadius: CGFloat = min(iconFrame.width, iconFrame.height) / 2.0 - transition.setCornerRadius(layer: self.highlightedIconBackgroundView.layer, cornerRadius: cornerRadius) - transition.setCornerRadius(layer: self.highlightedTintIconBackgroundView.layer, cornerRadius: cornerRadius) + lensSelection = (iconFrame.origin.x, iconFrame.width) } - - nextIconOrigin.x += iconInfo.size.width + iconSpacing + nextIconOrigin.x += iconInfo.size.width + iconSpacing - 8.0 } } - - if activeContentId == nil { - self.highlightedIconBackgroundView.isHidden = true + + if let selectionGestureState = self.selectionGestureState { + lensSelection = (selectionGestureState.currentX, lensSelection.width) } + transition.setFrame(view: self.liquidLensView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - tabsSize.width) / 2.0), y: 0.0), size: tabsSize)) + self.liquidLensView.update(size: CGSize(width: tabsSize.width, height: tabsSize.height), selectionOrigin: CGPoint(x: max(0.0, min(tabsSize.width - lensSelection.width, lensSelection.x)), y: 0.0), selectionSize: CGSize(width: lensSelection.width, height: tabsSize.height), inset: 3.0, isDark: component.theme.overallDarkAppearance, isLifted: self.selectionGestureState != nil, isCollapsed: activeContentId == nil, transition: transition) + var removedIconViewIds: [AnyHashable] = [] - for (id, iconView) in self.iconViews { + for (id, iconView) in self.itemViews { if !validIconIds.contains(id) { removedIconViewIds.append(id) iconView.removeFromSuperview() } } + for id in removedIconViewIds { - self.iconViews.removeValue(forKey: id) + self.itemViews.removeValue(forKey: id) } transition.setFrame(view: self.separatorView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: UIScreenPixel))) @@ -473,8 +534,11 @@ final class EntityKeyboardBottomPanelComponent: Component { transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: height))) //self.backgroundView.update(size: CGSize(width: availableSize.width, height: height), transition: transition.containedViewLayoutTransition) - - self.component = component + + let edgeEffectHeight: CGFloat = 80.0 + let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: height - edgeEffectHeight), size: CGSize(width: availableSize.width, height: edgeEffectHeight)) + transition.setFrame(view: self.edgeEffectView, frame: edgeEffectFrame) + self.edgeEffectView.update(content: component.theme.chat.inputMediaPanel.backgroundColor.withMultipliedAlpha(0.8), rect: edgeEffectFrame, edge: .bottom, edgeSize: min(edgeEffectHeight, 50.0), transition: transition) return CGSize(width: availableSize.width, height: height) } diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopContainerPanelComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopContainerPanelComponent.swift index a7d0dfc9..83c09abb 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopContainerPanelComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopContainerPanelComponent.swift @@ -9,15 +9,18 @@ import Postbox public final class EntityKeyboardTopContainerPanelEnvironment: Equatable { let isContentInFocus: Bool + let height: CGFloat let visibilityFractionUpdated: ActionSlot<(CGFloat, ComponentTransition)> let isExpandedUpdated: (Bool, ComponentTransition) -> Void init( isContentInFocus: Bool, + height: CGFloat, visibilityFractionUpdated: ActionSlot<(CGFloat, ComponentTransition)>, isExpandedUpdated: @escaping (Bool, ComponentTransition) -> Void ) { self.isContentInFocus = isContentInFocus + self.height = height self.visibilityFractionUpdated = visibilityFractionUpdated self.isExpandedUpdated = isExpandedUpdated } @@ -26,6 +29,9 @@ public final class EntityKeyboardTopContainerPanelEnvironment: Equatable { if lhs.isContentInFocus != rhs.isContentInFocus { return false } + if lhs.height != rhs.height { + return false + } if lhs.visibilityFractionUpdated !== rhs.visibilityFractionUpdated { return false } @@ -39,17 +45,20 @@ final class EntityKeyboardTopContainerPanelComponent: Component { let theme: PresentationTheme let overflowHeight: CGFloat let topInset: CGFloat + let height: CGFloat let displayBackground: EntityKeyboardComponent.DisplayTopPanelBackground init( theme: PresentationTheme, overflowHeight: CGFloat, topInset: CGFloat, + height: CGFloat, displayBackground: EntityKeyboardComponent.DisplayTopPanelBackground ) { self.theme = theme self.overflowHeight = overflowHeight self.topInset = topInset + self.height = height self.displayBackground = displayBackground } @@ -60,6 +69,9 @@ final class EntityKeyboardTopContainerPanelComponent: Component { if lhs.overflowHeight != rhs.overflowHeight { return false } + if lhs.height != rhs.height { + return false + } if lhs.topInset != rhs.topInset { return false } @@ -105,7 +117,7 @@ final class EntityKeyboardTopContainerPanelComponent: Component { } func update(component: EntityKeyboardTopContainerPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { - let intrinsicHeight: CGFloat = 34.0 + let intrinsicHeight: CGFloat = component.height let height = intrinsicHeight + component.topInset let panelEnvironment = environment[PagerComponentPanelEnvironment.self].value @@ -165,6 +177,7 @@ final class EntityKeyboardTopContainerPanelComponent: Component { environment: { EntityKeyboardTopContainerPanelEnvironment( isContentInFocus: panelEnvironment.isContentInFocus, + height: intrinsicHeight, visibilityFractionUpdated: panelView.visibilityFractionUpdated, isExpandedUpdated: { [weak self] isExpanded, transition in guard let strongSelf = self else { diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift index d6c9379c..a28088ad 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift @@ -2009,7 +2009,7 @@ public final class EntityKeyboardTopPanelComponent: Component { let panelEnvironment = environment[EntityKeyboardTopContainerPanelEnvironment.self].value self.environment = panelEnvironment - let isExpanded = availableSize.height > 34.0 + let isExpanded = availableSize.height > panelEnvironment.height let wasExpanded = self.isExpanded self.isExpanded = isExpanded diff --git a/submodules/TelegramUI/Components/FaceScanScreen/Sources/AgeVerificationScreen.swift b/submodules/TelegramUI/Components/FaceScanScreen/Sources/AgeVerificationScreen.swift index 761c842b..177ed05c 100644 --- a/submodules/TelegramUI/Components/FaceScanScreen/Sources/AgeVerificationScreen.swift +++ b/submodules/TelegramUI/Components/FaceScanScreen/Sources/AgeVerificationScreen.swift @@ -116,7 +116,7 @@ private final class SheetContent: CombinedComponent { component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: theme.chat.inputPanel.panelControlColor ) )), action: { _ in diff --git a/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift b/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift index 08000283..0a0b5a40 100644 --- a/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift +++ b/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift @@ -1012,7 +1012,7 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer { isHiddenUpdatedImpl?(isHidden) }, openPremium: { openPremiumImpl?() - }), navigationBarAppearance: .transparent) + }), navigationBarAppearance: .default) let presentationData = context.sharedContext.currentPresentationData.with { $0 } let title: String diff --git a/submodules/TelegramUI/Components/Gifts/GiftAnimationComponent/Sources/GiftCompositionComponent.swift b/submodules/TelegramUI/Components/Gifts/GiftAnimationComponent/Sources/GiftCompositionComponent.swift index bad7a166..c570354d 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftAnimationComponent/Sources/GiftCompositionComponent.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftAnimationComponent/Sources/GiftCompositionComponent.swift @@ -23,7 +23,6 @@ public final class GiftCompositionComponent: Component { public fileprivate(set) var previewSymbol: StarGift.UniqueGift.Attribute? public init() { - self.previewPatternColor = nil } } diff --git a/submodules/TelegramUI/Components/Gifts/GiftLoadingShimmerView/Sources/GiftLoadingShimmerView.swift b/submodules/TelegramUI/Components/Gifts/GiftLoadingShimmerView/Sources/GiftLoadingShimmerView.swift index 55967358..07ecd244 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftLoadingShimmerView/Sources/GiftLoadingShimmerView.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftLoadingShimmerView/Sources/GiftLoadingShimmerView.swift @@ -164,7 +164,7 @@ public final class GiftLoadingShimmerView: UIView { } } - var currentY: CGFloat = 39.0 + 7.0 + var currentY: CGFloat = 52.0 + 7.0 var rowIndex: Int = 0 let optionSpacing: CGFloat = 10.0 diff --git a/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/BUILD b/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/BUILD index 558f8cfb..089497fb 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/BUILD +++ b/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/BUILD @@ -46,6 +46,9 @@ swift_library( "//submodules/TelegramUI/Components/Gifts/GiftViewScreen", "//submodules/TelegramUI/Components/EdgeEffect", "//submodules/TelegramUI/Components/GlassBarButtonComponent", + "//submodules/TelegramUI/Components/AlertComponent", + "//submodules/TelegramUI/Components/AlertComponent/AlertTransferHeaderComponent", + "//submodules/TelegramUI/Components/AvatarComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftAuctionTransferController.swift b/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftAuctionTransferController.swift index fde4bd99..3344febc 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftAuctionTransferController.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftAuctionTransferController.swift @@ -3,238 +3,20 @@ import UIKit import SwiftSignalKit import AsyncDisplayKit import Display -import Postbox +import ComponentFlow import TelegramCore import TelegramPresentationData -import TelegramUIPreferences import AccountContext import AppBundle -import AvatarNode -import Markdown +import AlertComponent +import AlertTransferHeaderComponent +import AvatarComponent -private final class GiftAuctionTransferAlertContentNode: AlertContentNode { - private let strings: PresentationStrings - private let title: String - private let text: String - - private let titleNode: ASTextNode - private let textNode: ASTextNode - private let avatarNode: AvatarNode - private let arrowNode: ASImageNode - private let secondAvatarNode: AvatarNode - - private let actionNodesSeparator: ASDisplayNode - private let actionNodes: [TextAlertContentActionNode] - private let actionVerticalSeparators: [ASDisplayNode] - - private var validLayout: CGSize? - - override var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled - } - - init(context: AccountContext, theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, fromPeer: EnginePeer, toPeer: EnginePeer, title: String, text: String, actions: [TextAlertAction]) { - self.strings = strings - self.title = title - self.text = text - - self.titleNode = ASTextNode() - self.titleNode.maximumNumberOfLines = 0 - - self.textNode = ASTextNode() - self.textNode.maximumNumberOfLines = 0 - - self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0)) - - self.arrowNode = ASImageNode() - self.arrowNode.displaysAsynchronously = false - self.arrowNode.displayWithoutProcessing = true - - self.secondAvatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0)) - - self.actionNodesSeparator = ASDisplayNode() - self.actionNodesSeparator.isLayerBacked = true - - self.actionNodes = actions.map { action -> TextAlertContentActionNode in - return TextAlertContentActionNode(theme: theme, action: action) - } - - var actionVerticalSeparators: [ASDisplayNode] = [] - if actions.count > 1 { - for _ in 0 ..< actions.count - 1 { - let separatorNode = ASDisplayNode() - separatorNode.isLayerBacked = true - actionVerticalSeparators.append(separatorNode) - } - } - self.actionVerticalSeparators = actionVerticalSeparators - - super.init() - - self.addSubnode(self.titleNode) - self.addSubnode(self.textNode) - self.addSubnode(self.avatarNode) - self.addSubnode(self.arrowNode) - self.addSubnode(self.secondAvatarNode) - - self.addSubnode(self.actionNodesSeparator) - - for actionNode in self.actionNodes { - self.addSubnode(actionNode) - } - - for separatorNode in self.actionVerticalSeparators { - self.addSubnode(separatorNode) - } - - self.updateTheme(theme) - - self.avatarNode.setPeer(context: context, theme: ptheme, peer: fromPeer) - self.secondAvatarNode.setPeer(context: context, theme: ptheme, peer: toPeer) - } - - override func updateTheme(_ theme: AlertControllerTheme) { - self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.bold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center) - self.textNode.attributedText = parseMarkdownIntoAttributedString(self.text, attributes: MarkdownAttributes( - body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor), - bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: theme.primaryColor), - link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor), - linkAttribute: { url in - return ("URL", url) - } - ), textAlignment: .center) - self.arrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Peer Info/AlertArrow"), color: theme.secondaryColor) - - self.actionNodesSeparator.backgroundColor = theme.separatorColor - for actionNode in self.actionNodes { - actionNode.updateTheme(theme) - } - for separatorNode in self.actionVerticalSeparators { - separatorNode.backgroundColor = theme.separatorColor - } - - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) - } - } - - override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - var size = size - size.width = min(size.width, 270.0) - - self.validLayout = size - - var origin: CGPoint = CGPoint(x: 0.0, y: 20.0) - - let avatarSize = CGSize(width: 60.0, height: 60.0) - self.avatarNode.updateSize(size: avatarSize) - - let avatarFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) - 44.0, y: origin.y), size: avatarSize) - transition.updateFrame(node: self.avatarNode, frame: avatarFrame) - - if let arrowImage = self.arrowNode.image { - let arrowFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - arrowImage.size.width) / 2.0), y: origin.y + floorToScreenPixels((avatarSize.height - arrowImage.size.height) / 2.0)), size: arrowImage.size) - transition.updateFrame(node: self.arrowNode, frame: arrowFrame) - } - - let secondAvatarFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) + 44.0, y: origin.y), size: avatarSize) - transition.updateFrame(node: self.secondAvatarNode, frame: secondAvatarFrame) - - origin.y += avatarSize.height + 10.0 - - let titleSize = self.titleNode.measure(CGSize(width: size.width - 32.0, height: size.height)) - transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize)) - origin.y += titleSize.height + 4.0 - - let textSize = self.textNode.measure(CGSize(width: size.width - 32.0, height: size.height)) - transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize)) - origin.y += textSize.height + 10.0 - - let actionButtonHeight: CGFloat = 44.0 - var minActionsWidth: CGFloat = 0.0 - let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) - let actionTitleInsets: CGFloat = 8.0 - - var effectiveActionLayout = TextAlertContentActionLayout.horizontal - for actionNode in self.actionNodes { - let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) - if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 { - effectiveActionLayout = .vertical - } - switch effectiveActionLayout { - case .horizontal: - minActionsWidth += actionTitleSize.width + actionTitleInsets - case .vertical: - minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) - } - } - - let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0) - - let contentWidth = max(size.width, minActionsWidth) - - var actionsHeight: CGFloat = 0.0 - switch effectiveActionLayout { - case .horizontal: - actionsHeight = actionButtonHeight - case .vertical: - actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) - } - - let resultSize = CGSize(width: contentWidth, height: avatarSize.height + titleSize.height + textSize.height + actionsHeight + 16.0 + insets.top + insets.bottom) - transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - - var actionOffset: CGFloat = 0.0 - let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) - var separatorIndex = -1 - var nodeIndex = 0 - for actionNode in self.actionNodes { - if separatorIndex >= 0 { - let separatorNode = self.actionVerticalSeparators[separatorIndex] - switch effectiveActionLayout { - case .horizontal: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) - case .vertical: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - } - } - separatorIndex += 1 - - let currentActionWidth: CGFloat - switch effectiveActionLayout { - case .horizontal: - if nodeIndex == self.actionNodes.count - 1 { - currentActionWidth = resultSize.width - actionOffset - } else { - currentActionWidth = actionWidth - } - case .vertical: - currentActionWidth = resultSize.width - } - - let actionNodeFrame: CGRect - switch effectiveActionLayout { - case .horizontal: - actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += currentActionWidth - case .vertical: - actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += actionButtonHeight - } - - transition.updateFrame(node: actionNode, frame: actionNodeFrame) - - nodeIndex += 1 - } - - return resultSize - } -} - -func giftAuctionTransferController(context: AccountContext, fromPeer: EnginePeer, toPeer: EnginePeer, commit: @escaping () -> Void) -> AlertController { +func giftAuctionTransferController(context: AccountContext, fromPeer: EnginePeer, toPeer: EnginePeer, commit: @escaping () -> Void) -> AlertScreen { let presentationData = context.sharedContext.currentPresentationData.with { $0 } let strings = presentationData.strings + let title = strings.Gift_AuctionTransfer_Title let text: String if fromPeer.id == context.account.peerId { text = strings.Gift_AuctionTransfer_TextFromYourself(toPeer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder)).string @@ -243,25 +25,53 @@ func giftAuctionTransferController(context: AccountContext, fromPeer: EnginePeer } else { text = strings.Gift_AuctionTransfer_Text(fromPeer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder), toPeer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder)).string } - - var dismissImpl: ((Bool) -> Void)? - var contentNode: GiftAuctionTransferAlertContentNode? - let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - dismissImpl?(true) - }), TextAlertAction(type: .defaultAction, title: strings.Gift_AuctionTransfer_Change, action: { - dismissImpl?(true) - commit() - })] - contentNode = GiftAuctionTransferAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: strings, fromPeer: fromPeer, toPeer: toPeer, title: strings.Gift_AuctionTransfer_Title, text: text, actions: actions) + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "header", + component: AnyComponent( + AlertTransferHeaderComponent( + fromComponent: AnyComponentWithIdentity(id: "fromPeer", component: AnyComponent( + AvatarComponent( + context: context, + theme: presentationData.theme, + peer: fromPeer + ) + )), + toComponent: AnyComponentWithIdentity(id: "toPeer", component: AnyComponent( + AvatarComponent( + context: context, + theme: presentationData.theme, + peer: toPeer + ) + )), + type: .transfer + ) + ) + )) + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: title) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(text)) + ) + )) - let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode!) - dismissImpl = { [weak controller] animated in - if animated { - controller?.dismissAnimated() - } else { - controller?.dismiss() - } - } - return controller + let alertController = AlertScreen( + context: context, + configuration: AlertScreen.Configuration(actionAlignment: .vertical, dismissOnOutsideTap: true, allowInputInset: false), + content: content, + actions: [ + .init(title: strings.Gift_AuctionTransfer_Change, type: .default, action: { + commit() + }), + .init(title: strings.Common_Cancel) + ] + ) + return alertController } diff --git a/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift index 71760422..377a6833 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift @@ -34,6 +34,7 @@ final class GiftOptionsScreenComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment let context: AccountContext + let overNavigationContainer: UIView let starsContext: StarsContext let peerId: EnginePeer.Id let premiumOptions: [CachedPremiumGiftOption] @@ -42,6 +43,7 @@ final class GiftOptionsScreenComponent: Component { init( context: AccountContext, + overNavigationContainer: UIView, starsContext: StarsContext, peerId: EnginePeer.Id, premiumOptions: [CachedPremiumGiftOption], @@ -49,6 +51,7 @@ final class GiftOptionsScreenComponent: Component { completion: (() -> Void)? ) { self.context = context + self.overNavigationContainer = overNavigationContainer self.starsContext = starsContext self.peerId = peerId self.premiumOptions = premiumOptions @@ -366,7 +369,7 @@ final class GiftOptionsScreenComponent: Component { return } - if gift.flags.contains(.isAuction) { + if gift.flags.contains(.isAuction) && !((gift.availability?.resale ?? 0) > 0 && component.peerId != component.context.account.peerId) { guard let giftAuctionsManager = component.context.giftAuctionsManager else { return } @@ -416,6 +419,7 @@ final class GiftOptionsScreenComponent: Component { let giftController = component.context.sharedContext.makeGiftAuctionViewScreen( context: component.context, auctionContext: auctionContext, + peerId: component.peerId, completion: { [weak mainController] acquiredGifts, upgradeAttributes in if component.peerId == context.account.peerId, let upgradeAttributes, let navigationController = mainController?.navigationController as? NavigationController { let controller = context.sharedContext.makeGiftAuctionWearPreviewScreen(context: context, auctionContext: auctionContext, acquiredGifts: acquiredGifts, attributes: upgradeAttributes, completion: { @@ -988,7 +992,7 @@ final class GiftOptionsScreenComponent: Component { controller.present(alertController, in: .current) dismissAlertImpl = { [weak alertController] in - alertController?.dismissAnimated() + alertController?.dismiss() } } @@ -1084,6 +1088,9 @@ final class GiftOptionsScreenComponent: Component { guard let self, let component = self.component, let controller = controller(), let navigationController = controller.navigationController as? NavigationController else { return } + guard component.peerId != component.context.account.peerId else { + return + } let _ = (component.context.engine.data.get( TelegramEngine.EngineData.Item.Peer.Peer(id: component.peerId) ) @@ -1141,18 +1148,18 @@ final class GiftOptionsScreenComponent: Component { } if isGlass { - let barButtonSize = CGSize(width: 40.0, height: 40.0) + let barButtonSize = CGSize(width: 44.0, height: 44.0) let cancelButtonSize = self.cancelButton.update( transition: transition, component: AnyComponent(GlassBarButtonComponent( size: barButtonSize, - backgroundColor: theme.rootController.navigationBar.glassBarButtonBackgroundColor, + backgroundColor: nil, isDark: theme.overallDarkAppearance, - state: .generic, + state: .glass, component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: theme.chat.inputPanel.panelControlColor ) )), action: { _ in @@ -1919,6 +1926,8 @@ final class GiftOptionsScreenComponent: Component { open class GiftOptionsScreen: ViewControllerComponentContainer, GiftOptionsScreenProtocol { private let context: AccountContext + private let overNavigationContainer: UIView + public var parentController: () -> ViewController? = { return nil } @@ -1933,8 +1942,11 @@ open class GiftOptionsScreen: ViewControllerComponentContainer, GiftOptionsScree ) { self.context = context + self.overNavigationContainer = SparseContainerView() + super.init(context: context, component: GiftOptionsScreenComponent( context: context, + overNavigationContainer: self.overNavigationContainer, starsContext: starsContext, peerId: peerId, premiumOptions: premiumOptions, @@ -1951,6 +1963,10 @@ open class GiftOptionsScreen: ViewControllerComponentContainer, GiftOptionsScree } componentView.scrollToTop() } + + if let navigationBar = self.navigationBar { + navigationBar.customOverBackgroundContentView.insertSubview(self.overNavigationContainer, at: 0) + } } required public init(coder aDecoder: NSCoder) { diff --git a/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/ChatGiftPreviewItem.swift b/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/ChatGiftPreviewItem.swift index 01753181..f6c7f11a 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/ChatGiftPreviewItem.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/ChatGiftPreviewItem.swift @@ -193,7 +193,7 @@ final class ChatGiftPreviewItemNode: ListViewItemNode { self.containerNode = ASDisplayNode() self.containerNode.subnodeTransform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.clipsToBounds = true self.isUserInteractionEnabled = false diff --git a/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/GiftSetupScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/GiftSetupScreen.swift index b4ac9a97..dc774f1c 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/GiftSetupScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftSetupScreen/Sources/GiftSetupScreen.swift @@ -622,7 +622,9 @@ private final class GiftSetupScreenComponent: Component { } } - starsContext.load(force: true) + Queue.mainQueue().after(2.5) { + starsContext.load(force: true) + } }, error: { [weak self] error in guard let self, let controller = self.environment?.controller() else { return @@ -768,7 +770,6 @@ private final class GiftSetupScreenComponent: Component { pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, - importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, @@ -1118,14 +1119,14 @@ private final class GiftSetupScreenComponent: Component { let closeButtonSize = self.closeButton.update( transition: .immediate, component: AnyComponent(GlassBarButtonComponent( - size: CGSize(width: 40.0, height: 40.0), - backgroundColor: environment.theme.rootController.navigationBar.glassBarButtonBackgroundColor, + size: CGSize(width: 44.0, height: 44.0), + backgroundColor: nil, isDark: environment.theme.overallDarkAppearance, - state: .generic, + state: .glass, component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: environment.theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: environment.theme.chat.inputPanel.panelControlColor ) )), action: { [weak self] _ in @@ -1136,7 +1137,7 @@ private final class GiftSetupScreenComponent: Component { } )), environment: {}, - containerSize: CGSize(width: 40.0, height: 40.0) + containerSize: CGSize(width: 44.0, height: 44.0) ) let closeButtonFrame = CGRect(origin: CGPoint(x: rawSideInset + 16.0, y: 16.0), size: closeButtonSize) if let closeButtonView = self.closeButton.view { diff --git a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/BUILD b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/BUILD index 26cd12ad..16f3589b 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/BUILD +++ b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/BUILD @@ -46,6 +46,8 @@ swift_library( "//submodules/TelegramUI/Components/LottieComponent", "//submodules/TelegramUI/Components/TextFieldComponent", "//submodules/TelegramUI/Components/Gifts/GiftLoadingShimmerView", + "//submodules/TelegramUI/Components/EdgeEffect", + "//submodules/TelegramUI/Components/GlassBackgroundComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/FilterSelectorComponent.swift b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/FilterSelectorComponent.swift index f282b150..14f0697c 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/FilterSelectorComponent.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/FilterSelectorComponent.swift @@ -9,21 +9,10 @@ import BundleIconComponent import TextFormat import AccountContext import LottieComponent +import TelegramPresentationData +import GlassBackgroundComponent public final class FilterSelectorComponent: Component { - public struct Colors: Equatable { - public var foreground: UIColor - public var background: UIColor - - public init( - foreground: UIColor, - background: UIColor - ) { - self.foreground = foreground - self.background = background - } - } - public struct Item: Equatable { public var id: AnyHashable public var index: Int @@ -51,18 +40,18 @@ public final class FilterSelectorComponent: Component { } public let context: AccountContext? - public let colors: Colors + public let theme: PresentationTheme public let items: [Item] public let selectedItemId: AnyHashable? public init( context: AccountContext? = nil, - colors: Colors, + theme: PresentationTheme, items: [Item], selectedItemId: AnyHashable? ) { self.context = context - self.colors = colors + self.theme = theme self.items = items self.selectedItemId = selectedItemId } @@ -71,7 +60,7 @@ public final class FilterSelectorComponent: Component { if lhs.context !== rhs.context { return false } - if lhs.colors != rhs.colors { + if lhs.theme !== rhs.theme { return false } if lhs.items != rhs.items { @@ -133,11 +122,11 @@ public final class FilterSelectorComponent: Component { self.component = component self.state = state - let baseHeight: CGFloat = 28.0 + let baseHeight: CGFloat = 36.0 var spacing: CGFloat = 6.0 - let itemFont = Font.semibold(14.0) + let itemFont = Font.medium(14.0) let allowScroll = true var innerContentWidth: CGFloat = 0.0 @@ -162,25 +151,19 @@ public final class FilterSelectorComponent: Component { let itemSize = itemView.title.update( transition: transition, - component: AnyComponent(PlainButtonComponent( - content: AnyComponent(ItemComponent( - context: component.context, - index: item.index, - iconName: item.iconName, - text: item.title, - font: itemFont, - color: component.colors.foreground, - backgroundColor: component.colors.background, - isSelected: itemId == component.selectedItemId - )), - effectAlignment: .center, - minSize: nil, + component: AnyComponent(ItemComponent( + context: component.context, + index: item.index, + iconName: item.iconName, + text: item.title, + font: itemFont, + theme: component.theme, + isSelected: itemId == component.selectedItemId, action: { [weak itemView] in if let view = itemView?.title.view { item.action(view) } - }, - animateScale: false + } )), environment: {}, containerSize: CGSize(width: 200.0, height: 100.0) @@ -261,9 +244,9 @@ private final class ItemComponent: Component { let iconName: String? let text: String let font: UIFont - let color: UIColor - let backgroundColor: UIColor + let theme: PresentationTheme let isSelected: Bool + let action: () -> Void init( context: AccountContext?, @@ -271,18 +254,18 @@ private final class ItemComponent: Component { iconName: String?, text: String, font: UIFont, - color: UIColor, - backgroundColor: UIColor, - isSelected: Bool + theme: PresentationTheme, + isSelected: Bool, + action: @escaping () -> Void ) { self.context = context self.index = index self.iconName = iconName self.text = text self.font = font - self.color = color - self.backgroundColor = backgroundColor + self.theme = theme self.isSelected = isSelected + self.action = action } static func ==(lhs: ItemComponent, rhs: ItemComponent) -> Bool { @@ -301,10 +284,7 @@ private final class ItemComponent: Component { if lhs.font != rhs.font { return false } - if lhs.color != rhs.color { - return false - } - if lhs.backgroundColor != rhs.backgroundColor { + if lhs.theme !== rhs.theme { return false } if lhs.isSelected != rhs.isSelected { @@ -317,7 +297,7 @@ private final class ItemComponent: Component { private var component: ItemComponent? private weak var state: EmptyComponentState? - private let background = ComponentView() + private let backgroundView: GlassBackgroundView private let title = ComponentView() private let icon = ComponentView() @@ -327,13 +307,24 @@ private final class ItemComponent: Component { private let playOnce = ActionSlot() override init(frame: CGRect) { + self.backgroundView = GlassBackgroundView() + super.init(frame: frame) + + self.addSubview(self.backgroundView) + self.backgroundView.contentView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.onTapGesture(_:)))) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + @objc private func onTapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + self.component?.action() + } + } + func update(component: ItemComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { let previousComponent = self.component self.component = component @@ -342,7 +333,7 @@ private final class ItemComponent: Component { var animateTitleInDirection: CGFloat? if let previousComponent, previousComponent.text != component.text, !transition.animation.isImmediate, let titleView = self.title.view, let snapshotView = titleView.snapshotView(afterScreenUpdates: false) { snapshotView.frame = titleView.frame - self.addSubview(snapshotView) + self.backgroundView.contentView.addSubview(snapshotView) var direction: CGFloat = 1.0 if previousComponent.index < component.index { @@ -357,7 +348,7 @@ private final class ItemComponent: Component { animateTitleInDirection = direction } - let attributedTitle = NSAttributedString(string: component.text, font: component.font, textColor: component.color) + let attributedTitle = NSAttributedString(string: component.text, font: component.font, textColor: component.theme.chat.inputPanel.panelControlColor) let titleSize = self.title.update( transition: .immediate, component: AnyComponent(MultilineTextComponent( @@ -374,7 +365,7 @@ private final class ItemComponent: Component { transition: transition, component: AnyComponent(LottieComponent( content: LottieComponent.AppBundleContent(name: animationName), - color: component.color, + color: component.theme.chat.inputPanel.panelControlColor, playOnce: self.playOnce )), environment: {}, @@ -406,29 +397,14 @@ private final class ItemComponent: Component { } let spacing: CGFloat = 4.0 let totalWidth = titleSize.width + animationSize.width + spacing - let size = CGSize(width: totalWidth + leftPadding + padding, height: 28.0) + let size = CGSize(width: totalWidth + leftPadding + padding, height:36.0) - let backgroundSize = self.background.update( - transition: transition, - component: AnyComponent(RoundedRectangle( - color: component.backgroundColor, - cornerRadius: 14.0 - )), - environment: {}, - containerSize: size - ) - - if let backgroundView = self.background.view { - if backgroundView.superview == nil { - self.addSubview(backgroundView) - } - transition.setPosition(view: backgroundView, position: CGPoint(x: size.width / 2.0, y: size.height / 2.0)) - transition.setBounds(view: backgroundView, bounds: CGRect(origin: CGPoint(), size: backgroundSize)) - } + self.backgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: component.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: component.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition) + transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: size)) if let titleView = self.title.view { if titleView.superview == nil { - self.addSubview(titleView) + self.backgroundView.contentView.addSubview(titleView) } let titlePosition: CGPoint if let _ = component.iconName { @@ -446,7 +422,7 @@ private final class ItemComponent: Component { if let iconView = self.icon.view { if iconView.superview == nil { - self.addSubview(iconView) + self.backgroundView.contentView.addSubview(iconView) } let iconPosition: CGPoint if let _ = component.iconName { diff --git a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift index f8fcc34c..313b5508 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftStoreScreen/Sources/GiftStoreScreen.swift @@ -27,6 +27,8 @@ import UndoUI import ContextUI import LottieComponent import GiftLoadingShimmerView +import EdgeEffect +import GlassBackgroundComponent private let minimumCountToDisplayFilters = 18 @@ -34,17 +36,20 @@ final class GiftStoreScreenComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment let context: AccountContext + let overNavigationContainer: UIView let starsContext: StarsContext let peerId: EnginePeer.Id let gift: StarGift.Gift init( context: AccountContext, + overNavigationContainer: UIView, starsContext: StarsContext, peerId: EnginePeer.Id, gift: StarGift.Gift ) { self.context = context + self.overNavigationContainer = overNavigationContainer self.starsContext = starsContext self.peerId = peerId self.gift = gift @@ -77,11 +82,11 @@ final class GiftStoreScreenComponent: Component { private let emptyResultsTitle = ComponentView() private let clearFilters = ComponentView() - private let topPanel = ComponentView() - private let topSeparator = ComponentView() + private let edgeEffectView: EdgeEffectView private let cancelButton = ComponentView() private let sortButton = ComponentView() - + + private let balanceBackgroundView: GlassBackgroundView private let balanceTitle = ComponentView() private let balanceValue = ComponentView() private let balanceIcon = ComponentView() @@ -106,6 +111,8 @@ final class GiftStoreScreenComponent: Component { private var environment: EnvironmentType? override init(frame: CGRect) { + self.balanceBackgroundView = GlassBackgroundView() + self.scrollView = ScrollView() self.scrollView.showsVerticalScrollIndicator = true self.scrollView.showsHorizontalScrollIndicator = false @@ -120,12 +127,16 @@ final class GiftStoreScreenComponent: Component { self.loadingView = GiftLoadingShimmerView() + self.edgeEffectView = EdgeEffectView() + super.init(frame: frame) self.scrollView.delegate = self self.addSubview(self.scrollView) self.addSubview(self.loadingView) + self.addSubview(self.edgeEffectView) + self.scrollView.layer.addSublayer(self.topOverscrollLayer) } @@ -169,15 +180,8 @@ final class GiftStoreScreenComponent: Component { let availableWidth = self.scrollView.bounds.width let availableHeight = self.scrollView.bounds.height - let contentOffset = self.scrollView.contentOffset.y - - let topPanelAlpha = min(20.0, max(0.0, contentOffset)) / 20.0 - if let topPanelView = self.topPanel.view, let topSeparator = self.topSeparator.view { - transition.setAlpha(view: topPanelView, alpha: topPanelAlpha) - transition.setAlpha(view: topSeparator, alpha: topPanelAlpha) - } - var topInset = environment.navigationHeight + 39.0 + var topInset = environment.navigationHeight + 53.0 if let initialCount = self.initialCount, initialCount < minimumCountToDisplayFilters { topInset = environment.navigationHeight } @@ -889,41 +893,15 @@ final class GiftStoreScreenComponent: Component { var contentHeight: CGFloat = 0.0 contentHeight += environment.navigationHeight - var topPanelHeight = environment.navigationHeight + 39.0 + var topPanelHeight = environment.navigationHeight + 53.0 if let initialCount = self.initialCount, initialCount < minimumCountToDisplayFilters { topPanelHeight = environment.navigationHeight } - - let topPanelSize = self.topPanel.update( - transition: transition, - component: AnyComponent(BlurredBackgroundComponent( - color: theme.rootController.navigationBar.blurredBackgroundColor - )), - environment: {}, - containerSize: CGSize(width: availableSize.width, height: topPanelHeight) - ) - let topSeparatorSize = self.topSeparator.update( - transition: transition, - component: AnyComponent(Rectangle( - color: theme.rootController.navigationBar.separatorColor - )), - environment: {}, - containerSize: CGSize(width: availableSize.width, height: UIScreenPixel) - ) - let topPanelFrame = CGRect(origin: .zero, size: CGSize(width: availableSize.width, height: topPanelSize.height)) - let topSeparatorFrame = CGRect(origin: CGPoint(x: 0.0, y: topPanelSize.height), size: CGSize(width: topSeparatorSize.width, height: topSeparatorSize.height)) - if let topPanelView = self.topPanel.view, let topSeparatorView = self.topSeparator.view { - if topPanelView.superview == nil { - topPanelView.alpha = 0.0 - topSeparatorView.alpha = 0.0 - - self.addSubview(topPanelView) - self.addSubview(topSeparatorView) - } - transition.setFrame(view: topPanelView, frame: topPanelFrame) - transition.setFrame(view: topSeparatorView, frame: topSeparatorFrame) - } + let edgeEffectHeight: CGFloat = topPanelHeight + 8.0 + let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: edgeEffectHeight)) + transition.setFrame(view: self.edgeEffectView, frame: edgeEffectFrame) + self.edgeEffectView.update(content: environment.theme.list.blocksBackgroundColor, blur: true, rect: edgeEffectFrame, edge: .top, edgeSize: min(30, edgeEffectFrame.height), transition: transition) let balanceTitleSize = self.balanceTitle.update( transition: .immediate, @@ -960,26 +938,41 @@ final class GiftStoreScreenComponent: Component { containerSize: availableSize ) - if let balanceTitleView = self.balanceTitle.view, let balanceValueView = self.balanceValue.view, let balanceIconView = self.balanceIcon.view { - if balanceTitleView.superview == nil { - self.addSubview(balanceTitleView) - self.addSubview(balanceValueView) - self.addSubview(balanceIconView) - } - let navigationHeight = environment.navigationHeight - environment.statusBarHeight - let topBalanceOriginY = environment.statusBarHeight + (navigationHeight - balanceTitleSize.height - balanceValueSize.height) / 2.0 - balanceTitleView.center = CGPoint(x: availableSize.width - 16.0 - environment.safeInsets.right - balanceTitleSize.width / 2.0, y: topBalanceOriginY + balanceTitleSize.height / 2.0) - balanceTitleView.bounds = CGRect(origin: .zero, size: balanceTitleSize) - balanceValueView.center = CGPoint(x: availableSize.width - 16.0 - environment.safeInsets.right - balanceValueSize.width / 2.0, y: topBalanceOriginY + balanceTitleSize.height + balanceValueSize.height / 2.0) - balanceValueView.bounds = CGRect(origin: .zero, size: balanceValueSize) - balanceIconView.center = CGPoint(x: availableSize.width - 16.0 - environment.safeInsets.right - balanceValueSize.width - balanceIconSize.width / 2.0 - 2.0, y: topBalanceOriginY + balanceTitleSize.height + balanceValueSize.height / 2.0 - UIScreenPixel) - balanceIconView.bounds = CGRect(origin: .zero, size: balanceIconSize) + if self.balanceBackgroundView.superview == nil { + component.overNavigationContainer.addSubview(self.balanceBackgroundView) } var topInset: CGFloat = 0.0 if environment.statusBarHeight > 0.0 { topInset = environment.statusBarHeight - 6.0 } + + if let balanceTitleView = self.balanceTitle.view, let balanceValueView = self.balanceValue.view, let balanceIconView = self.balanceIcon.view { + if balanceTitleView.superview == nil { + self.balanceBackgroundView.contentView.addSubview(balanceTitleView) + self.balanceBackgroundView.contentView.addSubview(balanceValueView) + self.balanceBackgroundView.contentView.addSubview(balanceIconView) + } + + let topBalanceOriginY = (44.0 - balanceTitleSize.height - balanceValueSize.height) / 2.0 + + let balanceSideInset: CGFloat = 12.0 + var balanceBackgroundSize = CGSize(width: balanceTitleSize.width + balanceSideInset * 2.0, height: 44.0) + balanceBackgroundSize.width = max(balanceBackgroundSize.width, balanceValueSize.width + balanceIconSize.width + 2.0 + balanceSideInset * 2.0) + + let balanceBackgroundFrame = CGRect(origin: CGPoint(x: availableSize.width - environment.safeInsets.right - 16.0 - balanceBackgroundSize.width, y: environment.navigationHeight - 60.0 + 2.0 + floor((60.0 - 44.0) * 0.5)), size: balanceBackgroundSize) + + transition.setFrame(view: self.balanceBackgroundView, frame: balanceBackgroundFrame) + self.balanceBackgroundView.update(size: balanceBackgroundFrame.size, cornerRadius: balanceBackgroundFrame.height * 0.5, isDark: environment.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: environment.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), transition: transition) + + balanceTitleView.center = CGPoint(x: balanceBackgroundFrame.width - balanceSideInset - balanceTitleSize.width / 2.0, y: topBalanceOriginY + balanceTitleSize.height / 2.0) + balanceTitleView.bounds = CGRect(origin: .zero, size: balanceTitleSize) + balanceValueView.center = CGPoint(x: balanceBackgroundFrame.width - balanceSideInset - balanceValueSize.width / 2.0, y: topBalanceOriginY + balanceTitleSize.height + balanceValueSize.height / 2.0) + balanceValueView.bounds = CGRect(origin: .zero, size: balanceValueSize) + balanceIconView.center = CGPoint(x: balanceBackgroundFrame.width - balanceSideInset - balanceValueSize.width - balanceIconSize.width / 2.0 - 2.0, y: topBalanceOriginY + balanceTitleSize.height + balanceValueSize.height / 2.0 - UIScreenPixel) + balanceIconView.bounds = CGRect(origin: .zero, size: balanceIconSize) + } + let titleSize = self.title.update( transition: transition, component: AnyComponent(MultilineTextComponent( @@ -991,9 +984,9 @@ final class GiftStoreScreenComponent: Component { ) if let titleView = self.title.view { if titleView.superview == nil { - self.addSubview(titleView) + component.overNavigationContainer.addSubview(titleView) } - transition.setFrame(view: titleView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - titleSize.width) / 2.0), y: topInset + 10.0), size: titleSize)) + transition.setFrame(view: titleView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - titleSize.width) / 2.0), y: topInset + 22.0), size: titleSize)) } let effectiveCount: Int32 @@ -1018,10 +1011,10 @@ final class GiftStoreScreenComponent: Component { environment: {}, containerSize: CGSize(width: availableSize.width - headerSideInset * 2.0, height: 100.0) ) - let subtitleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - subtitleSize.width) / 2.0), y: topInset + 31.0), size: subtitleSize) + let subtitleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - subtitleSize.width) / 2.0), y: topInset + 43.0), size: subtitleSize) if let subtitleView = self.subtitle.view { if subtitleView.superview == nil { - self.addSubview(subtitleView) + component.overNavigationContainer.addSubview(subtitleView) } transition.setFrame(view: subtitleView, frame: subtitleFrame) } @@ -1144,10 +1137,7 @@ final class GiftStoreScreenComponent: Component { transition: transition, component: AnyComponent(FilterSelectorComponent( context: component.context, - colors: FilterSelectorComponent.Colors( - foreground: theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.65), - background: theme.list.itemSecondaryTextColor.withMultipliedAlpha(0.15) - ), + theme: theme, items: filterItems, selectedItemId: self.selectedFilterId )), @@ -1157,9 +1147,9 @@ final class GiftStoreScreenComponent: Component { if let filterSelectorView = self.filterSelector.view { if filterSelectorView.superview == nil { filterSelectorView.alpha = 0.0 - self.addSubview(filterSelectorView) + component.overNavigationContainer.addSubview(filterSelectorView) } - transition.setFrame(view: filterSelectorView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - filterSize.width) / 2.0), y: topInset + 56.0), size: filterSize)) + transition.setFrame(view: filterSelectorView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - filterSize.width) / 2.0), y: topInset + 60.0 + 18.0), size: filterSize)) if let initialCount = self.initialCount, initialCount >= minimumCountToDisplayFilters { loadingTransition.setAlpha(view: filterSelectorView, alpha: 1.0) @@ -1281,6 +1271,8 @@ final class GiftStoreScreenComponent: Component { public class GiftStoreScreen: ViewControllerComponentContainer { private let context: AccountContext + private let overNavigationContainer: UIView + public var parentController: () -> ViewController? = { return nil } @@ -1293,8 +1285,11 @@ public class GiftStoreScreen: ViewControllerComponentContainer { ) { self.context = context + self.overNavigationContainer = SparseContainerView() + super.init(context: context, component: GiftStoreScreenComponent( context: context, + overNavigationContainer: self.overNavigationContainer, starsContext: starsContext, peerId: peerId, gift: gift @@ -1308,6 +1303,10 @@ public class GiftStoreScreen: ViewControllerComponentContainer { } componentView.scrollToTop() } + + if let navigationBar = self.navigationBar { + navigationBar.customOverBackgroundContentView.insertSubview(self.overNavigationContainer, at: 0) + } } required public init(coder aDecoder: NSCoder) { diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/BUILD b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/BUILD index a5d6d5dc..5d86b40c 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/BUILD +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/BUILD @@ -46,7 +46,6 @@ swift_library( "//submodules/MoreButtonNode", "//submodules/TelegramUI/Components/EmojiStatusComponent", "//submodules/PasswordSetupUI", - "//submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController", "//submodules/TelegramUI/Components/PremiumLockButtonSubtitleComponent", "//submodules/TelegramUI/Components/Stars/StarsBalanceOverlayComponent", "//submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen", @@ -64,6 +63,13 @@ swift_library( "//submodules/TelegramUI/Components/SegmentControlComponent", "//submodules/TelegramUI/Components/Gifts/GiftRemainingCountComponent", "//submodules/TelegramUI/Components/Gifts/InfoParagraphComponent", + "//submodules/TelegramUI/Components/Gifts/TableComponent", + "//submodules/TelegramUI/Components/Gifts/PeerTableCellComponent", + "//submodules/TelegramUI/Components/AlertComponent", + "//submodules/TelegramUI/Components/AvatarComponent", + "//submodules/TelegramUI/Components/AlertComponent/AlertTransferHeaderComponent", + "//submodules/TelegramUI/Components/AlertComponent/AlertTableComponent", + "//submodules/TelegramUI/Components/AlertComponent/AlertInputFieldComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionAcquiredScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionAcquiredScreen.swift index 574e2a4d..bbf99fe9 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionAcquiredScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionAcquiredScreen.swift @@ -19,6 +19,8 @@ import TelegramStringFormatting import GlassBarButtonComponent import GiftItemComponent import EdgeEffect +import TableComponent +import PeerTableCellComponent private final class GiftAuctionAcquiredScreenComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment @@ -373,7 +375,7 @@ private final class GiftAuctionAcquiredScreenComponent: Component { title: environment.strings.Gift_Acquired_Recipient, component: AnyComponent(Button( content: AnyComponent( - PeerCellComponent( + PeerTableCellComponent( context: component.context, theme: environment.theme, strings: environment.strings, @@ -472,7 +474,7 @@ private final class GiftAuctionAcquiredScreenComponent: Component { component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: environment.theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: environment.theme.chat.inputPanel.panelControlColor ) )), action: { [weak self] _ in diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionActiveBidsScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionActiveBidsScreen.swift index 1904b870..c7004ea0 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionActiveBidsScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionActiveBidsScreen.swift @@ -372,7 +372,7 @@ private final class GiftAuctionActiveBidsScreenComponent: Component { if self.backgroundHandleView.image == nil { self.backgroundHandleView.image = generateStretchableFilledCircleImage(diameter: 5.0, color: .white)?.withRenderingMode(.alwaysTemplate) } - self.backgroundHandleView.tintColor = environment.theme.list.itemPrimaryTextColor.withMultipliedAlpha(environment.theme.overallDarkAppearance ? 0.2 : 0.07) + self.backgroundHandleView.tintColor = theme.list.itemPrimaryTextColor.withMultipliedAlpha(theme.overallDarkAppearance ? 0.2 : 0.07) let backgroundHandleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - 36.0) * 0.5), y: 5.0), size: CGSize(width: 36.0, height: 5.0)) if self.backgroundHandleView.superview == nil { self.navigationBarContainer.addSubview(self.backgroundHandleView) @@ -383,13 +383,13 @@ private final class GiftAuctionActiveBidsScreenComponent: Component { transition: .immediate, component: AnyComponent(GlassBarButtonComponent( size: CGSize(width: 40.0, height: 40.0), - backgroundColor: environment.theme.rootController.navigationBar.glassBarButtonBackgroundColor, + backgroundColor: theme.rootController.navigationBar.glassBarButtonBackgroundColor, isDark: environment.theme.overallDarkAppearance, state: .generic, component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: environment.theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: theme.chat.inputPanel.panelControlColor ) )), action: { [weak self] _ in @@ -413,14 +413,13 @@ private final class GiftAuctionActiveBidsScreenComponent: Component { let containerInset: CGFloat = environment.statusBarHeight + 10.0 contentHeight += environment.safeInsets.bottom - var initialContentHeight = contentHeight let clippingY: CGFloat let titleText: String = environment.strings.Gift_ActiveAuctions_Title(Int32(self.auctionStates.count)) let titleSize = self.title.update( transition: .immediate, component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: titleText, font: Font.semibold(17.0), textColor: environment.theme.list.itemPrimaryTextColor)) + text: .plain(NSAttributedString(string: titleText, font: Font.semibold(17.0), textColor: theme.list.itemPrimaryTextColor)) )), environment: {}, containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0) @@ -434,12 +433,12 @@ private final class GiftAuctionActiveBidsScreenComponent: Component { transition.setFrame(view: titleView, frame: titleFrame) } - initialContentHeight = contentHeight + let initialContentHeight = contentHeight let edgeEffectHeight: CGFloat = 80.0 let edgeEffectFrame = CGRect(origin: CGPoint(x: rawSideInset, y: 0.0), size: CGSize(width: fillingSize, height: edgeEffectHeight)) transition.setFrame(view: self.topEdgeEffectView, frame: edgeEffectFrame) - self.topEdgeEffectView.update(content: environment.theme.actionSheet.opaqueItemBackgroundColor, blur: true, alpha: 1.0, rect: edgeEffectFrame, edge: .top, edgeSize: edgeEffectFrame.height, transition: transition) + self.topEdgeEffectView.update(content: theme.actionSheet.opaqueItemBackgroundColor, blur: true, alpha: 1.0, rect: edgeEffectFrame, edge: .top, edgeSize: edgeEffectFrame.height, transition: transition) if self.topEdgeEffectView.superview == nil { self.navigationBarContainer.insertSubview(self.topEdgeEffectView, at: 0) } diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionBidScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionBidScreen.swift index 9863ae0d..d65fbb44 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionBidScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionBidScreen.swift @@ -18,7 +18,6 @@ import RoundedRectWithTailPath import AvatarNode import BundleIconComponent import TextFormat -import CheckComponent import ContextUI import StarsBalanceOverlayComponent import StoryLiveChatMessageComponent @@ -369,17 +368,20 @@ private final class PeerPlaceComponent: Component { let theme: PresentationTheme let color: UIColor let place: Int32? + let placeIsApproximate: Bool let groupingSeparator: String init( theme: PresentationTheme, color: UIColor, place: Int32?, + placeIsApproximate: Bool, groupingSeparator: String ) { self.theme = theme self.color = color self.place = place + self.placeIsApproximate = placeIsApproximate self.groupingSeparator = groupingSeparator } @@ -393,6 +395,9 @@ private final class PeerPlaceComponent: Component { if lhs.place != rhs.place { return false } + if lhs.placeIsApproximate != rhs.placeIsApproximate { + return false + } if lhs.groupingSeparator != rhs.groupingSeparator { return false } @@ -455,7 +460,7 @@ private final class PeerPlaceComponent: Component { var placeString: String if let place = component.place { placeString = presentationStringsFormattedNumber(place, component.groupingSeparator) - if place >= 100 { + if component.placeIsApproximate { placeString = "\(compactNumericCountString(Int(place), decimalSeparator: ".", showDecimalPart: false))+" } } else { @@ -501,6 +506,7 @@ private final class PeerComponent: Component { let groupingSeparator: String let peer: EnginePeer let place: Int32 + let placeIsApproximate: Bool let amount: Int64 let status: Status? let isLast: Bool @@ -512,6 +518,7 @@ private final class PeerComponent: Component { groupingSeparator: String, peer: EnginePeer, place: Int32, + placeIsApproximate: Bool, amount: Int64, status: Status? = nil, isLast: Bool, @@ -522,6 +529,7 @@ private final class PeerComponent: Component { self.groupingSeparator = groupingSeparator self.peer = peer self.place = place + self.placeIsApproximate = placeIsApproximate self.amount = amount self.status = status self.isLast = isLast @@ -541,6 +549,9 @@ private final class PeerComponent: Component { if lhs.place != rhs.place { return false } + if lhs.placeIsApproximate != rhs.placeIsApproximate { + return false + } if lhs.amount != rhs.amount { return false } @@ -623,8 +634,17 @@ private final class PeerComponent: Component { let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } let placeSize = self.place.update( transition: .immediate, - component: AnyComponent(PeerPlaceComponent(theme: component.theme, color: color, place: component.status == .returned ? nil : component.place, groupingSeparator: presentationData.dateTimeFormat.groupingSeparator)), - environment: {}, + component: AnyComponent( + PeerPlaceComponent( + theme: component.theme, + color: color, + place: component.status == .returned ? nil : component.place, + placeIsApproximate: component.placeIsApproximate, + groupingSeparator: presentationData.dateTimeFormat.groupingSeparator + ) + ), + environment: { + }, containerSize: CGSize(width: 40.0, height: 40.0) ) let placeFrame = CGRect(origin: CGPoint(x: 0.0, y: floorToScreenPixels((size.height - placeSize.height) / 2.0)), size: placeSize) @@ -1616,8 +1636,10 @@ private final class GiftAuctionBidScreenComponent: Component { ), in: .current ) - - component.context.starsContext?.load(force: true) + + Queue.mainQueue().after(2.5) { + component.context.starsContext?.load(force: true) + } }, error: { [weak self] _ in guard let self else { return @@ -2061,7 +2083,7 @@ private final class GiftAuctionBidScreenComponent: Component { if case .finished = auctionState?.auctionState, let controller = self.environment?.controller() { if let navigationController = controller.navigationController as? NavigationController { controller.dismiss() - let auctionController = context.sharedContext.makeGiftAuctionViewScreen(context: context, auctionContext: auctionContext, completion: { _, _ in }) + let auctionController = context.sharedContext.makeGiftAuctionViewScreen(context: context, auctionContext: auctionContext, peerId: nil, completion: { _, _ in }) navigationController.pushViewController(auctionController) } } @@ -2213,8 +2235,9 @@ private final class GiftAuctionBidScreenComponent: Component { isBiddingUp = false } - place = giftAuctionState.getPlace(myBid: myBidAmount, myBidDate: myBidDate) ?? 1 - + let placeAndIsApproximate = giftAuctionState.getPlace(myBid: myBidAmount, myBidDate: myBidDate) ?? (1, false) + place = placeAndIsApproximate.place + var bidTitle: String var bidTitleColor: UIColor var bidStatus: PeerComponent.Status? @@ -2247,7 +2270,18 @@ private final class GiftAuctionBidScreenComponent: Component { if let peer = self.peersMap[component.context.account.peerId] { myBidTitleComponent = AnyComponent(PeerHeaderComponent(color: bidTitleColor, dateTimeFormat: environment.dateTimeFormat, title: bidTitle, giftTitle: giftTitle, giftNumber: giftNumber)) - myBidComponent = AnyComponent(PeerComponent(context: component.context, theme: environment.theme, groupingSeparator: environment.dateTimeFormat.groupingSeparator, peer: peer, place: place, amount: myBidAmount, status: bidStatus, isLast: true, action: nil)) + myBidComponent = AnyComponent(PeerComponent( + context: component.context, + theme: environment.theme, + groupingSeparator: environment.dateTimeFormat.groupingSeparator, + peer: peer, + place: place, + placeIsApproximate: placeAndIsApproximate.isApproximate, + amount: myBidAmount, + status: bidStatus, + isLast: true, + action: nil + )) } var i: Int32 = 1 @@ -2259,7 +2293,17 @@ private final class GiftAuctionBidScreenComponent: Component { break } } - topBidsComponents.append((peer.id, AnyComponent(PeerComponent(context: component.context, theme: environment.theme, groupingSeparator: environment.dateTimeFormat.groupingSeparator, peer: peer, place: i, amount: bid, isLast: i == topBidders.count, action: nil)))) + topBidsComponents.append((peer.id, AnyComponent(PeerComponent( + context: component.context, + theme: environment.theme, + groupingSeparator: environment.dateTimeFormat.groupingSeparator, + peer: peer, + place: i, + placeIsApproximate: false, + amount: bid, + isLast: i == topBidders.count, + action: nil + )))) i += 1 } @@ -2645,7 +2689,7 @@ private final class GiftAuctionBidScreenComponent: Component { component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: environment.theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: environment.theme.chat.inputPanel.panelControlColor ) )), action: { [weak self] _ in @@ -2678,7 +2722,7 @@ private final class GiftAuctionBidScreenComponent: Component { content: LottieComponent.AppBundleContent( name: "anim_morewide" ), - color: environment.theme.rootController.navigationBar.glassBarButtonForegroundColor, + color: environment.theme.chat.inputPanel.panelControlColor, size: CGSize(width: 34.0, height: 34.0), playOnce: self.moreButtonPlayOnce ) diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionCustomBidController.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionCustomBidController.swift index d9cf921a..5c7abf83 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionCustomBidController.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionCustomBidController.swift @@ -8,347 +8,287 @@ import TelegramPresentationData import AccountContext import UrlEscaping import ComponentFlow +import AlertComponent import StarsWithdrawalScreen -private final class GiftAuctionCustomBidAlertContentNode: AlertContentNode { - private let theme: PresentationTheme - private let strings: PresentationStrings - private let dateTimeFormat: PresentationDateTimeFormat - private let title: String - private let text: String - private let placeholder: String - private let minValue: Int64 - fileprivate var value: Int64 +func giftAuctionCustomBidController( + context: AccountContext, + title: String, + text: String, + placeholder: String, + action: String, + minValue: Int64, + value: Int64, + apply: @escaping (Int64) -> Void, + cancel: @escaping () -> Void +) -> ViewController { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let strings = presentationData.strings - private let titleNode: ASTextNode - private let textNode: ASTextNode - private let backgroundView = UIImageView() - let amountField = ComponentView() - - private let actionNodesSeparator: ASDisplayNode - private let actionNodes: [TextAlertContentActionNode] - private let actionVerticalSeparators: [ASDisplayNode] - - private let disposable = MetaDisposable() - - private var validLayout: CGSize? - - private let hapticFeedback = HapticFeedback() - - var complete: (() -> Void)? { - didSet { - //self.inputFieldNode.complete = self.complete - } - } - - override var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled - } - - init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, actions: [TextAlertAction], title: String, text: String, placeholder: String, minValue: Int64, value: Int64) { - self.theme = ptheme - self.strings = strings - self.dateTimeFormat = dateTimeFormat - self.title = title - self.text = text - self.placeholder = placeholder - self.minValue = minValue - self.value = value + let inputState = AlertAmountFieldComponent.ExternalState() - self.titleNode = ASTextNode() - self.titleNode.maximumNumberOfLines = 2 - self.textNode = ASTextNode() - self.textNode.maximumNumberOfLines = 8 - -// self.inputFieldNode = GiftAuctionCustomBidInputFieldNode(theme: ptheme, placeholder: placeholder) -// self.inputFieldNode.text = "\(value)" - - self.actionNodesSeparator = ASDisplayNode() - self.actionNodesSeparator.isLayerBacked = true - - self.actionNodes = actions.map { action -> TextAlertContentActionNode in - return TextAlertContentActionNode(theme: theme, action: action) - } - - var actionVerticalSeparators: [ASDisplayNode] = [] - if actions.count > 1 { - for _ in 0 ..< actions.count - 1 { - let separatorNode = ASDisplayNode() - separatorNode.isLayerBacked = true - actionVerticalSeparators.append(separatorNode) - } - } - self.actionVerticalSeparators = actionVerticalSeparators - - super.init() - - self.addSubnode(self.titleNode) - self.addSubnode(self.textNode) - -// self.addSubnode(self.inputFieldNode) - - self.addSubnode(self.actionNodesSeparator) - - for actionNode in self.actionNodes { - self.addSubnode(actionNode) - } - - for separatorNode in self.actionVerticalSeparators { - self.addSubnode(separatorNode) - } - - self.updateTheme(theme) - } - - deinit { - self.disposable.dispose() - } - - override func updateTheme(_ theme: AlertControllerTheme) { - self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.bold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center) - self.textNode.attributedText = NSAttributedString(string: self.text, font: Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center) - - self.actionNodesSeparator.backgroundColor = theme.separatorColor - for actionNode in self.actionNodes { - actionNode.updateTheme(theme) - } - for separatorNode in self.actionVerticalSeparators { - separatorNode.backgroundColor = theme.separatorColor - } - - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) - } - } - - override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - var size = size - size.width = min(size.width, 270.0) - let measureSize = CGSize(width: size.width - 16.0 * 2.0, height: CGFloat.greatestFiniteMagnitude) - - let hadValidLayout = self.validLayout != nil - - self.validLayout = size - - var origin: CGPoint = CGPoint(x: 0.0, y: 20.0) - let spacing: CGFloat = 5.0 - - let titleSize = self.titleNode.measure(measureSize) - transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize)) - origin.y += titleSize.height + 4.0 - - let textSize = self.textNode.measure(measureSize) - transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize)) - origin.y += textSize.height + 6.0 + spacing - - let actionButtonHeight: CGFloat = 44.0 - var minActionsWidth: CGFloat = 0.0 - let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) - let actionTitleInsets: CGFloat = 8.0 - - var effectiveActionLayout = TextAlertContentActionLayout.horizontal - for actionNode in self.actionNodes { - let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) - if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 { - effectiveActionLayout = .vertical - } - switch effectiveActionLayout { - case .horizontal: - minActionsWidth += actionTitleSize.width + actionTitleInsets - case .vertical: - minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) - } - } - - let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 9.0, right: 18.0) - - var contentWidth = max(titleSize.width, minActionsWidth) - contentWidth = max(contentWidth, 234.0) - - var actionsHeight: CGFloat = 0.0 - switch effectiveActionLayout { - case .horizontal: - actionsHeight = actionButtonHeight - case .vertical: - actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) - } - - let resultWidth = contentWidth + insets.left + insets.right - - let fieldWidth = resultWidth - 18.0 - let amountFieldSize = self.amountField.update( - transition: .immediate, - component: AnyComponent( - AmountFieldComponent( - textColor: self.theme.list.itemPrimaryTextColor, - secondaryColor: self.theme.list.itemSecondaryTextColor, - placeholderColor: self.theme.list.itemPlaceholderTextColor, - accentColor: self.theme.list.itemAccentColor, - value: self.value, - minValue: self.minValue, - forceMinValue: false, - allowZero: false, - maxValue: nil, - placeholderText: self.placeholder, - textFieldOffset: CGPoint(x: -4.0, y: -1.0), - labelText: nil, - currency: .stars, - dateTimeFormat: self.dateTimeFormat, - amountUpdated: { [weak self] value in - guard let self else { - return - } - if let value { - self.value = value - } - }, - tag: nil - ) - ), - environment: {}, - containerSize: CGSize(width: fieldWidth, height: 44.0) + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: title) ) - var amountFieldFrame = CGRect(origin: CGPoint(x: floor((resultWidth - fieldWidth) / 2.0), y: origin.y - 2.0), size: amountFieldSize) - if let amountFieldView = self.amountField.view { - if amountFieldView.superview == nil { - amountFieldView.clipsToBounds = true - self.backgroundView.image = generateStretchableFilledCircleImage(diameter: 12.0, color: self.theme.actionSheet.inputHollowBackgroundColor, strokeColor: self.theme.actionSheet.inputBorderColor, strokeWidth: 1.0) - - self.view.addSubview(self.backgroundView) - self.view.addSubview(amountFieldView) - } - self.backgroundView.frame = amountFieldFrame.insetBy(dx: 7.0, dy: 9.0) - amountFieldFrame.size.width -= 14.0 - amountFieldView.frame = amountFieldFrame - } - - let resultSize = CGSize(width: resultWidth, height: titleSize.height + textSize.height + spacing + amountFieldSize.height + actionsHeight + insets.top + insets.bottom + 3.0) - - transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - - var actionOffset: CGFloat = 0.0 - let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) - var separatorIndex = -1 - var nodeIndex = 0 - for actionNode in self.actionNodes { - if separatorIndex >= 0 { - let separatorNode = self.actionVerticalSeparators[separatorIndex] - switch effectiveActionLayout { - case .horizontal: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) - case .vertical: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(text)) + ) + )) + + var applyImpl: (() -> Void)? + content.append(AnyComponentWithIdentity( + id: "input", + component: AnyComponent( + AlertAmountFieldComponent( + context: context, + initialValue: value, + minValue: minValue, + maxValue: nil, + placeholder: placeholder, + isInitiallyFocused: true, + externalState: inputState, + returnKeyAction: { + applyImpl?() } - } - separatorIndex += 1 - - let currentActionWidth: CGFloat - switch effectiveActionLayout { - case .horizontal: - if nodeIndex == self.actionNodes.count - 1 { - currentActionWidth = resultSize.width - actionOffset - } else { - currentActionWidth = actionWidth - } - case .vertical: - currentActionWidth = resultSize.width - } - - let actionNodeFrame: CGRect - switch effectiveActionLayout { - case .horizontal: - actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += currentActionWidth - case .vertical: - actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += actionButtonHeight - } - - transition.updateFrame(node: actionNode, frame: actionNodeFrame) - - nodeIndex += 1 + ) + ) + )) + + let alertController = AlertScreen( + context: context, + configuration: AlertScreen.Configuration(allowInputInset: true), + content: content, + actions: [ + .init(title: strings.Common_Cancel, action: { + cancel() + }), + .init(title: action, type: .default, action: { + applyImpl?() + }, autoDismiss: false) + ] + ) + applyImpl = { + if let value = inputState.value, value >= minValue { + apply(value) + } else { + inputState.resetToMinValue() + inputState.animateError() + } + } + return alertController +} + +private final class AlertAmountFieldComponent: Component { + public typealias EnvironmentType = AlertComponentEnvironment + + public class ExternalState { + public fileprivate(set) var value: Int64? + public fileprivate(set) var animateError: () -> Void = {} + public fileprivate(set) var activateInput: () -> Void = {} + public fileprivate(set) var resetToMinValue: () -> Void = {} + fileprivate let valuePromise = ValuePromise(nil) + public var valueSignal: Signal { + return self.valuePromise.get() } - if !hadValidLayout { + public init() { + } + } + + let context: AccountContext + let initialValue: Int64? + let minValue: Int64? + let maxValue: Int64? + let placeholder: String + let isInitiallyFocused: Bool + let externalState: ExternalState + let returnKeyAction: (() -> Void)? + + public init( + context: AccountContext, + initialValue: Int64? = nil, + minValue: Int64? = nil, + maxValue: Int64? = nil, + placeholder: String, + isInitiallyFocused: Bool = false, + externalState: ExternalState, + returnKeyAction: (() -> Void)? = nil + ) { + self.context = context + self.initialValue = initialValue + self.minValue = minValue + self.maxValue = maxValue + self.placeholder = placeholder + self.isInitiallyFocused = isInitiallyFocused + self.externalState = externalState + self.returnKeyAction = returnKeyAction + } + + public static func ==(lhs: AlertAmountFieldComponent, rhs: AlertAmountFieldComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.initialValue != rhs.initialValue { + return false + } + if lhs.minValue != rhs.minValue { + return false + } + if lhs.maxValue != rhs.maxValue { + return false + } + if lhs.placeholder != rhs.placeholder { + return false + } + if lhs.isInitiallyFocused != rhs.isInitiallyFocused { + return false + } + return true + } + + public final class View: UIView, UITextFieldDelegate { + private let background = ComponentView() + private let amountField = ComponentView() + + private var currentValue: Int64? + + private var component: AlertAmountFieldComponent? + private weak var state: EmptyComponentState? + + private var isUpdating = false + + func activateInput() { if let amountFieldView = self.amountField.view as? AmountFieldComponent.View { amountFieldView.activateInput() - Queue.mainQueue().justDispatch { - amountFieldView.selectAll() - } } } - return resultSize - } - - func animateError() { - if let amountFieldView = self.amountField.view as? AmountFieldComponent.View { - self.value = self.minValue - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) - } - amountFieldView.resetValue() + func resetToMinValue() { + self.currentValue = self.component?.minValue + self.state?.updated() - amountFieldView.animateError() - amountFieldView.selectAll() + if let amountFieldView = self.amountField.view as? AmountFieldComponent.View { + amountFieldView.resetValue() + amountFieldView.selectAll() + } + } + + func animateError() { + if let amountFieldView = self.amountField.view as? AmountFieldComponent.View { + amountFieldView.animateError() + } + } + + func update(component: AlertAmountFieldComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.isUpdating = true + defer { + self.isUpdating = false + } + + if self.component == nil { + self.currentValue = component.initialValue + + component.externalState.animateError = { [weak self] in + self?.animateError() + } + component.externalState.activateInput = { [weak self] in + self?.activateInput() + } + component.externalState.resetToMinValue = { [weak self] in + self?.resetToMinValue() + } + } + + let isFirstTime = self.component == nil + + self.component = component + self.state = state + + let environment = environment[AlertComponentEnvironment.self] + + let topInset: CGFloat = 15.0 + + let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } + let amountFieldSize = self.amountField.update( + transition: .immediate, + component: AnyComponent( + AmountFieldComponent( + textColor: environment.theme.actionSheet.primaryTextColor, + secondaryColor: environment.theme.actionSheet.secondaryTextColor, + placeholderColor: environment.theme.actionSheet.inputPlaceholderColor, + accentColor: environment.theme.actionSheet.controlAccentColor, + value: self.currentValue, + minValue: component.minValue, + forceMinValue: false, + allowZero: false, + maxValue: nil, + placeholderText: component.placeholder, + textFieldOffset: CGPoint(x: -4.0, y: -1.0), + labelText: nil, + currency: .stars, + dateTimeFormat: presentationData.dateTimeFormat, + amountUpdated: { [weak self] value in + guard let self else { + return + } + self.currentValue = value + component.externalState.value = value + component.externalState.valuePromise.set(value) + }, + tag: nil + ) + ), + environment: {}, + containerSize: CGSize(width: availableSize.width, height: 44.0) + ) + var amountFieldFrame = CGRect(origin: CGPoint(x: -16.0, y: topInset - 1.0 + UIScreenPixel), size: amountFieldSize) + if let amountFieldView = self.amountField.view { + if amountFieldView.superview == nil { + amountFieldView.clipsToBounds = true + self.addSubview(amountFieldView) + } + amountFieldFrame.size.width -= 14.0 + amountFieldView.frame = amountFieldFrame + } + + let backgroundPadding: CGFloat = 14.0 + let size = CGSize(width: availableSize.width, height: 50.0) + + let backgroundSize = self.background.update( + transition: transition, + component: AnyComponent( + FilledRoundedRectangleComponent(color: environment.theme.actionSheet.primaryTextColor.withMultipliedAlpha(0.1), cornerRadius: .value(25.0), smoothCorners: false) + ), + environment: {}, + containerSize: CGSize(width: size.width + backgroundPadding * 2.0, height: size.height) + ) + let backgroundFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - backgroundSize.width) / 2.0), y: topInset ), size: backgroundSize) + if let backgroundView = self.background.view { + if backgroundView.superview == nil { + self.addSubview(backgroundView) + } + transition.setFrame(view: backgroundView, frame: backgroundFrame) + } + + if isFirstTime && component.isInitiallyFocused { + self.activateInput() + } + + return CGSize(width: availableSize.width, height: size.height + topInset) } } - func deactivateInput() { - if let amountFieldView = self.amountField.view as? AmountFieldComponent.View { - amountFieldView.deactivateInput() - } + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) } } - -func giftAuctionCustomBidController(context: AccountContext, title: String, text: String, placeholder: String, action: String, minValue: Int64, value: Int64, apply: @escaping (Int64) -> Void, cancel: @escaping () -> Void) -> AlertController { - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - - var dismissImpl: ((Bool) -> Void)? - var applyImpl: (() -> Void)? - - let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - dismissImpl?(true) - cancel() - }), TextAlertAction(type: .defaultAction, title: action, action: { - applyImpl?() - })] - - let contentNode = GiftAuctionCustomBidAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, actions: actions, title: title, text: text, placeholder: placeholder, minValue: minValue, value: value) - contentNode.complete = { - applyImpl?() - } - applyImpl = { [weak contentNode] in - guard let contentNode = contentNode else { - return - } - let value = contentNode.value - if value < minValue { - contentNode.animateError() - } else { - dismissImpl?(true) - apply(contentNode.value) - } - } - - let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode) - let presentationDataDisposable = context.sharedContext.presentationData.start(next: { [weak controller] presentationData in - controller?.theme = AlertControllerTheme(presentationData: presentationData) - }) - controller.dismissed = { byTapOutside in - presentationDataDisposable.dispose() - if byTapOutside { - cancel() - } - } - dismissImpl = { [weak controller, weak contentNode] animated in - contentNode?.deactivateInput() - let _ = contentNode - if animated { - controller?.dismissAnimated() - } else { - controller?.dismiss() - } - } - return controller -} diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionInfoScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionInfoScreen.swift index e4086acc..b4e39c8b 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionInfoScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionInfoScreen.swift @@ -256,7 +256,7 @@ private final class GiftAuctionInfoSheetContent: CombinedComponent { component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: theme.chat.inputPanel.panelControlColor ) )), action: { [weak state] _ in @@ -296,8 +296,7 @@ private final class GiftAuctionInfoSheetContent: CombinedComponent { style: .glass, color: theme.list.itemCheckColors.fillColor, foreground: theme.list.itemCheckColors.foregroundColor, - pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9), - cornerRadius: 10.0, + pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9) ), content: AnyComponentWithIdentity( id: AnyHashable(0), diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionViewScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionViewScreen.swift index 04a2bb12..9f6a5f72 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionViewScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionViewScreen.swift @@ -28,23 +28,27 @@ import ButtonComponent import UndoUI import LottieComponent import AnimatedTextComponent +import TableComponent private final class GiftAuctionViewSheetContent: CombinedComponent { typealias EnvironmentType = ViewControllerComponentContainer.Environment let context: AccountContext let auctionContext: GiftAuctionContext + let peerId: EnginePeer.Id? let animateOut: ActionSlot> let getController: () -> ViewController? init( context: AccountContext, auctionContext: GiftAuctionContext, + peerId: EnginePeer.Id?, animateOut: ActionSlot>, getController: @escaping () -> ViewController? ) { self.context = context self.auctionContext = auctionContext + self.peerId = peerId self.animateOut = animateOut self.getController = getController } @@ -61,6 +65,7 @@ private final class GiftAuctionViewSheetContent: CombinedComponent { private let context: AccountContext private let auctionContext: GiftAuctionContext + private let peerId: EnginePeer.Id? private let animateOut: ActionSlot> private let getController: () -> ViewController? @@ -92,11 +97,13 @@ private final class GiftAuctionViewSheetContent: CombinedComponent { init( context: AccountContext, auctionContext: GiftAuctionContext, + peerId: EnginePeer.Id?, animateOut: ActionSlot>, getController: @escaping () -> ViewController? ) { self.context = context self.auctionContext = auctionContext + self.peerId = peerId self.animateOut = animateOut self.getController = getController @@ -398,7 +405,7 @@ private final class GiftAuctionViewSheetContent: CombinedComponent { } let storeController = self.context.sharedContext.makeGiftStoreController( context: self.context, - peerId: self.context.account.peerId, + peerId: self.peerId ?? self.context.account.peerId, gift: gift ) controller.push(storeController) @@ -472,7 +479,7 @@ private final class GiftAuctionViewSheetContent: CombinedComponent { } func makeState() -> State { - return State(context: self.context, auctionContext: self.auctionContext, animateOut: self.animateOut, getController: self.getController) + return State(context: self.context, auctionContext: self.auctionContext, peerId: self.peerId, animateOut: self.animateOut, getController: self.getController) } static var body: Body { @@ -1075,7 +1082,7 @@ private final class GiftAuctionViewSheetContent: CombinedComponent { guard let state, let attributes = state.giftUpgradeAttributes else { return } - let variantsController = component.context.sharedContext.makeGiftUpgradeVariantsPreviewScreen(context: component.context, gift: .generic(gift), attributes: attributes) + let variantsController = component.context.sharedContext.makeGiftUpgradeVariantsScreen(context: component.context, gift: .generic(gift), attributes: attributes, selectedAttributes: nil, focusedAttribute: nil) environment.controller()?.push(variantsController) }, animateScale: false), availableSize: CGSize(width: context.availableSize.width - 64.0, height: context.availableSize.height), @@ -1276,13 +1283,16 @@ final class GiftAuctionViewSheetComponent: CombinedComponent { let context: AccountContext let auctionContext: GiftAuctionContext + let peerId: EnginePeer.Id? init( context: AccountContext, - auctionContext: GiftAuctionContext + auctionContext: GiftAuctionContext, + peerId: EnginePeer.Id? ) { self.context = context self.auctionContext = auctionContext + self.peerId = peerId } static func ==(lhs: GiftAuctionViewSheetComponent, rhs: GiftAuctionViewSheetComponent) -> Bool { @@ -1307,6 +1317,7 @@ final class GiftAuctionViewSheetComponent: CombinedComponent { content: AnyComponent(GiftAuctionViewSheetContent( context: context.component.context, auctionContext: context.component.auctionContext, + peerId: context.component.peerId, animateOut: animateOut, getController: controller )), @@ -1391,6 +1402,7 @@ public final class GiftAuctionViewScreen: ViewControllerComponentContainer { public init( context: AccountContext, auctionContext: GiftAuctionContext, + peerId: EnginePeer.Id?, completion: @escaping (Signal<[GiftAuctionAcquiredGift], NoError>, [StarGift.UniqueGift.Attribute]?) -> Void ) { self.completion = completion @@ -1399,7 +1411,8 @@ public final class GiftAuctionViewScreen: ViewControllerComponentContainer { context: context, component: GiftAuctionViewSheetComponent( context: context, - auctionContext: auctionContext + auctionContext: auctionContext, + peerId: peerId ), navigationBarAppearance: .none, statusBarStyle: .ignore, diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionWearPreviewScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionWearPreviewScreen.swift index 26014f36..8198689c 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionWearPreviewScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftAuctionWearPreviewScreen.swift @@ -26,6 +26,7 @@ import GiftAnimationComponent import GlassBarButtonComponent import GiftRemainingCountComponent import AnimatedTextComponent +import AvatarComponent private final class GiftAuctionWearPreviewSheetContent: CombinedComponent { typealias EnvironmentType = ViewControllerComponentContainer.Environment @@ -199,7 +200,7 @@ private final class GiftAuctionWearPreviewSheetContent: CombinedComponent { return { context in let environment = context.environment[ViewControllerComponentContainer.Environment.self].value let component = context.component - let theme = environment.theme + let theme = environment.theme.withModalBlocksBackground() let strings = environment.strings let nameDisplayOrder = component.context.sharedContext.currentPresentationData.with { $0 }.nameDisplayOrder let controller = environment.controller diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftOfferAlertController.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftOfferAlertController.swift index 7f9a368d..7b7409f4 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftOfferAlertController.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftOfferAlertController.swift @@ -3,478 +3,32 @@ import UIKit import AsyncDisplayKit import Display import ComponentFlow +import SwiftSignalKit import Postbox import TelegramCore import TelegramPresentationData -import TelegramUIPreferences import AccountContext import AppBundle -import AvatarNode -import Markdown import GiftItemComponent -import ChatMessagePaymentAlertController -import ActivityIndicator import TooltipUI import MultilineTextComponent -import BalancedTextComponent +import BundleIconComponent import TelegramStringFormatting - -private final class GiftOfferAlertContentNode: AlertContentNode { - private let context: AccountContext - private let strings: PresentationStrings - private var presentationTheme: PresentationTheme - private let title: String - private let text: String - private let amount: CurrencyAmount - private let gift: StarGift.UniqueGift - - private let titleNode: ASTextNode - private let giftView = ComponentView() - private let textNode: ASTextNode - private let arrowNode: ASImageNode - private let avatarNode: AvatarNode - private let tableView = ComponentView() - private let valueDelta = ComponentView() - - private let modelButtonTag = GenericComponentViewTag() - private let backdropButtonTag = GenericComponentViewTag() - private let symbolButtonTag = GenericComponentViewTag() - - fileprivate var getController: () -> ViewController? = { return nil} - - private let actionNodesSeparator: ASDisplayNode - private let actionNodes: [TextAlertContentActionNode] - private let actionVerticalSeparators: [ASDisplayNode] - - private var activityIndicator: ActivityIndicator? - - private var validLayout: CGSize? - - var inProgress = false { - didSet { - if let size = self.validLayout { - let _ = self.updateLayout(size: size, transition: .immediate) - } - } - } - - override var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled - } - - init( - context: AccountContext, - theme: AlertControllerTheme, - ptheme: PresentationTheme, - strings: PresentationStrings, - gift: StarGift.UniqueGift, - peer: EnginePeer, - title: String, - text: String, - amount: CurrencyAmount, - actions: [TextAlertAction] - ) { - self.context = context - self.strings = strings - self.presentationTheme = ptheme - self.title = title - self.text = text - self.amount = amount - self.gift = gift - - self.titleNode = ASTextNode() - self.titleNode.maximumNumberOfLines = 0 - - self.textNode = ASTextNode() - self.textNode.maximumNumberOfLines = 0 - - self.arrowNode = ASImageNode() - self.arrowNode.displaysAsynchronously = false - self.arrowNode.displayWithoutProcessing = true - - self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0)) - - self.actionNodesSeparator = ASDisplayNode() - self.actionNodesSeparator.isLayerBacked = true - - self.actionNodes = actions.map { action -> TextAlertContentActionNode in - return TextAlertContentActionNode(theme: theme, action: action) - } - - var actionVerticalSeparators: [ASDisplayNode] = [] - if actions.count > 1 { - for _ in 0 ..< actions.count - 1 { - let separatorNode = ASDisplayNode() - separatorNode.isLayerBacked = true - actionVerticalSeparators.append(separatorNode) - } - } - self.actionVerticalSeparators = actionVerticalSeparators - - super.init() - - self.addSubnode(self.titleNode) - self.addSubnode(self.textNode) - self.addSubnode(self.arrowNode) - self.addSubnode(self.avatarNode) - - self.addSubnode(self.actionNodesSeparator) - - for actionNode in self.actionNodes { - self.addSubnode(actionNode) - } - - for separatorNode in self.actionVerticalSeparators { - self.addSubnode(separatorNode) - } - - self.updateTheme(theme) - - self.avatarNode.setPeer(context: context, theme: ptheme, peer: peer) - } - - override func updateTheme(_ theme: AlertControllerTheme) { - self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.semibold(17.0), textColor: theme.primaryColor) - self.textNode.attributedText = parseMarkdownIntoAttributedString(self.text, attributes: MarkdownAttributes( - body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor), - bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: theme.primaryColor), - link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor), - linkAttribute: { url in - return ("URL", url) - } - ), textAlignment: .center) - self.arrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Peer Info/AlertArrow"), color: theme.secondaryColor.withAlphaComponent(0.9)) - - self.actionNodesSeparator.backgroundColor = theme.separatorColor - for actionNode in self.actionNodes { - actionNode.updateTheme(theme) - } - for separatorNode in self.actionVerticalSeparators { - separatorNode.backgroundColor = theme.separatorColor - } - - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) - } - } - - fileprivate func dismissAllTooltips() { - guard let controller = self.getController() else { - return - } - controller.window?.forEachController({ controller in - if let controller = controller as? TooltipScreen { - controller.dismiss(inPlace: false) - } - }) - controller.forEachController({ controller in - if let controller = controller as? TooltipScreen { - controller.dismiss(inPlace: false) - } - return true - }) - } - - func showAttributeInfo(tag: Any, text: String) { - guard let controller = self.getController() else { - return - } - self.dismissAllTooltips() - - guard let sourceView = self.tableView.findTaggedView(tag: tag), let absoluteLocation = sourceView.superview?.convert(sourceView.center, to: controller.view) else { - return - } - - let location = CGRect(origin: CGPoint(x: absoluteLocation.x, y: absoluteLocation.y - 12.0), size: CGSize()) - let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: text), style: .wide, location: .point(location, .bottom), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _, _ in - return .dismiss(consume: false) - }) - controller.present(tooltipController, in: .current) - } - - override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - var size = size - size.width = min(size.width, 310.0) - - let strings = self.strings - - self.validLayout = size - - var origin: CGPoint = CGPoint(x: 0.0, y: 20.0) - - let avatarSize = CGSize(width: 60.0, height: 60.0) - self.avatarNode.updateSize(size: avatarSize) - - let giftFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) - 44.0, y: origin.y), size: avatarSize) - - let _ = self.giftView.update( - transition: .immediate, - component: AnyComponent( - GiftItemComponent( - context: self.context, - theme: self.presentationTheme, - strings: strings, - peer: nil, - subject: .uniqueGift(gift: self.gift, price: nil), - mode: .thumbnail - ) - ), - environment: {}, - containerSize: avatarSize - ) - if let view = self.giftView.view { - if view.superview == nil { - self.view.addSubview(view) - } - view.frame = giftFrame - } - - if let arrowImage = self.arrowNode.image { - let arrowFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - arrowImage.size.width) / 2.0), y: origin.y + floorToScreenPixels((avatarSize.height - arrowImage.size.height) / 2.0)), size: arrowImage.size) - transition.updateFrame(node: self.arrowNode, frame: arrowFrame) - } - - let avatarFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) + 44.0, y: origin.y), size: avatarSize) - transition.updateFrame(node: self.avatarNode, frame: avatarFrame) - - origin.y += avatarSize.height + 17.0 - - let titleSize = self.titleNode.measure(CGSize(width: size.width - 32.0, height: size.height)) - transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize)) - origin.y += titleSize.height + 5.0 - - let textSize = self.textNode.measure(CGSize(width: size.width - 32.0, height: size.height)) - transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize)) - origin.y += textSize.height + 10.0 - - let actionButtonHeight: CGFloat = 44.0 - var minActionsWidth: CGFloat = 0.0 - let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) - let actionTitleInsets: CGFloat = 8.0 - - for actionNode in self.actionNodes { - let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) - minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) - } - - let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0) - - let contentWidth = max(size.width, minActionsWidth) - - let actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) - - let tableFont = Font.regular(15.0) - let tableTextColor = self.presentationTheme.list.itemPrimaryTextColor - - var tableItems: [TableComponent.Item] = [] - let order: [StarGift.UniqueGift.Attribute.AttributeType] = [ - .model, .pattern, .backdrop, .originalInfo - ] - - var attributeMap: [StarGift.UniqueGift.Attribute.AttributeType: StarGift.UniqueGift.Attribute] = [:] - for attribute in self.gift.attributes { - attributeMap[attribute.attributeType] = attribute - } - - for type in order { - if let attribute = attributeMap[type] { - let id: String? - let title: String? - let value: NSAttributedString - let percentage: Float? - let tag: AnyObject? - - switch attribute { - case let .model(name, _, rarity): - id = "model" - title = strings.Gift_Unique_Model - value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor) - percentage = Float(rarity) * 0.1 - tag = self.modelButtonTag - case let .backdrop(name, _, _, _, _, _, rarity): - id = "backdrop" - title = strings.Gift_Unique_Backdrop - value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor) - percentage = Float(rarity) * 0.1 - tag = self.backdropButtonTag - case let .pattern(name, _, rarity): - id = "pattern" - title = strings.Gift_Unique_Symbol - value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor) - percentage = Float(rarity) * 0.1 - tag = self.symbolButtonTag - case .originalInfo: - continue - } - - var items: [AnyComponentWithIdentity] = [] - items.append( - AnyComponentWithIdentity( - id: AnyHashable(0), - component: AnyComponent( - MultilineTextComponent(text: .plain(value)) - ) - ) - ) - if let percentage, let tag { - items.append(AnyComponentWithIdentity( - id: AnyHashable(1), - component: AnyComponent(Button( - content: AnyComponent(ButtonContentComponent( - context: self.context, - text: formatPercentage(percentage), - color: self.presentationTheme.list.itemAccentColor - )), - action: { [weak self] in - self?.showAttributeInfo(tag: tag, text: strings.Gift_Unique_AttributeDescription(formatPercentage(percentage)).string) - } - ).tagged(tag)) - )) - } - let itemComponent = AnyComponent( - HStack(items, spacing: 4.0) - ) - - tableItems.append(.init( - id: id, - title: title, - hasBackground: false, - component: itemComponent - )) - } - } - - let tableSize = self.tableView.update( - transition: .immediate, - component: AnyComponent( - TableComponent( - theme: self.presentationTheme, - items: tableItems, - semiTransparent: true - ) - ), - environment: {}, - containerSize: CGSize(width: contentWidth - 32.0, height: size.height) - ) - let tableFrame = CGRect(origin: CGPoint(x: 16.0, y: avatarSize.height + titleSize.height + textSize.height + 60.0), size: tableSize) - if let view = self.tableView.view { - if view.superview == nil { - self.view.addSubview(view) - } - view.frame = tableFrame - } - - var valueDeltaHeight: CGFloat = 0.0 - if let valueAmount = self.gift.valueUsdAmount { - let resaleConfiguration = StarsSubscriptionConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 }) - - let usdRate: Double - switch self.amount.currency { - case .stars: - usdRate = Double(resaleConfiguration.usdWithdrawRate) / 1000.0 / 100.0 - case .ton: - usdRate = Double(resaleConfiguration.tonUsdRate) / 1000.0 / 1000000.0 - } - let offerUsdValue = Double(self.amount.amount.value) * usdRate - let giftUsdValue = Double(valueAmount) / 100.0 - - let fraction = giftUsdValue / offerUsdValue - let percentage = Int(fraction * 100) - 100 - - if percentage > 20 { - let textColor = self.presentationTheme.list.itemDestructiveColor - let markdownAttributes = MarkdownAttributes( - body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: textColor), - bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: textColor), - link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: textColor), - linkAttribute: { url in - return ("URL", url) - } - ) - let valueDeltaSize = self.valueDelta.update( - transition: .immediate, - component: AnyComponent( - BalancedTextComponent( - text: .markdown(text: strings.Chat_GiftPurchaseOffer_AcceptConfirmation_BadValue("\(percentage)%").string, attributes: markdownAttributes), - horizontalAlignment: .center, - maximumNumberOfLines: 0 - ) - ), - environment: {}, - containerSize: CGSize(width: contentWidth - 32.0, height: size.height) - ) - let valueDeltaFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - valueDeltaSize.width) / 2.0), y: avatarSize.height + titleSize.height + textSize.height + 73.0 + tableSize.height), size: valueDeltaSize) - if let view = self.valueDelta.view { - if view.superview == nil { - self.view.addSubview(view) - } - view.frame = valueDeltaFrame - } - valueDeltaHeight += valueDeltaSize.height + 10.0 - } - } - - let resultSize = CGSize(width: contentWidth, height: avatarSize.height + titleSize.height + textSize.height + tableSize.height + actionsHeight + valueDeltaHeight + 40.0 + insets.top + insets.bottom) - transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - - var actionOffset: CGFloat = 0.0 - var separatorIndex = -1 - var nodeIndex = 0 - for actionNode in self.actionNodes { - if separatorIndex >= 0 { - let separatorNode = self.actionVerticalSeparators[separatorIndex] - do { - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - } - } - separatorIndex += 1 - - let currentActionWidth: CGFloat - do { - currentActionWidth = resultSize.width - } - - let actionNodeFrame: CGRect - do { - actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += actionButtonHeight - } - - transition.updateFrame(node: actionNode, frame: actionNodeFrame) - - nodeIndex += 1 - } - - if self.inProgress { - let activityIndicator: ActivityIndicator - if let current = self.activityIndicator { - activityIndicator = current - } else { - activityIndicator = ActivityIndicator(type: .custom(self.presentationTheme.list.freeInputField.controlColor, 18.0, 1.5, false)) - self.addSubnode(activityIndicator) - } - - if let actionNode = self.actionNodes.first { - actionNode.isUserInteractionEnabled = false - actionNode.isHidden = false - - let indicatorSize = CGSize(width: 22.0, height: 22.0) - transition.updateFrame(node: activityIndicator, frame: CGRect(origin: CGPoint(x: actionNode.frame.minX + floor((actionNode.frame.width - indicatorSize.width) / 2.0), y: actionNode.frame.minY + floor((actionNode.frame.height - indicatorSize.height) / 2.0)), size: indicatorSize)) - } - } - - return resultSize - } -} +import AlertComponent +import TableComponent +import AvatarComponent +import AlertTransferHeaderComponent +import AlertTableComponent public func giftOfferAlertController( context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal)?, gift: StarGift.UniqueGift, peer: EnginePeer, amount: CurrencyAmount, commit: @escaping () -> Void -) -> AlertController { - let presentationData = context.sharedContext.currentPresentationData.with { $0 } +) -> ViewController { + let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } let strings = presentationData.strings let title = strings.Chat_GiftPurchaseOffer_AcceptConfirmation_Title @@ -485,7 +39,7 @@ public func giftOfferAlertController( case .stars: priceString = strings.Chat_GiftPurchaseOffer_AcceptConfirmation_Text_Stars(Int32(clamping: amount.amount.value)) case .ton: - priceString = "\(amount.amount) TON" + priceString = formatTonAmountText(amount.amount.value, dateTimeFormat: presentationData.dateTimeFormat) + " TON" } let resaleConfiguration = StarsSubscriptionConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) @@ -502,27 +56,205 @@ public func giftOfferAlertController( let giftTitle = "\(gift.title) #\(formatCollectibleNumber(gift.number, dateTimeFormat: presentationData.dateTimeFormat))" let text = strings.Chat_GiftPurchaseOffer_AcceptConfirmation_Text(giftTitle, peer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder), priceString, finalPriceString).string - var contentNode: GiftOfferAlertContentNode? - var dismissImpl: ((Bool) -> Void)? - let actions: [TextAlertAction] = [TextAlertAction(type: .defaultAction, title: buttonText, action: { [weak contentNode] in - contentNode?.inProgress = true - commit() - }), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - dismissImpl?(true) - })] + let tableFont = Font.regular(15.0) + let tableTextColor = presentationData.theme.list.itemPrimaryTextColor - contentNode = GiftOfferAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: strings, gift: gift, peer: peer, title: title, text: text, amount: amount, actions: actions) + let modelButtonTag = GenericComponentViewTag() + let backdropButtonTag = GenericComponentViewTag() + let symbolButtonTag = GenericComponentViewTag() + var showAttributeInfoImpl: ((Any, String) -> Void)? - let controller = ChatMessagePaymentAlertController(context: context, presentationData: presentationData, contentNode: contentNode!, navigationController: nil, chatPeerId: context.account.peerId, showBalance: false) - contentNode?.getController = { [weak controller] in - return controller + var tableItems: [TableComponent.Item] = [] + let order: [StarGift.UniqueGift.Attribute.AttributeType] = [ + .model, .pattern, .backdrop, .originalInfo + ] + + var attributeMap: [StarGift.UniqueGift.Attribute.AttributeType: StarGift.UniqueGift.Attribute] = [:] + for attribute in gift.attributes { + attributeMap[attribute.attributeType] = attribute } - dismissImpl = { [weak controller] animated in - if animated { - controller?.dismissAnimated() - } else { - controller?.dismiss() + + for type in order { + if let attribute = attributeMap[type] { + let id: String? + let title: String? + let value: NSAttributedString + let percentage: Float? + let tag: AnyObject? + + switch attribute { + case let .model(name, _, rarity): + id = "model" + title = strings.Gift_Unique_Model + value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor) + percentage = Float(rarity) * 0.1 + tag = modelButtonTag + case let .backdrop(name, _, _, _, _, _, rarity): + id = "backdrop" + title = strings.Gift_Unique_Backdrop + value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor) + percentage = Float(rarity) * 0.1 + tag = backdropButtonTag + case let .pattern(name, _, rarity): + id = "pattern" + title = strings.Gift_Unique_Symbol + value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor) + percentage = Float(rarity) * 0.1 + tag = symbolButtonTag + case .originalInfo: + continue + } + + var items: [AnyComponentWithIdentity] = [] + items.append( + AnyComponentWithIdentity( + id: AnyHashable(0), + component: AnyComponent( + MultilineTextComponent(text: .plain(value)) + ) + ) + ) + if let percentage, let tag { + items.append(AnyComponentWithIdentity( + id: AnyHashable(1), + component: AnyComponent(Button( + content: AnyComponent(ButtonContentComponent( + context: context, + text: formatPercentage(percentage), + color: presentationData.theme.list.itemAccentColor + )), + action: { + showAttributeInfoImpl?(tag, strings.Gift_Unique_AttributeDescription(formatPercentage(percentage)).string) + } + ).tagged(tag)) + )) + } + let itemComponent = AnyComponent( + HStack(items, spacing: 4.0) + ) + + tableItems.append(.init( + id: id, + title: title, + hasBackground: false, + component: itemComponent + )) } } - return controller + + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "header", + component: AnyComponent( + AlertTransferHeaderComponent( + fromComponent: AnyComponentWithIdentity(id: "gift", component: AnyComponent( + GiftItemComponent( + context: context, + theme: presentationData.theme, + strings: strings, + peer: nil, + subject: .uniqueGift(gift: gift, price: nil), + mode: .thumbnail + ) + )), + toComponent: AnyComponentWithIdentity(id: "avatar", component: AnyComponent( + AvatarComponent( + context: context, + theme: presentationData.theme, + peer: peer + ) + )), + type: .transfer + ) + ) + )) + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: title) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(text)) + ) + )) + content.append(AnyComponentWithIdentity( + id: "table", + component: AnyComponent( + AlertTableComponent(items: tableItems) + ) + )) + + if let valueAmount = gift.valueUsdAmount { + let resaleConfiguration = StarsSubscriptionConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) + + let usdRate: Double + switch amount.currency { + case .stars: + usdRate = Double(resaleConfiguration.usdWithdrawRate) / 1000.0 / 100.0 + case .ton: + usdRate = Double(resaleConfiguration.tonUsdRate) / 1000.0 / 1000000.0 + } + let offerUsdValue = Double(amount.amount.value) * usdRate + let giftUsdValue = Double(valueAmount) / 100.0 + + let fraction = giftUsdValue / offerUsdValue + let percentage = Int(fraction * 100) - 100 + + if percentage > 20 { + let warningText = strings.Chat_GiftPurchaseOffer_AcceptConfirmation_BadValue("\(percentage)%").string + content.append(AnyComponentWithIdentity( + id: "warning", + component: AnyComponent( + AlertTextComponent(content: .plain(warningText), color: .destructive, style: .plain(.small)) + ) + )) + } + } + + let updatedPresentationDataSignal = updatedPresentationData?.signal ?? context.sharedContext.presentationData + let alertController = AlertScreen( + configuration: AlertScreen.Configuration(actionAlignment: .vertical, dismissOnOutsideTap: true, allowInputInset: false), + content: content, + actions: [ + .init(title: buttonText, type: .default, action: { + commit() + }), + .init(title: strings.Common_Cancel) + ], + updatedPresentationData: (initial: presentationData, signal: updatedPresentationDataSignal) + ) + + var dismissAllTooltipsImpl: (() -> Void)? + showAttributeInfoImpl = { [weak alertController] tag, text in + dismissAllTooltipsImpl?() + guard let alertController, let sourceView = alertController.node.hostView.findTaggedView(tag: tag), let absoluteLocation = sourceView.superview?.convert(sourceView.center, to: alertController.view) else { + return + } + + let location = CGRect(origin: CGPoint(x: absoluteLocation.x, y: absoluteLocation.y - 12.0), size: CGSize()) + let tooltipController = TooltipScreen(account: context.account, sharedContext: context.sharedContext, text: .plain(text: text), style: .wide, location: .point(location, .bottom), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _, _ in + return .dismiss(consume: false) + }) + alertController.present(tooltipController, in: .current) + } + dismissAllTooltipsImpl = { [weak alertController] in + guard let alertController else { + return + } + alertController.window?.forEachController({ controller in + if let controller = controller as? TooltipScreen { + controller.dismiss(inPlace: false) + } + }) + alertController.forEachController({ controller in + if let controller = controller as? TooltipScreen { + controller.dismiss(inPlace: false) + } + return true + }) + } + return alertController } diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftPurchaseAlertController.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftPurchaseAlertController.swift index 805bf3bf..d2cc73af 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftPurchaseAlertController.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftPurchaseAlertController.swift @@ -11,440 +11,16 @@ import TelegramUIPreferences import AccountContext import AppBundle import AvatarNode -import Markdown import GiftItemComponent import ChatMessagePaymentAlertController -import ActivityIndicator import TabSelectorComponent import BundleIconComponent import MultilineTextComponent import TelegramStringFormatting import TooltipUI - -private final class GiftPurchaseAlertContentNode: AlertContentNode { - private let context: AccountContext - private let strings: PresentationStrings - private var presentationTheme: PresentationTheme - private let gift: StarGift.UniqueGift - private let peer: EnginePeer - - fileprivate var currency: CurrencyAmount.Currency - - fileprivate let header = ComponentView() - private let title = ComponentView() - private let text = ComponentView() - private let giftView = ComponentView() - private let arrow = ComponentView() - private let avatarNode: AvatarNode - - private let actionNodesSeparator: ASDisplayNode - private let actionNodes: [TextAlertContentActionNode] - private let actionVerticalSeparators: [ASDisplayNode] - - private var activityIndicator: ActivityIndicator? - - private var validLayout: CGSize? - - var inProgress = false { - didSet { - if let size = self.validLayout { - let _ = self.updateLayout(size: size, transition: .immediate) - } - } - } - - var updatedCurrency: (CurrencyAmount.Currency) -> Void = { _ in } - - override var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled - } - - init( - context: AccountContext, - theme: AlertControllerTheme, - presentationTheme: PresentationTheme, - strings: PresentationStrings, - gift: StarGift.UniqueGift, - peer: EnginePeer, - actions: [TextAlertAction] - ) { - self.context = context - self.strings = strings - self.presentationTheme = presentationTheme - self.gift = gift - self.peer = peer - - self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0)) - - self.actionNodesSeparator = ASDisplayNode() - self.actionNodesSeparator.isLayerBacked = true - - self.actionNodes = actions.map { action -> TextAlertContentActionNode in - return TextAlertContentActionNode(theme: theme, action: action) - } - - var actionVerticalSeparators: [ASDisplayNode] = [] - if actions.count > 1 { - for _ in 0 ..< actions.count - 1 { - let separatorNode = ASDisplayNode() - separatorNode.isLayerBacked = true - actionVerticalSeparators.append(separatorNode) - } - } - self.actionVerticalSeparators = actionVerticalSeparators - - self.currency = self.gift.resellForTonOnly ? .ton : .stars - - super.init() - - self.addSubnode(self.avatarNode) - - self.addSubnode(self.actionNodesSeparator) - - for actionNode in self.actionNodes { - self.addSubnode(actionNode) - } - - for separatorNode in self.actionVerticalSeparators { - self.addSubnode(separatorNode) - } - - self.updateTheme(theme) - - self.avatarNode.setPeer(context: context, theme: presentationTheme, peer: peer) - } - - override func updateTheme(_ theme: AlertControllerTheme) { - self.actionNodesSeparator.backgroundColor = theme.separatorColor - for actionNode in self.actionNodes { - actionNode.updateTheme(theme) - } - for separatorNode in self.actionVerticalSeparators { - separatorNode.backgroundColor = theme.separatorColor - } - - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) - } - } - - func requestUpdate(transition: ContainedViewLayoutTransition) { - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: transition) - } - } - - override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - let containerSize = size - var size = size - size.width = min(size.width, 270.0) - - var origin = CGPoint(x: 0.0, y: 20.0) - if self.gift.resellForTonOnly { - let headerSize = self.header.update( - transition: .immediate, - component: AnyComponent( - MultilineTextComponent( - text: .plain(NSAttributedString(string: self.strings.Gift_Buy_AcceptsTonOnly, font: Font.regular(13.0), textColor: self.presentationTheme.actionSheet.secondaryTextColor)), - horizontalAlignment: .center, - maximumNumberOfLines: 2 - ) - ), - environment: {}, - containerSize: CGSize(width: size.width - 32.0, height: size.height) - ) - - let headerFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - headerSize.width) / 2.0), y: origin.y), size: headerSize) - if let view = self.header.view { - if view.superview == nil { - self.view.addSubview(view) - } - view.frame = headerFrame - } - origin.y += headerSize.height + 17.0 - } else { - origin.y -= 4.0 - - let headerSize = self.header.update( - transition: ComponentTransition(transition), - component: AnyComponent(TabSelectorComponent( - colors: TabSelectorComponent.Colors( - foreground: self.presentationTheme.list.itemSecondaryTextColor, - selection: self.presentationTheme.list.itemSecondaryTextColor.withMultipliedAlpha(0.15), - simple: true - ), - theme: self.presentationTheme, - customLayout: TabSelectorComponent.CustomLayout( - font: Font.medium(14.0), - spacing: 10.0 - ), - items: [ - TabSelectorComponent.Item( - id: AnyHashable(0), - content: .text(self.strings.Gift_Buy_PayInStars) - ), - TabSelectorComponent.Item( - id: AnyHashable(1), - content: .text(self.strings.Gift_Buy_PayInTon) - ) - ], - selectedId: self.currency == .ton ? AnyHashable(1) : AnyHashable(0), - setSelectedId: { [weak self] id in - guard let self else { - return - } - let currency: CurrencyAmount.Currency - if id == AnyHashable(0) { - currency = .stars - } else { - currency = .ton - } - if self.currency != currency { - self.currency = currency - self.updatedCurrency(currency) - self.requestUpdate(transition: .animated(duration: 0.4, curve: .spring)) - } - } - )), - environment: {}, - containerSize: CGSize(width: containerSize.width - 16.0 * 2.0, height: 100.0) - ) - - size.width = min(containerSize.width, max(270.0, headerSize.width + 32.0)) - - let headerFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - headerSize.width) / 2.0), y: origin.y), size: headerSize) - if let view = self.header.view { - if view.superview == nil { - self.view.addSubview(view) - } - view.frame = headerFrame - } - origin.y += headerSize.height + 17.0 - } - - self.validLayout = size - - let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - - var resellPrice: CurrencyAmount? - if let actionNode = self.actionNodes.first { - switch self.currency { - case .stars: - if let resellAmount = self.gift.resellAmounts?.first(where: { $0.currency == .stars }) { - resellPrice = resellAmount - actionNode.action = TextAlertAction(type: .defaultAction, title: self.strings.Gift_Buy_Confirm_BuyFor(Int32(resellAmount.amount.value)), action: actionNode.action.action) - } - case .ton: - if let resellAmount = self.gift.resellAmounts?.first(where: { $0.currency == .ton }) { - resellPrice = resellAmount - let valueString = formatTonAmountText(resellAmount.amount.value, dateTimeFormat: presentationData.dateTimeFormat) - actionNode.action = TextAlertAction(type: .defaultAction, title: self.strings.Gift_Buy_Confirm_BuyForTon(valueString).string, action: actionNode.action.action) - } - } - } - - let avatarSize = CGSize(width: 60.0, height: 60.0) - self.avatarNode.updateSize(size: avatarSize) - - let giftFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) - 44.0, y: origin.y), size: avatarSize) - - let _ = self.giftView.update( - transition: .immediate, - component: AnyComponent( - GiftItemComponent( - context: self.context, - theme: self.presentationTheme, - strings: self.strings, - peer: nil, - subject: .uniqueGift(gift: self.gift, price: nil), - mode: .thumbnail - ) - ), - environment: {}, - containerSize: avatarSize - ) - if let view = self.giftView.view { - if view.superview == nil { - self.view.addSubview(view) - } - view.frame = giftFrame - } - - let arrowSize = self.arrow.update( - transition: .immediate, - component: AnyComponent(BundleIconComponent(name: "Peer Info/AlertArrow", tintColor: self.presentationTheme.actionSheet.secondaryTextColor)), - environment: {}, - containerSize: size - ) - let arrowFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - arrowSize.width) / 2.0), y: origin.y + floorToScreenPixels((avatarSize.height - arrowSize.height) / 2.0)), size: arrowSize) - if let view = self.arrow.view { - if view.superview == nil { - self.view.addSubview(view) - } - view.frame = arrowFrame - } - - let avatarFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) + 44.0, y: origin.y), size: avatarSize) - transition.updateFrame(node: self.avatarNode, frame: avatarFrame) - origin.y += avatarSize.height + 17.0 - - let titleSize = self.title.update( - transition: .immediate, - component: AnyComponent( - MultilineTextComponent( - text: .plain(NSAttributedString(string: self.strings.Gift_Buy_Confirm_Title, font: Font.semibold(17.0), textColor: self.presentationTheme.actionSheet.primaryTextColor)), - horizontalAlignment: .center - ) - ), - environment: { - }, - containerSize: CGSize(width: size.width - 32.0, height: size.height) - ) - let titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize) - if let view = self.title.view { - if view.superview == nil { - self.view.addSubview(view) - } - view.frame = titleFrame - } - origin.y += titleSize.height + 5.0 - - let giftTitle = "\(self.gift.title) #\(presentationStringsFormattedNumber(self.gift.number, presentationData.dateTimeFormat.groupingSeparator))" - - let priceString: String - if let resellPrice { - switch resellPrice.currency { - case .stars: - priceString = self.strings.Gift_Buy_Confirm_Text_Stars(Int32(clamping: resellPrice.amount.value)) - case .ton: - priceString = "**\(formatTonAmountText(resellPrice.amount.value, dateTimeFormat: presentationData.dateTimeFormat)) TON**" - } - } else { - priceString = "" - } - - let text: String - if self.peer.id == self.context.account.peerId { - text = self.strings.Gift_Buy_Confirm_Text(giftTitle, priceString).string - } else { - text = self.strings.Gift_Buy_Confirm_GiftText(giftTitle, priceString, self.peer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder)).string - } - - let textSize = self.text.update( - transition: .immediate, - component: AnyComponent( - MultilineTextComponent( - text: .markdown(text: text, attributes: MarkdownAttributes( - body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: self.presentationTheme.actionSheet.primaryTextColor), - bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: self.presentationTheme.actionSheet.primaryTextColor), - link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: self.presentationTheme.actionSheet.primaryTextColor), - linkAttribute: { url in - return ("URL", url) - } - )), - horizontalAlignment: .center, - maximumNumberOfLines: 0 - ) - ), - environment: { - }, - containerSize: CGSize(width: size.width - 32.0, height: size.height) - ) - let textFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize) - if let view = self.text.view { - if view.superview == nil { - self.view.addSubview(view) - } - view.frame = textFrame - } - origin.y += textSize.height + 10.0 - - let actionButtonHeight: CGFloat = 44.0 - var minActionsWidth: CGFloat = 0.0 - let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) - let actionTitleInsets: CGFloat = 8.0 - - for actionNode in self.actionNodes { - let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) - minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) - } - - let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0) - - let contentWidth = max(size.width, minActionsWidth) - - let actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) - - let resultSize = CGSize(width: contentWidth, height: origin.y + actionsHeight - 26.0 + insets.top + insets.bottom) - transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - - var actionOffset: CGFloat = 0.0 - //let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) - var separatorIndex = -1 - var nodeIndex = 0 - for actionNode in self.actionNodes { - if separatorIndex >= 0 { - let separatorNode = self.actionVerticalSeparators[separatorIndex] - /*switch effectiveActionLayout { - case .horizontal: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) - case .vertical:*/ - do { - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - } - } - separatorIndex += 1 - - let currentActionWidth: CGFloat - /*switch effectiveActionLayout { - case .horizontal: - if nodeIndex == self.actionNodes.count - 1 { - currentActionWidth = resultSize.width - actionOffset - } else { - currentActionWidth = actionWidth - } - case .vertical:*/ - do { - currentActionWidth = resultSize.width - } - - let actionNodeFrame: CGRect - /*switch effectiveActionLayout { - case .horizontal: - actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += currentActionWidth - case .vertical:*/ - do { - actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += actionButtonHeight - } - - transition.updateFrame(node: actionNode, frame: actionNodeFrame) - - nodeIndex += 1 - } - - if self.inProgress { - let activityIndicator: ActivityIndicator - if let current = self.activityIndicator { - activityIndicator = current - } else { - activityIndicator = ActivityIndicator(type: .custom(self.presentationTheme.list.freeInputField.controlColor, 18.0, 1.5, false)) - self.addSubnode(activityIndicator) - } - - if let actionNode = self.actionNodes.first { - actionNode.isUserInteractionEnabled = false - actionNode.isHidden = false - - let indicatorSize = CGSize(width: 22.0, height: 22.0) - transition.updateFrame(node: activityIndicator, frame: CGRect(origin: CGPoint(x: actionNode.frame.minX + floor((actionNode.frame.width - indicatorSize.width) / 2.0), y: actionNode.frame.minY + floor((actionNode.frame.height - indicatorSize.height) / 2.0)), size: indicatorSize)) - } - } - - return resultSize - } -} +import AlertComponent +import AlertTransferHeaderComponent +import AvatarComponent public func giftPurchaseAlertController( context: AccountContext, @@ -454,68 +30,256 @@ public func giftPurchaseAlertController( navigationController: NavigationController?, commit: @escaping (CurrencyAmount.Currency) -> Void, dismissed: @escaping () -> Void -) -> AlertController { +) -> ViewController { let presentationData = context.sharedContext.currentPresentationData.with { $0 } let strings = presentationData.strings + + let currencyPromise = ValuePromise(.stars) + if gift.resellForTonOnly { + currencyPromise.set(.ton) + } + + let contentSignal = currencyPromise.get() + |> map { currency in + var content: [AnyComponentWithIdentity] = [] + if gift.resellForTonOnly { + content.append(AnyComponentWithIdentity( + id: "tonOnly", + component: AnyComponent( + AlertTextComponent( + content: .plain(strings.Gift_Buy_AcceptsTonOnly), + alignment: .center, + color: .secondary, + style: .plain(.small), + insets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 8.0, right: 0.0) + ) + ) + )) + } else { + content.append(AnyComponentWithIdentity( + id: "currency", + component: AnyComponent( + AlertCurrencyComponent( + currency: currency, + updatedCurrency: { currency in + currencyPromise.set(currency) + } + ) + ) + )) + } + + content.append(AnyComponentWithIdentity( + id: "header", + component: AnyComponent( + AlertTransferHeaderComponent( + fromComponent: AnyComponentWithIdentity(id: "gift", component: AnyComponent( + GiftItemComponent( + context: context, + theme: presentationData.theme, + strings: strings, + peer: nil, + subject: .uniqueGift(gift: gift, price: nil), + mode: .thumbnail + ) + )), + toComponent: AnyComponentWithIdentity(id: "avatar", component: AnyComponent( + AvatarComponent( + context: context, + theme: presentationData.theme, + peer: peer + ) + )), + type: .transfer + ) + ) + )) + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: strings.Gift_Buy_Confirm_Title) + ) + )) - var contentNode: GiftPurchaseAlertContentNode? - var dismissImpl: ((Bool) -> Void)? - var commitImpl: (() -> Void)? - let actions: [TextAlertAction] = [TextAlertAction(type: .defaultAction, title: "", action: { - commitImpl?() - dismissImpl?(true) - }), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - dismissImpl?(true) - })] + let giftTitle = "\(gift.title) #\(presentationStringsFormattedNumber(gift.number, presentationData.dateTimeFormat.groupingSeparator))" + var priceString = "" + switch currency { + case .stars: + if let resellAmount = gift.resellAmounts?.first(where: { $0.currency == .stars }) { + priceString = strings.Gift_Buy_Confirm_Text_Stars(Int32(clamping: resellAmount.amount.value)) + } + case .ton: + if let resellAmount = gift.resellAmounts?.first(where: { $0.currency == .ton }) { + priceString = "**\(formatTonAmountText(resellAmount.amount.value, dateTimeFormat: presentationData.dateTimeFormat)) TON**" + } + } - contentNode = GiftPurchaseAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), presentationTheme: presentationData.theme, strings: strings, gift: gift, peer: peer, actions: actions) + let text: String + if peer.id == context.account.peerId { + text = strings.Gift_Buy_Confirm_Text(giftTitle, priceString).string + } else { + text = strings.Gift_Buy_Confirm_GiftText(giftTitle, priceString, peer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder)).string + } + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(text)) + ) + )) + return content + } - let controller = ChatMessagePaymentAlertController( + let actionsSignal = currencyPromise.get() + |> map { currency in + var actions: [AlertScreen.Action] = [] + var buyString = "" + switch currency { + case .stars: + if let resellAmount = gift.resellAmounts?.first(where: { $0.currency == .stars }) { + buyString = strings.Gift_Buy_Confirm_BuyFor(Int32(resellAmount.amount.value)) + } + case .ton: + if let resellAmount = gift.resellAmounts?.first(where: { $0.currency == .ton }) { + buyString = strings.Gift_Buy_Confirm_BuyForTon(formatTonAmountText(resellAmount.amount.value, dateTimeFormat: presentationData.dateTimeFormat)).string + } + } + actions.append(.init(id: "buy", title: buyString, type: .default, action: { + commit(currency) + })) + actions.append(.init(title: strings.Common_Cancel)) + return actions + } + + let alertController = ChatMessagePaymentAlertController( context: context, presentationData: presentationData, - contentNode: contentNode!, + updatedPresentationData: (presentationData, context.sharedContext.presentationData), + configuration: AlertScreen.Configuration(actionAlignment: .vertical, dismissOnOutsideTap: true, allowInputInset: false), + contentSignal: contentSignal, + actionsSignal: actionsSignal, navigationController: navigationController, chatPeerId: context.account.peerId, showBalance: true, - currency: gift.resellForTonOnly ? .ton : .stars, + currencySignal: currencyPromise.get(), animateBalanceOverlay: animateBalanceOverlay ) - controller.dismissed = { _ in + alertController.dismissed = { _ in dismissed() } - - dismissImpl = { [weak controller] animated in - if animated { - controller?.dismissAnimated() - } else { - controller?.dismiss() - } - } - commitImpl = { [weak contentNode] in - contentNode?.inProgress = true - commit(contentNode?.currency ?? .stars) - } + +// if !gift.resellForTonOnly { +// Queue.mainQueue().after(0.3) { +// if let headerView = contentNode?.header.view as? TabSelectorComponent.View { +// let absoluteFrame = headerView.convert(headerView.bounds, to: nil) +// var originX = absoluteFrame.width * 0.75 +// if let itemFrame = headerView.frameForItem(AnyHashable(1)) { +// originX = itemFrame.midX +// } +// let location = CGRect(origin: CGPoint(x: absoluteFrame.minX + floor(originX), y: absoluteFrame.minY - 8.0), size: CGSize()) +// let tooltipController = TooltipScreen(account: context.account, sharedContext: context.sharedContext, text: .plain(text: presentationData.strings.Gift_Buy_PayInTon_Tooltip), style: .wide, location: .point(location, .bottom), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _, _ in +// return .dismiss(consume: false) +// }) +// controller.present(tooltipController, in: .window(.root)) +// } +// } +// } - contentNode?.updatedCurrency = { [weak controller] currency in - controller?.currency = currency - } - - if !gift.resellForTonOnly { - Queue.mainQueue().after(0.3) { - if let headerView = contentNode?.header.view as? TabSelectorComponent.View { - let absoluteFrame = headerView.convert(headerView.bounds, to: nil) - var originX = absoluteFrame.width * 0.75 - if let itemFrame = headerView.frameForItem(AnyHashable(1)) { - originX = itemFrame.midX - } - let location = CGRect(origin: CGPoint(x: absoluteFrame.minX + floor(originX), y: absoluteFrame.minY - 8.0), size: CGSize()) - let tooltipController = TooltipScreen(account: context.account, sharedContext: context.sharedContext, text: .plain(text: presentationData.strings.Gift_Buy_PayInTon_Tooltip), style: .wide, location: .point(location, .bottom), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _, _ in - return .dismiss(consume: false) - }) - controller.present(tooltipController, in: .window(.root)) - } - } - } - - return controller + return alertController +} + +private final class AlertCurrencyComponent: Component { + public typealias EnvironmentType = AlertComponentEnvironment + + let currency: CurrencyAmount.Currency + let updatedCurrency: (CurrencyAmount.Currency) -> Void + + public init( + currency: CurrencyAmount.Currency, + updatedCurrency: @escaping (CurrencyAmount.Currency) -> Void + ) { + self.currency = currency + self.updatedCurrency = updatedCurrency + } + + public static func ==(lhs: AlertCurrencyComponent, rhs: AlertCurrencyComponent) -> Bool { + if lhs.currency != rhs.currency { + return false + } + return true + } + + final class View: UIView { + private let header = ComponentView() + + private var component: AlertCurrencyComponent? + private weak var state: EmptyComponentState? + + func update(component: AlertCurrencyComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + self.state = state + + let environment = environment[AlertComponentEnvironment.self] + + let headerSize = self.header.update( + transition: transition, + component: AnyComponent(TabSelectorComponent( + colors: TabSelectorComponent.Colors( + foreground: environment.theme.actionSheet.primaryTextColor.withMultipliedAlpha(0.35), + selection: environment.theme.actionSheet.primaryTextColor.withMultipliedAlpha(0.1), + simple: true + ), + theme: environment.theme, + customLayout: TabSelectorComponent.CustomLayout( + font: Font.medium(14.0), + spacing: 10.0 + ), + items: [ + TabSelectorComponent.Item( + id: AnyHashable(0), + content: .text(environment.strings.Gift_Buy_PayInStars) + ), + TabSelectorComponent.Item( + id: AnyHashable(1), + content: .text(environment.strings.Gift_Buy_PayInTon) + ) + ], + selectedId: component.currency == .ton ? AnyHashable(1) : AnyHashable(0), + setSelectedId: { [weak self] id in + guard let self, let component = self.component else { + return + } + let currency: CurrencyAmount.Currency + if id == AnyHashable(0) { + currency = .stars + } else { + currency = .ton + } + if currency != component.currency { + component.updatedCurrency(currency) + } + } + )), + environment: {}, + containerSize: CGSize(width: availableSize.width + 54.0, height: 100.0) + ) + + let headerFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - headerSize.width) / 2.0), y: 0.0), size: headerSize) + if let view = self.header.view { + if view.superview == nil { + self.addSubview(view) + } + view.frame = headerFrame + } + + return CGSize(width: availableSize.width, height: headerSize.height + 12.0) + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } } diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftRemoveInfoAlertController.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftRemoveInfoAlertController.swift index a2e99d51..f461e20c 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftRemoveInfoAlertController.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftRemoveInfoAlertController.swift @@ -9,298 +9,10 @@ import TelegramPresentationData import TelegramUIPreferences import AccountContext import AppBundle -import Markdown import ChatMessagePaymentAlertController -import ActivityIndicator -import MultilineTextWithEntitiesComponent import TelegramStringFormatting import TextFormat - -private final class GiftRemoveInfoAlertContentNode: AlertContentNode { - private let context: AccountContext - private let strings: PresentationStrings - private var presentationTheme: PresentationTheme - private let title: String - private let text: String - private let gift: StarGift.UniqueGift - private let peers: [EnginePeer.Id: EnginePeer] - - private let titleNode: ASTextNode - private let textNode: ASTextNode - private let infoBackgroundNode: ASDisplayNode - private let infoView = ComponentView() - - private let actionNodesSeparator: ASDisplayNode - private let actionNodes: [TextAlertContentActionNode] - private let actionVerticalSeparators: [ASDisplayNode] - - private var activityIndicator: ActivityIndicator? - - private var validLayout: CGSize? - - var inProgress = false { - didSet { - if let size = self.validLayout { - let _ = self.updateLayout(size: size, transition: .immediate) - } - } - } - - override var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled - } - - init( - context: AccountContext, - theme: AlertControllerTheme, - ptheme: PresentationTheme, - strings: PresentationStrings, - gift: StarGift.UniqueGift, - peers: [EnginePeer.Id: EnginePeer], - title: String, - text: String, - actions: [TextAlertAction] - ) { - self.context = context - self.strings = strings - self.presentationTheme = ptheme - self.title = title - self.text = text - self.gift = gift - self.peers = peers - - self.titleNode = ASTextNode() - self.titleNode.maximumNumberOfLines = 0 - - self.textNode = ASTextNode() - self.textNode.maximumNumberOfLines = 0 - - self.infoBackgroundNode = ASDisplayNode() - self.infoBackgroundNode.backgroundColor = ptheme.overallDarkAppearance ? ptheme.list.itemModalBlocksBackgroundColor : ptheme.list.itemPrimaryTextColor.withAlphaComponent(0.04) - self.infoBackgroundNode.cornerRadius = 10.0 - self.infoBackgroundNode.displaysAsynchronously = false - - self.actionNodesSeparator = ASDisplayNode() - self.actionNodesSeparator.isLayerBacked = true - - self.actionNodes = actions.map { action -> TextAlertContentActionNode in - return TextAlertContentActionNode(theme: theme, action: action) - } - - var actionVerticalSeparators: [ASDisplayNode] = [] - if actions.count > 1 { - for _ in 0 ..< actions.count - 1 { - let separatorNode = ASDisplayNode() - separatorNode.isLayerBacked = true - actionVerticalSeparators.append(separatorNode) - } - } - self.actionVerticalSeparators = actionVerticalSeparators - - super.init() - - self.addSubnode(self.titleNode) - self.addSubnode(self.textNode) - - self.addSubnode(self.infoBackgroundNode) - - self.addSubnode(self.actionNodesSeparator) - - for actionNode in self.actionNodes { - self.addSubnode(actionNode) - } - - for separatorNode in self.actionVerticalSeparators { - self.addSubnode(separatorNode) - } - - self.updateTheme(theme) - } - - override func updateTheme(_ theme: AlertControllerTheme) { - self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.semibold(17.0), textColor: theme.primaryColor) - self.textNode.attributedText = parseMarkdownIntoAttributedString(self.text, attributes: MarkdownAttributes( - body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor), - bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: theme.primaryColor), - link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor), - linkAttribute: { url in - return ("URL", url) - } - ), textAlignment: .center) - - self.actionNodesSeparator.backgroundColor = theme.separatorColor - for actionNode in self.actionNodes { - actionNode.updateTheme(theme) - } - for separatorNode in self.actionVerticalSeparators { - separatorNode.backgroundColor = theme.separatorColor - } - - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) - } - } - - override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - var size = size - size.width = min(size.width, 310.0) - - let strings = self.strings - - self.validLayout = size - - var origin: CGPoint = CGPoint(x: 0.0, y: 20.0) - - let titleSize = self.titleNode.measure(CGSize(width: size.width - 32.0, height: size.height)) - transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize)) - origin.y += titleSize.height + 5.0 - - let textSize = self.textNode.measure(CGSize(width: size.width - 32.0, height: size.height)) - transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize)) - origin.y += textSize.height + 10.0 - - let actionButtonHeight: CGFloat = 44.0 - var minActionsWidth: CGFloat = 0.0 - let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) - let actionTitleInsets: CGFloat = 8.0 - - for actionNode in self.actionNodes { - let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) - minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) - } - - let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0) - - let contentWidth = max(size.width, minActionsWidth) - - let actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) - - var infoSize: CGSize = .zero - for attribute in self.gift.attributes { - if case let .originalInfo(senderPeerId, recipientPeerId, date, text, entities) = attribute { - let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - - let tableFont = Font.regular(13.0) - let tableBoldFont = Font.semibold(13.0) - let tableItalicFont = Font.italic(13.0) - let tableBoldItalicFont = Font.semiboldItalic(13.0) - let tableMonospaceFont = Font.monospace(13.0) - - let tableTextColor = self.presentationTheme.list.itemPrimaryTextColor - let tableLinkColor = self.presentationTheme.list.itemAccentColor - - let senderName = senderPeerId.flatMap { self.peers[$0]?.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder) } - let recipientName = self.peers[recipientPeerId]?.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder) ?? "" - - let dateString = stringForMediumDate(timestamp: date, strings: strings, dateTimeFormat: presentationData.dateTimeFormat, withTime: false) - let value: NSAttributedString - if let text { - let attributedText = stringWithAppliedEntities(text, entities: entities ?? [], baseColor: tableTextColor, linkColor: tableLinkColor, baseFont: tableFont, linkFont: tableFont, boldFont: tableBoldFont, italicFont: tableItalicFont, boldItalicFont: tableBoldItalicFont, fixedFont: tableMonospaceFont, blockQuoteFont: tableFont, message: nil) - - let format = senderName != nil ? presentationData.strings.Gift_Unique_OriginalInfoSenderWithText(senderName!, recipientName, dateString, "") : presentationData.strings.Gift_Unique_OriginalInfoWithText(recipientName, dateString, "") - let string = NSMutableAttributedString(string: format.string, font: tableFont, textColor: tableTextColor) - string.replaceCharacters(in: format.ranges[format.ranges.count - 1].range, with: attributedText) - if let _ = senderPeerId { - string.addAttribute(.foregroundColor, value: tableLinkColor, range: format.ranges[0].range) - string.addAttribute(.foregroundColor, value: tableLinkColor, range: format.ranges[1].range) - } else { - string.addAttribute(.foregroundColor, value: tableLinkColor, range: format.ranges[0].range) - } - value = string - } else { - let format = senderName != nil ? presentationData.strings.Gift_Unique_OriginalInfoSender(senderName!, recipientName, dateString) : presentationData.strings.Gift_Unique_OriginalInfo(recipientName, dateString) - let string = NSMutableAttributedString(string: format.string, font: tableFont, textColor: tableTextColor) - if let _ = senderPeerId { - string.addAttribute(.foregroundColor, value: tableLinkColor, range: format.ranges[0].range) - string.addAttribute(.foregroundColor, value: tableLinkColor, range: format.ranges[1].range) - } else { - string.addAttribute(.foregroundColor, value: tableLinkColor, range: format.ranges[0].range) - } - - value = string - } - - infoSize = self.infoView.update( - transition: .immediate, - component: AnyComponent( - MultilineTextWithEntitiesComponent( - context: self.context, - animationCache: self.context.animationCache, - animationRenderer: self.context.animationRenderer, - placeholderColor: self.presentationTheme.list.mediaPlaceholderColor, - text: .plain(value), - horizontalAlignment: .center, - maximumNumberOfLines: 0, - handleSpoilers: true - ) - ), - environment: {}, - containerSize: CGSize(width: contentWidth - 64.0, height: size.height) - ) - let infoFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - infoSize.width) / 2.0), y: titleSize.height + textSize.height + 54.0), size: infoSize) - if let view = self.infoView.view { - if view.superview == nil { - self.view.addSubview(view) - } - view.frame = infoFrame - } - self.infoBackgroundNode.frame = infoFrame.insetBy(dx: -12.0, dy: -12.0) - - break - } - } - - let resultSize = CGSize(width: contentWidth, height: titleSize.height + textSize.height + infoSize.height + actionsHeight + 46.0 + insets.top + insets.bottom) - transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - - var actionOffset: CGFloat = 0.0 - var separatorIndex = -1 - var nodeIndex = 0 - for actionNode in self.actionNodes { - if separatorIndex >= 0 { - let separatorNode = self.actionVerticalSeparators[separatorIndex] - do { - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - } - } - separatorIndex += 1 - - let currentActionWidth: CGFloat - do { - currentActionWidth = resultSize.width - } - - let actionNodeFrame: CGRect - do { - actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += actionButtonHeight - } - - transition.updateFrame(node: actionNode, frame: actionNodeFrame) - - nodeIndex += 1 - } - - if self.inProgress { - let activityIndicator: ActivityIndicator - if let current = self.activityIndicator { - activityIndicator = current - } else { - activityIndicator = ActivityIndicator(type: .custom(self.presentationTheme.list.freeInputField.controlColor, 18.0, 1.5, false)) - self.addSubnode(activityIndicator) - } - - if let actionNode = self.actionNodes.first { - actionNode.isHidden = true - - let indicatorSize = CGSize(width: 22.0, height: 22.0) - transition.updateFrame(node: activityIndicator, frame: CGRect(origin: CGPoint(x: actionNode.frame.minX + floor((actionNode.frame.width - indicatorSize.width) / 2.0), y: actionNode.frame.minY + floor((actionNode.frame.height - indicatorSize.height) / 2.0)), size: indicatorSize)) - } - } - - return resultSize - } -} +import AlertComponent public func giftRemoveInfoAlertController( context: AccountContext, @@ -309,32 +21,89 @@ public func giftRemoveInfoAlertController( removeInfoStars: Int64, navigationController: NavigationController?, commit: @escaping () -> Void -) -> AlertController { +) -> ViewController { let presentationData = context.sharedContext.currentPresentationData.with { $0 } let strings = presentationData.strings - let title = strings.Gift_RemoveDetails_Title - let text = strings.Gift_RemoveDetails_Text - let buttonText = strings.Gift_RemoveDetails_Action(" $ \(presentationStringsFormattedNumber(Int32(clamping: removeInfoStars), presentationData.dateTimeFormat.groupingSeparator))").string - - var contentNode: GiftRemoveInfoAlertContentNode? - var dismissImpl: ((Bool) -> Void)? - let actions: [TextAlertAction] = [TextAlertAction(type: .defaultAction, title: buttonText, action: { - dismissImpl?(true) - commit() - }), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - dismissImpl?(true) - })] + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: strings.Gift_RemoveDetails_Title) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(strings.Gift_RemoveDetails_Text)) + ) + )) - contentNode = GiftRemoveInfoAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: strings, gift: gift, peers: peers, title: title, text: text, actions: actions) - - let controller = ChatMessagePaymentAlertController(context: context, presentationData: presentationData, contentNode: contentNode!, navigationController: navigationController, chatPeerId: context.account.peerId, showBalance: removeInfoStars > 0) - dismissImpl = { [weak controller] animated in - if animated { - controller?.dismissAnimated() - } else { - controller?.dismiss() + for attribute in gift.attributes { + if case let .originalInfo(senderPeerId, recipientPeerId, date, text, entities) = attribute { + let textColor = presentationData.theme.actionSheet.primaryTextColor + let linkColor = presentationData.theme.actionSheet.controlAccentColor + + let textFont = Font.regular(15.0) + let boldTextFont = Font.semibold(15.0) + let italicTextFont = Font.italic(15.0) + let boldItalicTextFont = Font.with(size: 15.0, weight: .semibold, traits: .italic) + let fixedTextFont = Font.monospace(15.0) + + let senderName = senderPeerId.flatMap { peers[$0]?.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder) } + let recipientName = peers[recipientPeerId]?.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder) ?? "" + + let dateString = stringForMediumDate(timestamp: date, strings: strings, dateTimeFormat: presentationData.dateTimeFormat, withTime: false) + let value: NSAttributedString + if let text { + let attributedText = stringWithAppliedEntities(text, entities: entities ?? [], baseColor: textColor, linkColor: linkColor, baseFont: textFont, linkFont: textFont, boldFont: boldTextFont, italicFont: italicTextFont, boldItalicFont: boldItalicTextFont, fixedFont: fixedTextFont, blockQuoteFont: textFont, message: nil) + + let format = senderName != nil ? strings.Gift_Unique_OriginalInfoSenderWithText(senderName!, recipientName, dateString, "") : strings.Gift_Unique_OriginalInfoWithText(recipientName, dateString, "") + let string = NSMutableAttributedString(string: format.string, font: textFont, textColor: textColor) + string.replaceCharacters(in: format.ranges[format.ranges.count - 1].range, with: attributedText) + if let _ = senderPeerId { + string.addAttribute(.foregroundColor, value: linkColor, range: format.ranges[0].range) + string.addAttribute(.foregroundColor, value: linkColor, range: format.ranges[1].range) + } else { + string.addAttribute(.foregroundColor, value: linkColor, range: format.ranges[0].range) + } + value = string + } else { + let format = senderName != nil ? strings.Gift_Unique_OriginalInfoSender(senderName!, recipientName, dateString) : strings.Gift_Unique_OriginalInfo(recipientName, dateString) + let string = NSMutableAttributedString(string: format.string, font: textFont, textColor: textColor) + if let _ = senderPeerId { + string.addAttribute(.foregroundColor, value: linkColor, range: format.ranges[0].range) + string.addAttribute(.foregroundColor, value: linkColor, range: format.ranges[1].range) + } else { + string.addAttribute(.foregroundColor, value: linkColor, range: format.ranges[0].range) + } + + value = string + } + + content.append(AnyComponentWithIdentity( + id: "info", + component: AnyComponent( + AlertTextComponent(content: .attributed(value), style: .background(.small)) + ) + )) } } - return controller + + let alertController = ChatMessagePaymentAlertController( + context: context, + presentationData: presentationData, + configuration: AlertScreen.Configuration(actionAlignment: .vertical), + content: content, + actions: [ + .init(title: strings.Gift_RemoveDetails_Action(" $ \(presentationStringsFormattedNumber(Int32(clamping: removeInfoStars), presentationData.dateTimeFormat.groupingSeparator))").string, type: .default, action: { + commit() + }), + .init(title: strings.Common_Cancel) + ], + navigationController: navigationController, + chatPeerId: context.account.peerId, + showBalance: removeInfoStars > 0 + ) + return alertController } diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftTransferAlertController.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftTransferAlertController.swift index a58d8bf5..f625cc31 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftTransferAlertController.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftTransferAlertController.swift @@ -6,421 +6,18 @@ import ComponentFlow import Postbox import TelegramCore import TelegramPresentationData -import TelegramUIPreferences import AccountContext import AppBundle -import AvatarNode -import Markdown import GiftItemComponent import ChatMessagePaymentAlertController -import ActivityIndicator import TooltipUI import MultilineTextComponent import TelegramStringFormatting - -private final class GiftTransferAlertContentNode: AlertContentNode { - private let context: AccountContext - private let strings: PresentationStrings - private var presentationTheme: PresentationTheme - private let title: String - private let text: String - private let gift: StarGift.UniqueGift - - private let titleNode: ASTextNode - private let giftView = ComponentView() - private let textNode: ASTextNode - private let arrowNode: ASImageNode - private let avatarNode: AvatarNode - private let tableView = ComponentView() - - private let modelButtonTag = GenericComponentViewTag() - private let backdropButtonTag = GenericComponentViewTag() - private let symbolButtonTag = GenericComponentViewTag() - - fileprivate var getController: () -> ViewController? = { return nil} - - private let actionNodesSeparator: ASDisplayNode - private let actionNodes: [TextAlertContentActionNode] - private let actionVerticalSeparators: [ASDisplayNode] - - private var activityIndicator: ActivityIndicator? - - private var validLayout: CGSize? - - var inProgress = false { - didSet { - if let size = self.validLayout { - let _ = self.updateLayout(size: size, transition: .immediate) - } - } - } - - override var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled - } - - init( - context: AccountContext, - theme: AlertControllerTheme, - ptheme: PresentationTheme, - strings: PresentationStrings, - gift: StarGift.UniqueGift, - peer: EnginePeer, - title: String, - text: String, - actions: [TextAlertAction] - ) { - self.context = context - self.strings = strings - self.presentationTheme = ptheme - self.title = title - self.text = text - self.gift = gift - - self.titleNode = ASTextNode() - self.titleNode.maximumNumberOfLines = 0 - - self.textNode = ASTextNode() - self.textNode.maximumNumberOfLines = 0 - - self.arrowNode = ASImageNode() - self.arrowNode.displaysAsynchronously = false - self.arrowNode.displayWithoutProcessing = true - - self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0)) - - self.actionNodesSeparator = ASDisplayNode() - self.actionNodesSeparator.isLayerBacked = true - - self.actionNodes = actions.map { action -> TextAlertContentActionNode in - return TextAlertContentActionNode(theme: theme, action: action) - } - - var actionVerticalSeparators: [ASDisplayNode] = [] - if actions.count > 1 { - for _ in 0 ..< actions.count - 1 { - let separatorNode = ASDisplayNode() - separatorNode.isLayerBacked = true - actionVerticalSeparators.append(separatorNode) - } - } - self.actionVerticalSeparators = actionVerticalSeparators - - super.init() - - self.addSubnode(self.titleNode) - self.addSubnode(self.textNode) - self.addSubnode(self.arrowNode) - self.addSubnode(self.avatarNode) - - self.addSubnode(self.actionNodesSeparator) - - for actionNode in self.actionNodes { - self.addSubnode(actionNode) - } - - for separatorNode in self.actionVerticalSeparators { - self.addSubnode(separatorNode) - } - - self.updateTheme(theme) - - self.avatarNode.setPeer(context: context, theme: ptheme, peer: peer) - } - - override func updateTheme(_ theme: AlertControllerTheme) { - self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.semibold(17.0), textColor: theme.primaryColor) - self.textNode.attributedText = parseMarkdownIntoAttributedString(self.text, attributes: MarkdownAttributes( - body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor), - bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: theme.primaryColor), - link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor), - linkAttribute: { url in - return ("URL", url) - } - ), textAlignment: .center) - self.arrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Peer Info/AlertArrow"), color: theme.secondaryColor.withAlphaComponent(0.9)) - - self.actionNodesSeparator.backgroundColor = theme.separatorColor - for actionNode in self.actionNodes { - actionNode.updateTheme(theme) - } - for separatorNode in self.actionVerticalSeparators { - separatorNode.backgroundColor = theme.separatorColor - } - - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) - } - } - - fileprivate func dismissAllTooltips() { - guard let controller = self.getController() else { - return - } - controller.window?.forEachController({ controller in - if let controller = controller as? TooltipScreen { - controller.dismiss(inPlace: false) - } - }) - controller.forEachController({ controller in - if let controller = controller as? TooltipScreen { - controller.dismiss(inPlace: false) - } - return true - }) - } - - func showAttributeInfo(tag: Any, text: String) { - guard let controller = self.getController() else { - return - } - self.dismissAllTooltips() - - guard let sourceView = self.tableView.findTaggedView(tag: tag), let absoluteLocation = sourceView.superview?.convert(sourceView.center, to: controller.view) else { - return - } - - let location = CGRect(origin: CGPoint(x: absoluteLocation.x, y: absoluteLocation.y - 12.0), size: CGSize()) - let tooltipController = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: text), style: .wide, location: .point(location, .bottom), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _, _ in - return .dismiss(consume: false) - }) - controller.present(tooltipController, in: .current) - } - - override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - var size = size - size.width = min(size.width, 310.0) - - let strings = self.strings - - self.validLayout = size - - var origin: CGPoint = CGPoint(x: 0.0, y: 20.0) - - let avatarSize = CGSize(width: 60.0, height: 60.0) - self.avatarNode.updateSize(size: avatarSize) - - let giftFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) - 44.0, y: origin.y), size: avatarSize) - - let _ = self.giftView.update( - transition: .immediate, - component: AnyComponent( - GiftItemComponent( - context: self.context, - theme: self.presentationTheme, - strings: strings, - peer: nil, - subject: .uniqueGift(gift: self.gift, price: nil), - mode: .thumbnail - ) - ), - environment: {}, - containerSize: avatarSize - ) - if let view = self.giftView.view { - if view.superview == nil { - self.view.addSubview(view) - } - view.frame = giftFrame - } - - if let arrowImage = self.arrowNode.image { - let arrowFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - arrowImage.size.width) / 2.0), y: origin.y + floorToScreenPixels((avatarSize.height - arrowImage.size.height) / 2.0)), size: arrowImage.size) - transition.updateFrame(node: self.arrowNode, frame: arrowFrame) - } - - let avatarFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) + 44.0, y: origin.y), size: avatarSize) - transition.updateFrame(node: self.avatarNode, frame: avatarFrame) - - origin.y += avatarSize.height + 17.0 - - let titleSize = self.titleNode.measure(CGSize(width: size.width - 32.0, height: size.height)) - transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize)) - origin.y += titleSize.height + 5.0 - - let textSize = self.textNode.measure(CGSize(width: size.width - 32.0, height: size.height)) - transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize)) - origin.y += textSize.height + 10.0 - - let actionButtonHeight: CGFloat = 44.0 - var minActionsWidth: CGFloat = 0.0 - let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) - let actionTitleInsets: CGFloat = 8.0 - - for actionNode in self.actionNodes { - let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) - minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) - } - - let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0) - - let contentWidth = max(size.width, minActionsWidth) - - let actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) - - let tableFont = Font.regular(15.0) - let tableTextColor = self.presentationTheme.list.itemPrimaryTextColor - - var tableItems: [TableComponent.Item] = [] - let order: [StarGift.UniqueGift.Attribute.AttributeType] = [ - .model, .pattern, .backdrop, .originalInfo - ] - - var attributeMap: [StarGift.UniqueGift.Attribute.AttributeType: StarGift.UniqueGift.Attribute] = [:] - for attribute in self.gift.attributes { - attributeMap[attribute.attributeType] = attribute - } - - for type in order { - if let attribute = attributeMap[type] { - let id: String? - let title: String? - let value: NSAttributedString - let percentage: Float? - let tag: AnyObject? - - switch attribute { - case let .model(name, _, rarity): - id = "model" - title = strings.Gift_Unique_Model - value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor) - percentage = Float(rarity) * 0.1 - tag = self.modelButtonTag - case let .backdrop(name, _, _, _, _, _, rarity): - id = "backdrop" - title = strings.Gift_Unique_Backdrop - value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor) - percentage = Float(rarity) * 0.1 - tag = self.backdropButtonTag - case let .pattern(name, _, rarity): - id = "pattern" - title = strings.Gift_Unique_Symbol - value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor) - percentage = Float(rarity) * 0.1 - tag = self.symbolButtonTag - case .originalInfo: - continue - } - - var items: [AnyComponentWithIdentity] = [] - items.append( - AnyComponentWithIdentity( - id: AnyHashable(0), - component: AnyComponent( - MultilineTextComponent(text: .plain(value)) - ) - ) - ) - if let percentage, let tag { - items.append(AnyComponentWithIdentity( - id: AnyHashable(1), - component: AnyComponent(Button( - content: AnyComponent(ButtonContentComponent( - context: self.context, - text: formatPercentage(percentage), - color: self.presentationTheme.list.itemAccentColor - )), - action: { [weak self] in - self?.showAttributeInfo(tag: tag, text: strings.Gift_Unique_AttributeDescription(formatPercentage(percentage)).string) - } - ).tagged(tag)) - )) - } - let itemComponent = AnyComponent( - HStack(items, spacing: 4.0) - ) - - tableItems.append(.init( - id: id, - title: title, - hasBackground: false, - component: itemComponent - )) - } - } - - if let valueAmount = self.gift.valueAmount, let valueCurrency = self.gift.valueCurrency { - tableItems.append(.init( - id: "fiatValue", - title: strings.Gift_Unique_Value, - component: AnyComponent( - MultilineTextComponent(text: .plain(NSAttributedString(string: "~\(formatCurrencyAmount(valueAmount, currency: valueCurrency))", font: tableFont, textColor: tableTextColor))) - ), - insets: UIEdgeInsets(top: 0.0, left: 10.0, bottom: 0.0, right: 12.0) - )) - } - - let tableSize = self.tableView.update( - transition: .immediate, - component: AnyComponent( - TableComponent( - theme: self.presentationTheme, - items: tableItems - ) - ), - environment: {}, - containerSize: CGSize(width: contentWidth - 32.0, height: size.height) - ) - let tableFrame = CGRect(origin: CGPoint(x: 16.0, y: avatarSize.height + titleSize.height + textSize.height + 60.0), size: tableSize) - if let view = self.tableView.view { - if view.superview == nil { - self.view.addSubview(view) - } - view.frame = tableFrame - } - - let resultSize = CGSize(width: contentWidth, height: avatarSize.height + titleSize.height + textSize.height + tableSize.height + actionsHeight + 40.0 + insets.top + insets.bottom) - transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - - var actionOffset: CGFloat = 0.0 - var separatorIndex = -1 - var nodeIndex = 0 - for actionNode in self.actionNodes { - if separatorIndex >= 0 { - let separatorNode = self.actionVerticalSeparators[separatorIndex] - do { - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - } - } - separatorIndex += 1 - - let currentActionWidth: CGFloat - do { - currentActionWidth = resultSize.width - } - - let actionNodeFrame: CGRect - do { - actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += actionButtonHeight - } - - transition.updateFrame(node: actionNode, frame: actionNodeFrame) - - nodeIndex += 1 - } - - if self.inProgress { - let activityIndicator: ActivityIndicator - if let current = self.activityIndicator { - activityIndicator = current - } else { - activityIndicator = ActivityIndicator(type: .custom(self.presentationTheme.list.freeInputField.controlColor, 18.0, 1.5, false)) - self.addSubnode(activityIndicator) - } - - if let actionNode = self.actionNodes.first { - actionNode.isUserInteractionEnabled = false - actionNode.isHidden = false - - let indicatorSize = CGSize(width: 22.0, height: 22.0) - transition.updateFrame(node: activityIndicator, frame: CGRect(origin: CGPoint(x: actionNode.frame.minX + floor((actionNode.frame.width - indicatorSize.width) / 2.0), y: actionNode.frame.minY + floor((actionNode.frame.height - indicatorSize.height) / 2.0)), size: indicatorSize)) - } - } - - return resultSize - } -} +import AlertComponent +import TableComponent +import AvatarComponent +import AlertTransferHeaderComponent +import AlertTableComponent public func giftTransferAlertController( context: AccountContext, @@ -429,7 +26,7 @@ public func giftTransferAlertController( transferStars: Int64, navigationController: NavigationController?, commit: @escaping () -> Void -) -> AlertController { +) -> AlertScreen { let presentationData = context.sharedContext.currentPresentationData.with { $0 } let strings = presentationData.strings @@ -444,27 +41,181 @@ public func giftTransferAlertController( buttonText = strings.Gift_Transfer_Confirmation_TransferFree } - var contentNode: GiftTransferAlertContentNode? - var dismissImpl: ((Bool) -> Void)? - let actions: [TextAlertAction] = [TextAlertAction(type: .defaultAction, title: buttonText, action: { [weak contentNode] in - contentNode?.inProgress = true - commit() - }), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - dismissImpl?(true) - })] + let tableFont = Font.regular(15.0) + let tableTextColor = presentationData.theme.list.itemPrimaryTextColor - contentNode = GiftTransferAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: strings, gift: gift, peer: peer, title: title, text: text, actions: actions) + let modelButtonTag = GenericComponentViewTag() + let backdropButtonTag = GenericComponentViewTag() + let symbolButtonTag = GenericComponentViewTag() + var showAttributeInfoImpl: ((Any, String) -> Void)? - let controller = ChatMessagePaymentAlertController(context: context, presentationData: presentationData, contentNode: contentNode!, navigationController: navigationController, chatPeerId: context.account.peerId, showBalance: transferStars > 0) - contentNode?.getController = { [weak controller] in - return controller + var tableItems: [TableComponent.Item] = [] + let order: [StarGift.UniqueGift.Attribute.AttributeType] = [ + .model, .pattern, .backdrop, .originalInfo + ] + + var attributeMap: [StarGift.UniqueGift.Attribute.AttributeType: StarGift.UniqueGift.Attribute] = [:] + for attribute in gift.attributes { + attributeMap[attribute.attributeType] = attribute } - dismissImpl = { [weak controller] animated in - if animated { - controller?.dismissAnimated() - } else { - controller?.dismiss() + + for type in order { + if let attribute = attributeMap[type] { + let id: String? + let title: String? + let value: NSAttributedString + let percentage: Float? + let tag: AnyObject? + + switch attribute { + case let .model(name, _, rarity): + id = "model" + title = strings.Gift_Unique_Model + value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor) + percentage = Float(rarity) * 0.1 + tag = modelButtonTag + case let .backdrop(name, _, _, _, _, _, rarity): + id = "backdrop" + title = strings.Gift_Unique_Backdrop + value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor) + percentage = Float(rarity) * 0.1 + tag = backdropButtonTag + case let .pattern(name, _, rarity): + id = "pattern" + title = strings.Gift_Unique_Symbol + value = NSAttributedString(string: name, font: tableFont, textColor: tableTextColor) + percentage = Float(rarity) * 0.1 + tag = symbolButtonTag + case .originalInfo: + continue + } + + var items: [AnyComponentWithIdentity] = [] + items.append( + AnyComponentWithIdentity( + id: AnyHashable(0), + component: AnyComponent( + MultilineTextComponent(text: .plain(value)) + ) + ) + ) + if let percentage, let tag { + items.append(AnyComponentWithIdentity( + id: AnyHashable(1), + component: AnyComponent(Button( + content: AnyComponent(ButtonContentComponent( + context: context, + text: formatPercentage(percentage), + color: presentationData.theme.list.itemAccentColor + )), + action: { + showAttributeInfoImpl?(tag, strings.Gift_Unique_AttributeDescription(formatPercentage(percentage)).string) + } + ).tagged(tag)) + )) + } + let itemComponent = AnyComponent( + HStack(items, spacing: 4.0) + ) + + tableItems.append(.init( + id: id, + title: title, + hasBackground: false, + component: itemComponent + )) } } - return controller + + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "header", + component: AnyComponent( + AlertTransferHeaderComponent( + fromComponent: AnyComponentWithIdentity(id: "gift", component: AnyComponent( + GiftItemComponent( + context: context, + theme: presentationData.theme, + strings: strings, + peer: nil, + subject: .uniqueGift(gift: gift, price: nil), + mode: .thumbnail + ) + )), + toComponent: AnyComponentWithIdentity(id: "avatar", component: AnyComponent( + AvatarComponent( + context: context, + theme: presentationData.theme, + peer: peer + ) + )), + type: .transfer + ) + ) + )) + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: title) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(text)) + ) + )) + content.append(AnyComponentWithIdentity( + id: "table", + component: AnyComponent( + AlertTableComponent(items: tableItems) + ) + )) + + let alertController = ChatMessagePaymentAlertController( + context: context, + presentationData: presentationData, + configuration: AlertScreen.Configuration(actionAlignment: .vertical, dismissOnOutsideTap: true, allowInputInset: false), + content: content, + actions: [ + .init(title: buttonText, type: .default, action: { + commit() + }), + .init(title: strings.Common_Cancel) + ], + navigationController: navigationController, + chatPeerId: context.account.peerId, + showBalance: transferStars > 0 + ) + + var dismissAllTooltipsImpl: (() -> Void)? + showAttributeInfoImpl = { [weak alertController] tag, text in + dismissAllTooltipsImpl?() + guard let alertController, let sourceView = alertController.node.hostView.findTaggedView(tag: tag), let absoluteLocation = sourceView.superview?.convert(sourceView.center, to: alertController.view) else { + return + } + + let location = CGRect(origin: CGPoint(x: absoluteLocation.x, y: absoluteLocation.y - 12.0), size: CGSize()) + let tooltipController = TooltipScreen(account: context.account, sharedContext: context.sharedContext, text: .plain(text: text), style: .wide, location: .point(location, .bottom), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _, _ in + return .dismiss(consume: false) + }) + alertController.present(tooltipController, in: .current) + } + dismissAllTooltipsImpl = { [weak alertController] in + guard let alertController else { + return + } + alertController.window?.forEachController({ controller in + if let controller = controller as? TooltipScreen { + controller.dismiss(inPlace: false) + } + }) + alertController.forEachController({ controller in + if let controller = controller as? TooltipScreen { + controller.dismiss(inPlace: false) + } + return true + }) + } + return alertController } diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftUnpinScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftUnpinScreen.swift index 43a1b832..761d7f8d 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftUnpinScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftUnpinScreen.swift @@ -97,7 +97,7 @@ private final class SheetContent: CombinedComponent { component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: theme.chat.inputPanel.panelControlColor ) )), action: { _ in diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftUpgradeCostScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftUpgradeCostScreen.swift index d9218330..e3c7712f 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftUpgradeCostScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftUpgradeCostScreen.swift @@ -17,6 +17,7 @@ import LottieComponent import ProfileLevelRatingBarComponent import TextFormat import TelegramStringFormatting +import TableComponent private final class GiftUpgradeCostScreenComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftUpgradeVariantsScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftUpgradeVariantsScreen.swift new file mode 100644 index 00000000..b0d45858 --- /dev/null +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftUpgradeVariantsScreen.swift @@ -0,0 +1,1526 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import TelegramPresentationData +import ComponentFlow +import AccountContext +import ViewControllerComponent +import TelegramCore +import SwiftSignalKit +import Display +import MultilineTextComponent +import MultilineTextWithEntitiesComponent +import ButtonComponent +import PlainButtonComponent +import Markdown +import BundleIconComponent +import TextFormat +import TelegramStringFormatting +import GlassBarButtonComponent +import GiftItemComponent +import EdgeEffect +import AnimatedTextComponent +import SegmentControlComponent +import GiftAnimationComponent +import GlassBackgroundComponent + +private final class GiftUpgradeVariantsScreenComponent: Component { + typealias EnvironmentType = ViewControllerComponentContainer.Environment + + let context: AccountContext + let gift: StarGift + let attributes: [StarGift.UniqueGift.Attribute] + let selectedAttributes: [StarGift.UniqueGift.Attribute]? + let focusedAttribute: StarGift.UniqueGift.Attribute? + + init( + context: AccountContext, + gift: StarGift, + attributes: [StarGift.UniqueGift.Attribute], + selectedAttributes: [StarGift.UniqueGift.Attribute]?, + focusedAttribute: StarGift.UniqueGift.Attribute? + ) { + self.context = context + self.gift = gift + self.attributes = attributes + self.selectedAttributes = selectedAttributes + self.focusedAttribute = focusedAttribute + } + + static func ==(lhs: GiftUpgradeVariantsScreenComponent, rhs: GiftUpgradeVariantsScreenComponent) -> Bool { + return true + } + + private struct ItemLayout: Equatable { + var containerSize: CGSize + var containerInset: CGFloat + var containerCornerRadius: CGFloat + var bottomInset: CGFloat + var topInset: CGFloat + + init(containerSize: CGSize, containerInset: CGFloat, containerCornerRadius: CGFloat, bottomInset: CGFloat, topInset: CGFloat) { + self.containerSize = containerSize + self.containerInset = containerInset + self.containerCornerRadius = containerCornerRadius + self.bottomInset = bottomInset + self.topInset = topInset + } + } + + enum SelectedSection { + case models + case backdrops + case symbols + } + + private final class ScrollView: UIScrollView { + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + return super.hitTest(point, with: event) + } + } + + final class View: UIView, UIScrollViewDelegate { + private let dimView: UIView + private let containerView: UIView + private let backgroundLayer: SimpleLayer + private let navigationBarContainer: SparseContainerView + private let closeGlassContainerView: GlassBackgroundContainerView + private let playbackGlassContainerView: GlassBackgroundContainerView + private let scrollView: ScrollView + private let scrollContentClippingView: SparseContainerView + private let scrollContentView: UIView + + private let backgroundHandleView: UIImageView + + private let header = ComponentView() + private let closeButton = ComponentView() + private let playbackButton = ComponentView() + + private let title = ComponentView() + private let subtitle = ComponentView() + + private var attributeInfos: [ComponentView] = [] + + private let topEdgeSolidView = UIView() + private let topEdgeEffectView: EdgeEffectView + private let segmentControl = ComponentView() + private let descriptionText = ComponentView() + + private var giftItems: [AnyHashable: ComponentView] = [:] + + private var selectedSection: SelectedSection = .models + + private let giftCompositionExternalState = GiftCompositionComponent.ExternalState() + + private var isPlaying = true + private var showRandomizeTip = false + private var previewTimer: SwiftSignalKit.Timer? + private var previewModelIndex: Int = 0 + private var previewBackdropIndex: Int = 0 + private var previewSymbolIndex: Int = 0 + + private var previewModels: [StarGift.UniqueGift.Attribute] = [] + private var previewBackdrops: [StarGift.UniqueGift.Attribute] = [] + private var previewSymbols: [StarGift.UniqueGift.Attribute] = [] + + private var selectedModel: StarGift.UniqueGift.Attribute? + private var selectedBackdrop: StarGift.UniqueGift.Attribute? + private var selectedSymbol: StarGift.UniqueGift.Attribute? + + private var modelCount: Int32 = 0 + private var backdropCount: Int32 = 0 + private var symbolCount: Int32 = 0 + + private var ignoreScrolling: Bool = false + + private var component: GiftUpgradeVariantsScreenComponent? + private weak var state: EmptyComponentState? + private var isUpdating: Bool = false + private var environment: ViewControllerComponentContainer.Environment? + private var itemLayout: ItemLayout? + + override init(frame: CGRect) { + self.dimView = UIView() + self.containerView = UIView() + + self.containerView.clipsToBounds = true + self.containerView.layer.cornerRadius = 40.0 + self.containerView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner] + + self.backgroundLayer = SimpleLayer() + self.backgroundLayer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] + self.backgroundLayer.cornerRadius = 40.0 + + self.backgroundHandleView = UIImageView() + + self.navigationBarContainer = SparseContainerView() + + self.topEdgeEffectView = EdgeEffectView() + self.topEdgeEffectView.alpha = 0.0 + + self.closeGlassContainerView = GlassBackgroundContainerView() + self.playbackGlassContainerView = GlassBackgroundContainerView() + + self.scrollView = ScrollView() + + self.scrollContentClippingView = SparseContainerView() + self.scrollContentClippingView.clipsToBounds = true + + self.scrollContentView = UIView() + + super.init(frame: frame) + + self.addSubview(self.dimView) + self.addSubview(self.containerView) + self.containerView.layer.addSublayer(self.backgroundLayer) + + self.scrollView.delaysContentTouches = true + self.scrollView.canCancelContentTouches = true + self.scrollView.clipsToBounds = false + if #available(iOSApplicationExtension 11.0, iOS 11.0, *) { + self.scrollView.contentInsetAdjustmentBehavior = .never + } + if #available(iOS 13.0, *) { + self.scrollView.automaticallyAdjustsScrollIndicatorInsets = false + } + self.scrollView.showsVerticalScrollIndicator = false + self.scrollView.showsHorizontalScrollIndicator = false + self.scrollView.alwaysBounceHorizontal = false + self.scrollView.alwaysBounceVertical = true + self.scrollView.scrollsToTop = false + self.scrollView.delegate = self + self.scrollView.clipsToBounds = true + + self.containerView.addSubview(self.scrollContentClippingView) + self.scrollContentClippingView.addSubview(self.scrollView) + + self.scrollView.addSubview(self.scrollContentView) + + self.containerView.addSubview(self.navigationBarContainer) + + self.dimView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:)))) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + if !self.ignoreScrolling { + self.updateScrolling(transition: .immediate) + } + } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if !self.bounds.contains(point) { + return nil + } + if !self.backgroundLayer.frame.contains(point) { + return self.dimView + } + + if let result = self.navigationBarContainer.hitTest(self.convert(point, to: self.navigationBarContainer), with: event) { + return result + } + let result = super.hitTest(point, with: event) + return result + } + + @objc private func dimTapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + guard let environment = self.environment, let controller = environment.controller() else { + return + } + controller.dismiss() + } + } + + private func updateScrolling(transition: ComponentTransition) { + guard let itemLayout = self.itemLayout else { + return + } + var topOffset = -self.scrollView.bounds.minY + itemLayout.topInset + topOffset = max(0.0, topOffset) + transition.setTransform(layer: self.backgroundLayer, transform: CATransform3DMakeTranslation(0.0, topOffset + itemLayout.containerInset, 0.0)) + + transition.setPosition(view: self.navigationBarContainer, position: CGPoint(x: 0.0, y: topOffset + itemLayout.containerInset)) + + var topOffsetFraction = self.scrollView.bounds.minY / 100.0 + topOffsetFraction = max(0.0, min(1.0, topOffsetFraction)) + + self.topEdgeEffectView.alpha = max(0.0, min(1.0, self.scrollView.bounds.minY / 8.0)) + + let minScale: CGFloat = (itemLayout.containerSize.width - 6.0 * 2.0) / itemLayout.containerSize.width + let minScaledTranslation: CGFloat = (itemLayout.containerSize.height - itemLayout.containerSize.height * minScale) * 0.5 - 6.0 + let minScaledCornerRadius: CGFloat = itemLayout.containerCornerRadius + + let scale = minScale * (1.0 - topOffsetFraction) + 1.0 * topOffsetFraction + let scaledTranslation = minScaledTranslation * (1.0 - topOffsetFraction) + let scaledCornerRadius = minScaledCornerRadius * (1.0 - topOffsetFraction) + itemLayout.containerCornerRadius * topOffsetFraction + + var containerTransform = CATransform3DIdentity + containerTransform = CATransform3DTranslate(containerTransform, 0.0, scaledTranslation, 0.0) + containerTransform = CATransform3DScale(containerTransform, scale, scale, scale) + transition.setTransform(view: self.containerView, transform: containerTransform) + transition.setCornerRadius(layer: self.containerView.layer, cornerRadius: scaledCornerRadius) + + self.updateItems(transition: transition) + } + + func animateIn() { + self.dimView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + let animateOffset: CGFloat = self.bounds.height - self.backgroundLayer.frame.minY + self.scrollContentClippingView.layer.animatePosition(from: CGPoint(x: 0.0, y: animateOffset), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true) + self.backgroundLayer.animatePosition(from: CGPoint(x: 0.0, y: animateOffset), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true) + self.navigationBarContainer.layer.animatePosition(from: CGPoint(x: 0.0, y: animateOffset), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true) + } + + func animateOut(completion: @escaping () -> Void) { + let animateOffset: CGFloat = self.bounds.height - self.backgroundLayer.frame.minY + + self.dimView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false) + self.scrollContentClippingView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: animateOffset), duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true, completion: { _ in + completion() + }) + self.backgroundLayer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: animateOffset), duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true) + self.navigationBarContainer.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: animateOffset), duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true) + } + + private func previewTimerTick() { + guard !self.previewModels.isEmpty else { return } + self.previewModelIndex = (self.previewModelIndex + 1) % self.previewModels.count + + let previousSymbolIndex = self.previewSymbolIndex + var randomSymbolIndex = previousSymbolIndex + while randomSymbolIndex == previousSymbolIndex && !self.previewSymbols.isEmpty { + randomSymbolIndex = Int.random(in: 0 ..< self.previewSymbols.count) + } + if !self.previewSymbols.isEmpty { self.previewSymbolIndex = randomSymbolIndex } + + let previousBackdropIndex = self.previewBackdropIndex + var randomBackdropIndex = previousBackdropIndex + while randomBackdropIndex == previousBackdropIndex && !self.previewBackdrops.isEmpty { + randomBackdropIndex = Int.random(in: 0 ..< self.previewBackdrops.count) + } + if !self.previewBackdrops.isEmpty { self.previewBackdropIndex = randomBackdropIndex } + + self.state?.updated(transition: .easeInOut(duration: 0.25)) + } + + private func updateTimer() { + if self.isPlaying { + self.previewTimer = SwiftSignalKit.Timer(timeout: 3.0, repeat: true, completion: { [weak self] in + guard let self else { + return + } + self.previewTimerTick() + }, queue: Queue.mainQueue()) + self.previewTimer?.start() + } else { + self.previewTimer?.invalidate() + self.previewTimer = nil + } + } + + private var effectiveGifts: [[StarGift.UniqueGift.Attribute]] = [] + private func updateEffectiveGifts(attributes: [StarGift.UniqueGift.Attribute]) { + var effectiveGifts: [[StarGift.UniqueGift.Attribute]] = [] + switch self.selectedSection { + case .models: + let models = Array(attributes.filter({ attribute in + if case .model = attribute { + return true + } else { + return false + } + })) + for model in models { + effectiveGifts.append([model]) + } + case .backdrops: + let selectedModel = self.selectedModel ?? self.previewModels[self.previewModelIndex] + let selectedSymbol = self.selectedSymbol ?? self.previewSymbols[self.previewSymbolIndex] + let backdrops = Array(attributes.filter({ attribute in + if case .backdrop = attribute { + return true + } else { + return false + } + })) + for backdrop in backdrops { + effectiveGifts.append([ + selectedModel, + backdrop, + selectedSymbol + ]) + } + case .symbols: + let selectedBackdrop = self.selectedBackdrop ?? self.previewBackdrops[self.previewBackdropIndex] + let symbols = Array(attributes.filter({ attribute in + if case .pattern = attribute { + return true + } else { + return false + } + })) + for symbol in symbols { + effectiveGifts.append([ + selectedBackdrop, + symbol + ]) + } + } + self.effectiveGifts = effectiveGifts + } + + private func updateItems(transition: ComponentTransition) { + guard let component = self.component, let environment = self.environment, let itemLayout = self.itemLayout else { + return + } + + let visibleBounds = self.scrollView.bounds.insetBy(dx: 0.0, dy: -10.0) + + let fillingSize: CGFloat + if case .regular = environment.metrics.widthClass { + fillingSize = min(itemLayout.containerSize.width, 414.0) - environment.safeInsets.left * 2.0 + } else { + fillingSize = min(itemLayout.containerSize.width, environment.deviceMetrics.screenSize.width) - environment.safeInsets.left * 2.0 + } + + let rawSideInset: CGFloat = floor((itemLayout.containerSize.width - fillingSize) * 0.5) + let sideInset: CGFloat = rawSideInset + 16.0 + + let optionSpacing: CGFloat = 10.0 + let optionWidth = (fillingSize - 16.0 * 2.0 - optionSpacing * 2.0) / 3.0 + let optionSize = CGSize(width: optionWidth, height: 126.0) + + let topInset: CGFloat = 393.0 + + var validIds: [AnyHashable] = [] + var itemFrame = CGRect(origin: CGPoint(x: sideInset, y: topInset + 9.0), size: optionSize) + + for attributeList in self.effectiveGifts { + var isVisible = false + if visibleBounds.intersects(itemFrame) { + isVisible = true + } + + var itemId = "" + var title = "" + var rarity: Int32 = 0 + + var modelAttribute: StarGift.UniqueGift.Attribute? + var backdropAttribute: StarGift.UniqueGift.Attribute? + var symbolAttribute: StarGift.UniqueGift.Attribute? + + switch self.selectedSection { + case .models: + itemId += "models_" + case .backdrops: + itemId += "backdrops_" + case .symbols: + itemId += "symbols_" + } + + var isSelected = false + for attribute in attributeList { + switch attribute { + case let .model(name, file, rarityValue): + itemId += "\(file.fileId.id)" + if self.selectedSection == .models { + title = name + rarity = rarityValue + modelAttribute = attribute + + if case let .model(_, selectedFile, _) = self.selectedModel { + isSelected = file.fileId == selectedFile.fileId + } else { + isSelected = false + } + } + case let .backdrop(name, id, _, _, _, _, rarityValue): + itemId += "\(id)" + if self.selectedSection == .backdrops { + title = name + rarity = rarityValue + backdropAttribute = attribute + + if case let .backdrop(_, selectedId, _, _, _, _, _) = self.selectedBackdrop { + isSelected = id == selectedId + } else { + isSelected = false + } + } + case let .pattern(name, file, rarityValue): + itemId += "\(file.fileId.id)" + if self.selectedSection == .symbols { + title = name + rarity = rarityValue + symbolAttribute = attribute + + if case let .pattern(_, selectedFile, _) = self.selectedSymbol { + isSelected = file.fileId == selectedFile.fileId + } else { + isSelected = false + } + } + default: + break + } + } + + if isVisible { + validIds.append(itemId) + + var itemTransition = transition + let visibleItem: ComponentView + if let current = self.giftItems[itemId] { + visibleItem = current + } else { + visibleItem = ComponentView() + if !transition.animation.isImmediate { + itemTransition = .immediate + } + self.giftItems[itemId] = visibleItem + } + + let subject: GiftItemComponent.Subject = .preview(attributes: attributeList, rarity: rarity) + let _ = visibleItem.update( + transition: itemTransition, + component: AnyComponent( + PlainButtonComponent( + content: AnyComponent( + GiftItemComponent( + context: component.context, + theme: environment.theme, + strings: environment.strings, + peer: nil, + subject: subject, + title: title, + ribbon: nil, + isSelected: isSelected, + mode: .upgradePreview + ) + ), + effectAlignment: .center, + action: { [weak self] in + guard let self, let state = self.state else { + return + } + if self.isPlaying { + self.isPlaying = false + self.showRandomizeTip = true + Queue.mainQueue().after(2.0) { + if self.showRandomizeTip { + self.showRandomizeTip = false + self.state?.updated(transition: .easeInOut(duration: 0.25)) + } + } + } + + switch self.selectedSection { + case .models: + self.selectedModel = modelAttribute + case .backdrops: + self.selectedBackdrop = backdropAttribute + case .symbols: + self.selectedSymbol = symbolAttribute + } + + state.updated(transition: .easeInOut(duration: 0.25)) + }, + animateAlpha: false + ) + ), + environment: {}, + containerSize: optionSize + ) + if let itemView = visibleItem.view { + if itemView.superview == nil { + self.scrollContentView.addSubview(itemView) + + if !transition.animation.isImmediate { + itemView.layer.animateScale(from: 0.01, to: 1.0, duration: 0.25) + itemView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) + } + } + itemTransition.setFrame(view: itemView, frame: itemFrame) + } + } + itemFrame.origin.x += itemFrame.width + optionSpacing + if itemFrame.maxX > rawSideInset + fillingSize { + itemFrame.origin.x = sideInset + itemFrame.origin.y += optionSize.height + optionSpacing + } + } + + var removeIds: [AnyHashable] = [] + for (id, item) in self.giftItems { + if !validIds.contains(id) { + removeIds.append(id) + if let itemView = item.view { + if !transition.animation.isImmediate { + itemView.layer.animateScale(from: 1.0, to: 0.01, duration: 0.25, removeOnCompletion: false) + itemView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in + itemView.removeFromSuperview() + }) + } else { + itemView.removeFromSuperview() + } + } + } + } + for id in removeIds { + self.giftItems.removeValue(forKey: id) + } + } + + func update(component: GiftUpgradeVariantsScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.isUpdating = true + defer { + self.isUpdating = false + } + + self.updateTimer() + + let environment = environment[ViewControllerComponentContainer.Environment.self].value + let themeUpdated = self.environment?.theme !== environment.theme + + let resetScrolling = self.scrollView.bounds.width != availableSize.width + + let fillingSize: CGFloat + if case .regular = environment.metrics.widthClass { + fillingSize = min(availableSize.width, 414.0) - environment.safeInsets.left * 2.0 + } else { + fillingSize = min(availableSize.width, environment.deviceMetrics.screenSize.width) - environment.safeInsets.left * 2.0 + } + let rawSideInset: CGFloat = floor((availableSize.width - fillingSize) * 0.5) + let sideInset: CGFloat = rawSideInset + 16.0 + + if self.component == nil { + var modelCount: Int32 = 0 + var backdropCount: Int32 = 0 + var symbolCount: Int32 = 0 + for attribute in component.attributes { + switch attribute { + case .model: + modelCount += 1 + case .backdrop: + backdropCount += 1 + case .pattern: + symbolCount += 1 + default: + break + } + } + self.modelCount = modelCount + self.backdropCount = backdropCount + self.symbolCount = symbolCount + + let randomModels = Array(component.attributes.filter({ attribute in + if case .model = attribute { + return true + } else { + return false + } + }).shuffled().prefix(15)) + self.previewModels = randomModels + + let randomBackdrops = Array(component.attributes.filter({ attribute in + if case .backdrop = attribute { + return true + } else { + return false + } + }).shuffled()) + self.previewBackdrops = randomBackdrops + + let randomSymbols = Array(component.attributes.filter({ attribute in + if case .pattern = attribute { + return true + } else { + return false + } + }).shuffled().prefix(15)) + self.previewSymbols = randomSymbols + + if let selectedAttributes = component.selectedAttributes { + self.isPlaying = false + for attribute in selectedAttributes { + switch attribute { + case .model: + self.selectedModel = attribute + case .pattern: + self.selectedSymbol = attribute + case .backdrop: + self.selectedBackdrop = attribute + default: + break + } + } + } + if let focusedAttribute = component.focusedAttribute { + switch focusedAttribute { + case .model: + self.selectedSection = .models + case .pattern: + self.selectedSection = .symbols + case .backdrop: + self.selectedSection = .backdrops + default: + break + } + } + + self.updateEffectiveGifts(attributes: component.attributes) + } + + self.component = component + self.state = state + self.environment = environment + + let theme = environment.theme.withModalBlocksBackground() + + if themeUpdated { + self.dimView.backgroundColor = UIColor(white: 0.0, alpha: 0.5) + self.backgroundLayer.backgroundColor = theme.list.blocksBackgroundColor.cgColor + } + + transition.setFrame(view: self.dimView, frame: CGRect(origin: CGPoint(), size: availableSize)) + + var buttonColor: UIColor = .white.withAlphaComponent(0.1) + var secondaryTextColor: UIColor = .white.withAlphaComponent(0.4) + var badgeColor: UIColor = .white.withAlphaComponent(0.4) + + var attributes: [StarGift.UniqueGift.Attribute] = [] + if !self.previewModels.isEmpty { + if self.isPlaying { + attributes.append(self.previewModels[self.previewModelIndex]) + attributes.append(self.previewBackdrops[self.previewBackdropIndex]) + attributes.append(self.previewSymbols[self.previewSymbolIndex]) + } else { + if self.selectedModel == nil { + self.selectedModel = self.previewModels[self.previewModelIndex] + } + if self.selectedBackdrop == nil { + self.selectedBackdrop = self.previewBackdrops[self.previewBackdropIndex] + } + if self.selectedSymbol == nil { + self.selectedSymbol = self.previewSymbols[self.previewSymbolIndex] + } + if let model = self.selectedModel { + attributes.append(model) + } + if let backdrop = self.selectedBackdrop { + attributes.append(backdrop) + } + if let symbol = self.selectedSymbol { + attributes.append(symbol) + } + } + } + + if let backdropAttribute = attributes.first(where: { attribute in + if case .backdrop = attribute { + return true + } else { + return false + } + }), case let .backdrop(_, _, innerColor, outerColor, _, _, _) = backdropAttribute { + buttonColor = UIColor(rgb: UInt32(bitPattern: innerColor)).withMultipliedBrightnessBy(1.05) + + badgeColor = UIColor(rgb: UInt32(bitPattern: innerColor)).withMultipliedBrightnessBy(1.05) + let outer = UIColor(rgb: UInt32(bitPattern: outerColor)) + if outer.lightness < 0.06 { + badgeColor = UIColor(rgb: UInt32(bitPattern: innerColor)).withMultipliedBrightnessBy(1.45) + } else if outer.lightness < 0.295 { + badgeColor = UIColor(rgb: UInt32(bitPattern: innerColor)).withMultipliedBrightnessBy(1.19) + } + secondaryTextColor = UIColor(rgb: UInt32(bitPattern: innerColor)).withMultiplied(hue: 1.0, saturation: 1.02, brightness: 1.25).mixedWith(UIColor.white, alpha: 0.3) + } + + var contentHeight: CGFloat = 0.0 + let headerSize = self.header.update( + transition: transition, + component: AnyComponent(GiftCompositionComponent( + context: component.context, + theme: environment.theme, + subject: .preview(attributes), + animationOffset: CGPoint(x: 0.0, y: 20.0), + animationScale: nil, + displayAnimationStars: false, + alwaysAnimateTransition: true, + revealedAttributes: Set(), + externalState: self.giftCompositionExternalState, + requestUpdate: { [weak state] transition in + state?.updated(transition: transition) + } + )), + environment: {}, + containerSize: CGSize(width: fillingSize, height: 300.0), + ) + let headerFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - headerSize.width) * 0.5), y: 0.0), size: headerSize) + if let headerView = self.header.view { + if headerView.superview == nil { + headerView.isUserInteractionEnabled = false + headerView.clipsToBounds = true + headerView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] + headerView.layer.cornerRadius = 38.0 + self.navigationBarContainer.addSubview(headerView) + } + transition.setFrame(view: headerView, frame: headerFrame) + } + + contentHeight += headerSize.height + + var titleText: String = "" + if case let .generic(gift) = component.gift { + titleText = gift.title ?? "" + } + + let titleSize = self.title.update( + transition: transition, + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: titleText, font: Font.semibold(20.0), textColor: .white)) + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0) + ) + let titleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - titleSize.width) * 0.5), y: contentHeight - 124.0), size: titleSize) + if let titleView = self.title.view { + if titleView.superview == nil { + self.navigationBarContainer.addSubview(titleView) + } + transition.setFrame(view: titleView, frame: titleFrame) + } + + var subtitleItems: [AnimatedTextComponent.Item] = [] + let subtitleString = self.isPlaying ? environment.strings.Gift_Variants_RandomTraits : environment.strings.Gift_Variants_SelectedTraits + let words = subtitleString.components(separatedBy: " ") + for i in 0 ..< words.count { + var text = words[i] + if i > 0 { + text = " \(text)" + } + subtitleItems.append(AnimatedTextComponent.Item(id: text.lowercased(), content: .text(text))) + } + + let subtitleSize = self.subtitle.update( + transition: .spring(duration: 0.2), + component: AnyComponent(AnimatedTextComponent( + font: Font.regular(14.0), + color: secondaryTextColor, + items: subtitleItems, + noDelay: true, + blur: true + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0) + ) + let subtitleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - subtitleSize.width) * 0.5), y: contentHeight - 97.0), size: subtitleSize) + if let subtitleView = self.subtitle.view { + if subtitleView.superview == nil { + self.navigationBarContainer.addSubview(subtitleView) + } + transition.setFrame(view: subtitleView, frame: subtitleFrame) + } + + let attributeSpacing: CGFloat = 10.0 + let attributeWidth: CGFloat = floor((fillingSize - attributeSpacing * CGFloat(attributes.count - 1)) / CGFloat(attributes.count)) + let attributeHeight: CGFloat = 45.0 + + for i in 0 ..< attributes.count { + var attributeFrame = CGRect(origin: CGPoint(x: sideInset + CGFloat(i) * (attributeWidth + attributeSpacing), y: contentHeight - 60.0), size: CGSize(width: attributeWidth, height: attributeHeight)) + if i == attributes.count - 1 { + attributeFrame.size.width = max(0.0, availableSize.width - sideInset - attributeFrame.minX) + } + let attributeInfo: ComponentView + if self.attributeInfos.count > i { + attributeInfo = self.attributeInfos[i] + } else { + attributeInfo = ComponentView() + self.attributeInfos.append(attributeInfo) + } + let attribute = attributes[i] + let _ = attributeInfo.update( + transition: transition, + component: AnyComponent(AttributeInfoComponent( + strings: environment.strings, + backgroundColor: UIColor.white.withAlphaComponent(0.16), + secondaryTextColor: secondaryTextColor.mixedWith(.white, alpha: 0.3), + badgeColor: badgeColor, + attribute: attribute + )), + environment: {}, + containerSize: attributeFrame.size + ) + if let attributeInfoView = attributeInfo.view { + if attributeInfoView.superview == nil { + self.navigationBarContainer.addSubview(attributeInfoView) + } + transition.setFrame(view: attributeInfoView, frame: attributeFrame) + } + } + + let edgeEffectHeight: CGFloat = 44.0 + let edgeEffectFrame = CGRect(origin: CGPoint(x: rawSideInset, y: contentHeight + 44.0), size: CGSize(width: fillingSize, height: edgeEffectHeight)) + let edgeSolidFrame = CGRect(origin: CGPoint(x: rawSideInset, y: contentHeight), size: CGSize(width: fillingSize, height: 44.0)) + transition.setFrame(view: self.topEdgeSolidView, frame: edgeSolidFrame) + transition.setFrame(view: self.topEdgeEffectView, frame: edgeEffectFrame) + self.topEdgeSolidView.backgroundColor = theme.list.blocksBackgroundColor + self.topEdgeEffectView.update(content: theme.list.blocksBackgroundColor, blur: true, alpha: 1.0, rect: edgeEffectFrame, edge: .top, edgeSize: edgeEffectFrame.height, transition: transition) + + contentHeight += 16.0 + + let selectedId: AnyHashable + switch self.selectedSection { + case .models: + selectedId = AnyHashable(SelectedSection.models) + case .backdrops: + selectedId = AnyHashable(SelectedSection.backdrops) + case .symbols: + selectedId = AnyHashable(SelectedSection.symbols) + } + + let segmentedSize = self.segmentControl.update( + transition: transition, + component: AnyComponent(SegmentControlComponent( + theme: environment.theme, + items: [ + SegmentControlComponent.Item(id: AnyHashable(SelectedSection.models), title: environment.strings.Gift_Variants_Models), + SegmentControlComponent.Item(id: AnyHashable(SelectedSection.backdrops), title: environment.strings.Gift_Variants_Backdrops), + SegmentControlComponent.Item(id: AnyHashable(SelectedSection.symbols), title: environment.strings.Gift_Variants_Symbols) + ], + selectedId: selectedId, + action: { [weak self] id in + guard let self, let component = self.component, let id = id.base as? SelectedSection else { + return + } + self.selectedSection = id + self.isPlaying = false + + self.updateEffectiveGifts(attributes: component.attributes) + self.state?.updated(transition: ComponentTransition(animation: .curve(duration: 0.4, curve: .spring))) + })), + environment: {}, + containerSize: CGSize(width: fillingSize - 8.0 * 2.0, height: 100.0) + ) + let segmentedControlFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - segmentedSize.width) * 0.5), y: contentHeight), size: segmentedSize) + if let segmentedControlComponentView = self.segmentControl.view { + if segmentedControlComponentView.superview == nil { + self.navigationBarContainer.addSubview(self.topEdgeSolidView) + self.navigationBarContainer.addSubview(self.topEdgeEffectView) + self.navigationBarContainer.addSubview(segmentedControlComponentView) + } + transition.setFrame(view: segmentedControlComponentView, frame: segmentedControlFrame) + } + contentHeight += segmentedSize.height + contentHeight += 18.0 + + let itemHeight: CGFloat = 126.0 + let itemSpacing: CGFloat = 10.0 + + let descriptionText: String + let itemCount: Int32 + switch self.selectedSection { + case .models: + descriptionText = environment.strings.Gift_Variants_CollectionInfo(environment.strings.Gift_Variants_CollectionInfo_Model(self.modelCount)).string + itemCount = self.modelCount + case .backdrops: + descriptionText = environment.strings.Gift_Variants_CollectionInfo(environment.strings.Gift_Variants_CollectionInfo_Backdrop(self.backdropCount)).string + itemCount = self.backdropCount + case .symbols: + descriptionText = environment.strings.Gift_Variants_CollectionInfo(environment.strings.Gift_Variants_CollectionInfo_Symbol(self.symbolCount)).string + itemCount = self.symbolCount + } + + let descriptionFont = Font.regular(13.0) + let descriptionBoldFont = Font.semibold(13.0) + let descriptionTextColor = theme.list.itemSecondaryTextColor + let descriptionMarkdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: descriptionFont, textColor: descriptionTextColor), bold: MarkdownAttributeSet(font: descriptionBoldFont, textColor: descriptionTextColor), link: MarkdownAttributeSet(font: descriptionFont, textColor: descriptionTextColor), linkAttribute: { contents in + return (TelegramTextAttributes.URL, contents) + }) + + let descriptionSize = self.descriptionText.update( + transition: .immediate, + component: AnyComponent(MultilineTextComponent( + text: .markdown(text: descriptionText, attributes: descriptionMarkdownAttributes) + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0) + ) + let descriptionFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - descriptionSize.width) * 0.5), y: contentHeight), size: descriptionSize) + if let descriptionView = self.descriptionText.view { + if descriptionView.superview == nil { + self.scrollContentView.addSubview(descriptionView) + } + descriptionView.frame = descriptionFrame + } + contentHeight += descriptionSize.height + contentHeight += 26.0 + + contentHeight += (itemHeight + itemSpacing) * ceil(CGFloat(itemCount) / 3.0) + + if self.backgroundHandleView.image == nil { + self.backgroundHandleView.image = generateStretchableFilledCircleImage(diameter: 5.0, color: .white)?.withRenderingMode(.alwaysTemplate) + } + self.backgroundHandleView.tintColor = UIColor.white.withAlphaComponent(0.4) + let backgroundHandleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - 36.0) * 0.5), y: 5.0), size: CGSize(width: 36.0, height: 5.0)) + if self.backgroundHandleView.superview == nil { + self.navigationBarContainer.addSubview(self.backgroundHandleView) + } + transition.setFrame(view: self.backgroundHandleView, frame: backgroundHandleFrame) + + self.playbackGlassContainerView.update(size: CGSize(width: fillingSize, height: 64.0), isDark: false, transition: .immediate) + self.playbackGlassContainerView.frame = CGRect(origin: CGPoint(x: rawSideInset, y: 0.0), size: CGSize(width: fillingSize, height: 64.0)) + + self.closeGlassContainerView.update(size: CGSize(width: 64.0, height: 64.0), isDark: false, transition: .immediate) + self.closeGlassContainerView.frame = CGRect(origin: CGPoint(x: rawSideInset, y: 0.0), size: CGSize(width: 64.0, height: 64.0)) + + let closeButtonSize = self.closeButton.update( + transition: transition, + component: AnyComponent(GlassBarButtonComponent( + size: CGSize(width: 40.0, height: 40.0), + backgroundColor: buttonColor, + isDark: false, + state: .tintedGlass, + component: AnyComponentWithIdentity(id: "close", component: AnyComponent( + BundleIconComponent( + name: "Navigation/Back", + tintColor: .white + ) + )), + action: { [weak self] _ in + guard let self else { + return + } + self.environment?.controller()?.dismiss() + } + )), + environment: {}, + containerSize: CGSize(width: 40.0, height: 40.0) + ) + let closeButtonFrame = CGRect(origin: CGPoint(x: 16.0, y: 16.0), size: closeButtonSize) + if let closeButtonView = self.closeButton.view { + if closeButtonView.superview == nil { + self.navigationBarContainer.addSubview(self.playbackGlassContainerView) + self.navigationBarContainer.addSubview(self.closeGlassContainerView) + self.closeGlassContainerView.contentView.addSubview(closeButtonView) + } + transition.setFrame(view: closeButtonView, frame: closeButtonFrame) + } + + let playbackButtonSize = self.playbackButton.update( + transition: transition, + component: AnyComponent(GlassBarButtonComponent( + size: nil, + backgroundColor: buttonColor, + isDark: false, + state: .tintedGlass, + component: AnyComponentWithIdentity(id: "content", component: AnyComponent( + PlayButtonComponent(isPlay: !self.isPlaying, title: !self.isPlaying && self.showRandomizeTip ? environment.strings.Gift_Variants_Randomize : nil) + )), + action: { [weak self] _ in + guard let self else { + return + } + self.isPlaying = !self.isPlaying + + if !self.isPlaying { + self.showRandomizeTip = true + Queue.mainQueue().after(2.0) { + if self.showRandomizeTip { + self.showRandomizeTip = false + self.state?.updated(transition: .easeInOut(duration: 0.25)) + } + } + } else { + self.selectedModel = nil + self.selectedBackdrop = nil + self.selectedSymbol = nil + + self.showRandomizeTip = false + + self.previewTimerTick() + } + self.state?.updated(transition: .easeInOut(duration: 0.25)) + } + )), + environment: {}, + containerSize: CGSize(width: 160.0, height: 40.0) + ) + let playbackButtonFrame = CGRect(origin: CGPoint(x: fillingSize - 16.0 - playbackButtonSize.width, y: 16.0), size: playbackButtonSize) + if let playbackButtonView = self.playbackButton.view { + if playbackButtonView.superview == nil { + self.playbackGlassContainerView.contentView.addSubview(playbackButtonView) + } + transition.setFrame(view: playbackButtonView, frame: playbackButtonFrame) + } + + let containerInset: CGFloat = environment.statusBarHeight + 10.0 + contentHeight += environment.safeInsets.bottom + + var initialContentHeight = contentHeight + let clippingY: CGFloat + + initialContentHeight = contentHeight + + clippingY = availableSize.height + + let topInset: CGFloat = max(0.0, availableSize.height - containerInset - initialContentHeight) + + let scrollContentHeight = max(topInset + contentHeight + containerInset, availableSize.height - containerInset) + + self.scrollContentClippingView.layer.cornerRadius = 38.0 + + self.itemLayout = ItemLayout(containerSize: availableSize, containerInset: containerInset, containerCornerRadius: environment.deviceMetrics.screenCornerRadius, bottomInset: environment.safeInsets.bottom, topInset: topInset) + + transition.setFrame(view: self.scrollContentView, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset + containerInset), size: CGSize(width: availableSize.width, height: contentHeight))) + + transition.setPosition(layer: self.backgroundLayer, position: CGPoint(x: availableSize.width / 2.0, y: availableSize.height / 2.0)) + transition.setBounds(layer: self.backgroundLayer, bounds: CGRect(origin: CGPoint(), size: CGSize(width: fillingSize, height: availableSize.height))) + + let scrollClippingFrame = CGRect(origin: CGPoint(x: 0.0, y: containerInset), size: CGSize(width: availableSize.width, height: clippingY - containerInset)) + transition.setPosition(view: self.scrollContentClippingView, position: scrollClippingFrame.center) + transition.setBounds(view: self.scrollContentClippingView, bounds: CGRect(origin: CGPoint(x: scrollClippingFrame.minX, y: scrollClippingFrame.minY), size: scrollClippingFrame.size)) + + self.ignoreScrolling = true + transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: availableSize.height))) + let contentSize = CGSize(width: availableSize.width, height: scrollContentHeight) + if contentSize != self.scrollView.contentSize { + self.scrollView.contentSize = contentSize + } + if resetScrolling { + self.scrollView.bounds = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: availableSize) + } + self.ignoreScrolling = false + self.updateScrolling(transition: transition) + + transition.setPosition(view: self.containerView, position: CGRect(origin: CGPoint(), size: availableSize).center) + transition.setBounds(view: self.containerView, bounds: CGRect(origin: CGPoint(), size: availableSize)) + + if let controller = environment.controller(), !controller.automaticallyControlPresentationContextLayout { + let bottomInset: CGFloat = contentHeight - 12.0 + + let layout = ContainerViewLayout( + size: availableSize, + metrics: environment.metrics, + deviceMetrics: environment.deviceMetrics, + intrinsicInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: bottomInset, right: 0.0), + safeInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0), + additionalInsets: .zero, + statusBarHeight: environment.statusBarHeight, + inputHeight: nil, + inputHeightIsInteractivellyChanging: false, + inVoiceOver: false + ) + controller.presentationContext.containerLayoutUpdated(layout, transition: transition.containedViewLayoutTransition) + } + + return availableSize + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + +public class GiftUpgradeVariantsScreen: ViewControllerComponentContainer { + private let context: AccountContext + + private var didPlayAppearAnimation: Bool = false + private var isDismissed: Bool = false + + public init( + context: AccountContext, + gift: StarGift, + attributes: [StarGift.UniqueGift.Attribute], + selectedAttributes: [StarGift.UniqueGift.Attribute]?, + focusedAttribute: StarGift.UniqueGift.Attribute? + ) { + self.context = context + + super.init(context: context, component: GiftUpgradeVariantsScreenComponent( + context: context, + gift: gift, + attributes: attributes, + selectedAttributes: selectedAttributes, + focusedAttribute: focusedAttribute + ), navigationBarAppearance: .none, theme: .default) + + self.statusBar.statusBarStyle = .Ignore + self.navigationPresentation = .flatModal + self.blocksBackgroundWhenInOverlay = true + self.automaticallyControlPresentationContextLayout = false + } + + required public init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + } + + override public func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + self.view.disablesInteractiveModalDismiss = true + + if !self.didPlayAppearAnimation { + self.didPlayAppearAnimation = true + + if let componentView = self.node.hostView.componentView as? GiftUpgradeVariantsScreenComponent.View { + componentView.animateIn() + } + } + } + + override public func dismiss(completion: (() -> Void)? = nil) { + if !self.isDismissed { + self.isDismissed = true + + if let componentView = self.node.hostView.componentView as? GiftUpgradeVariantsScreenComponent.View { + componentView.animateOut(completion: { [weak self] in + completion?() + self?.dismiss(animated: false) + }) + } else { + self.dismiss(animated: false) + } + } + } +} + +private final class AttributeInfoComponent: Component { + let strings: PresentationStrings + let backgroundColor: UIColor + let secondaryTextColor: UIColor + let badgeColor: UIColor + let attribute: StarGift.UniqueGift.Attribute + + init( + strings: PresentationStrings, + backgroundColor: UIColor, + secondaryTextColor: UIColor, + badgeColor: UIColor, + attribute: StarGift.UniqueGift.Attribute + ) { + self.strings = strings + self.backgroundColor = backgroundColor + self.secondaryTextColor = secondaryTextColor + self.badgeColor = badgeColor + self.attribute = attribute + } + + static func ==(lhs: AttributeInfoComponent, rhs: AttributeInfoComponent) -> Bool { + if lhs.strings !== rhs.strings { + return false + } + if lhs.backgroundColor != rhs.backgroundColor { + return false + } + if lhs.secondaryTextColor != rhs.secondaryTextColor { + return false + } + if lhs.badgeColor != rhs.badgeColor { + return false + } + if lhs.attribute != rhs.attribute { + return false + } + return true + } + + final class View: UIView { + let background = SimpleLayer() + let title = ComponentView() + let subtitle = ComponentView() + + let badgeBackground = SimpleLayer() + let badge = ComponentView() + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(component: AttributeInfoComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + let backgroundFrame = CGRect(origin: CGPoint(), size: availableSize) + if self.background.superlayer == nil { + self.background.cornerRadius = 16.0 + self.background.cornerCurve = .continuous + self.layer.addSublayer(self.background) + + self.badgeBackground.cornerRadius = 9.5 + self.badgeBackground.cornerCurve = .continuous + self.layer.addSublayer(self.badgeBackground) + } + self.background.frame = backgroundFrame + transition.setBackgroundColor(layer: self.background, color: component.backgroundColor) + + let title: String + let subtitle: String + let rarity: Int32 + switch component.attribute { + case let .model(name, _, rarityValue): + title = name + subtitle = component.strings.Gift_Variants_Model + rarity = rarityValue + case let .backdrop(name, _, _, _, _, _, rarityValue): + title = name + subtitle = component.strings.Gift_Variants_Backdrop + rarity = rarityValue + case let .pattern(name, _, rarityValue): + title = name + subtitle = component.strings.Gift_Variants_Symbol + rarity = rarityValue + default: + title = "" + subtitle = "" + rarity = 0 + } + + let titleSize = self.title.update( + transition: .spring(duration: 0.2), + component: AnyComponent(AnimatedTextComponent( + font: Font.semibold(13.0), + color: UIColor.white, + items: [AnimatedTextComponent.Item(id: "title", content: .text(title))], + noDelay: true, + blur: true + )), + environment: {}, + containerSize: CGSize(width: backgroundFrame.size.width - 8.0, height: backgroundFrame.size.height) + ) + let subtitleSize = self.subtitle.update( + transition: .immediate, + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: subtitle, font: Font.regular(11.0), textColor: .white)), + tintColor: component.secondaryTextColor + )), + environment: {}, + containerSize: backgroundFrame.size + ) + + let spacing: CGFloat = 0.0 + let titleFrame = CGRect(origin: CGPoint(x: floor((backgroundFrame.width - titleSize.width) * 0.5), y: floor((backgroundFrame.height - titleSize.height - spacing - subtitleSize.height) * 0.5)), size: titleSize) + let subtitleFrame = CGRect(origin: CGPoint(x: floor((backgroundFrame.width - subtitleSize.width) * 0.5), y: titleFrame.maxY + spacing), size: subtitleSize) + + if let titleView = self.title.view { + if titleView.superview == nil { + self.addSubview(titleView) + } + transition.setFrame(view: titleView, frame: titleFrame) + } + + if let subtitleView = self.subtitle.view { + if subtitleView.superview == nil { + self.addSubview(subtitleView) + } + transition.setFrame(view: subtitleView, frame: subtitleFrame) + } + + func formatPercentage(_ value: Float) -> String { + return String(format: "%0.1f", value).replacingOccurrences(of: ".0", with: "").replacingOccurrences(of: ",0", with: "") + } + let percentage = Float(rarity) * 0.1 + + let badgeSize = self.badge.update( + transition: .spring(duration: 0.2), + component: AnyComponent(AnimatedTextComponent( + font: Font.with(size: 12.0, weight: .semibold, traits: .monospacedNumbers), + color: UIColor.white, + items: [ + AnimatedTextComponent.Item(id: "value", content: .text(formatPercentage(percentage))), + AnimatedTextComponent.Item(id: "percent", content: .text("%")), + ], + noDelay: true, + blur: true + )), + environment: {}, + containerSize: backgroundFrame.size + ) + let badgeFrame = CGRect(origin: CGPoint(x: backgroundFrame.width - badgeSize.width - 2.0, y: backgroundFrame.minY - 8.0), size: badgeSize) + if let badgeView = self.badge.view { + if badgeView.superview == nil { + self.addSubview(badgeView) + } + transition.setFrame(view: badgeView, frame: badgeFrame) + } + + let badgeBackgroundFrame = badgeFrame.insetBy(dx: -5.5, dy: -2.0) + transition.setFrame(layer: self.badgeBackground, frame: badgeBackgroundFrame) + transition.setBackgroundColor(layer: self.badgeBackground, color: component.badgeColor) + + + return availableSize + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + + +private final class PlayButtonComponent: Component { + let isPlay: Bool + let title: String? + + public init( + isPlay: Bool, + title: String? + ) { + self.isPlay = isPlay + self.title = title + } + + static func ==(lhs: PlayButtonComponent, rhs: PlayButtonComponent) -> Bool { + if lhs.isPlay != rhs.isPlay { + return false + } + if lhs.title != rhs.title { + return false + } + return true + } + + final class View: UIView { + private var component: PlayButtonComponent? + private weak var componentState: EmptyComponentState? + + private let containerView = UIView() + private let titleContainerView = UIView() + private let title = ComponentView() + private let play = ComponentView() + private let pause = ComponentView() + + override init(frame: CGRect) { + super.init(frame: frame) + + self.containerView.clipsToBounds = true + self.containerView.layer.cornerRadius = 20.0 + self.addSubview(self.containerView) + + self.titleContainerView.clipsToBounds = true + self.containerView.addSubview(self.titleContainerView) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(component: PlayButtonComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + self.componentState = state + + var contentSize = CGSize(width: 15.0, height: 21.0) + + var titleSize = CGSize() + if let titleString = component.title { + titleSize = self.title.update( + transition: .immediate, + component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: titleString, font: Font.semibold(17.0), textColor: .white)))), + environment: {}, + containerSize: availableSize + ) + let titleFrame = CGRect(origin: CGPoint(x: 9.0, y: 10.0), size: titleSize) + if let titleView = self.title.view { + titleView.alpha = 1.0 + if titleView.superview == nil { + self.titleContainerView.addSubview(titleView) + transition.animateAlpha(view: titleView, from: 0.0, to: 1.0) + } + titleView.frame = titleFrame + } + contentSize.width += titleSize.width + 4.0 + } else if let titleView = self.title.view { + transition.setAlpha(view: titleView, alpha: 0.0, completion: { finished in + if finished { + titleView.removeFromSuperview() + } + }) + } + transition.setFrame(view: self.titleContainerView, frame: CGRect(origin: .zero, size: CGSize(width: titleSize.width + 14.0, height: 40.0))) + + if component.isPlay { + let iconSize = self.play.update( + transition: .immediate, + component: AnyComponent(BundleIconComponent(name: "Media Gallery/PlayButton", tintColor: .white)), + environment: {}, + containerSize: availableSize + ) + let iconFrame = CGRect(origin: CGPoint(x: contentSize.width - iconSize.width + 21.0, y: 5.0), size: iconSize) + if let iconView = self.play.view { + iconView.alpha = 1.0 + if iconView.superview == nil { + self.containerView.addSubview(iconView) + transition.animateAlpha(view: iconView, from: 0.0, to: 1.0) + transition.animateScale(view: iconView, from: 0.01, to: 1.0) + } + transition.setFrame(view: iconView, frame: iconFrame) + } + } else if let iconView = self.play.view { + transition.setAlpha(view: iconView, alpha: 0.0, completion: { finished in + if finished { + iconView.removeFromSuperview() + } + }) + transition.animateScale(view: iconView, from: 1.0, to: 0.01) + } + + if !component.isPlay { + let iconSize = self.pause.update( + transition: .immediate, + component: AnyComponent(BundleIconComponent(name: "Media Gallery/PictureInPicturePause", tintColor: .white)), + environment: {}, + containerSize: availableSize + ) + let iconFrame = CGRect(origin: CGPoint(x: contentSize.width - iconSize.width + 12.0 - UIScreenPixel, y: 13.0 - UIScreenPixel), size: iconSize) + if let iconView = self.pause.view { + iconView.alpha = 1.0 + if iconView.superview == nil { + self.containerView.addSubview(iconView) + transition.animateAlpha(view: iconView, from: 0.0, to: 1.0) + transition.animateScale(view: iconView, from: 0.01, to: 1.0) + } + transition.setFrame(view: iconView, frame: iconFrame) + } + } else if let iconView = self.pause.view { + transition.setAlpha(view: iconView, alpha: 0.0, completion: { finished in + if finished { + iconView.removeFromSuperview() + } + }) + transition.animateScale(view: iconView, from: 1.0, to: 0.01) + } + + let containerWidth: CGFloat = contentSize.width + 26.0 + let containerFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((contentSize.width - containerWidth) / 2.0), y: floorToScreenPixels((contentSize.height - 40.0) / 2.0)), size: CGSize(width: containerWidth, height: 40.0)) + transition.setFrame(view: self.containerView, frame: containerFrame) + + return contentSize + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftValueScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftValueScreen.swift index 55867118..c267b6f2 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftValueScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftValueScreen.swift @@ -25,6 +25,7 @@ import GiftAnimationComponent import ContextUI import GiftItemComponent import GlassBarButtonComponent +import TableComponent private final class GiftValueSheetContent: CombinedComponent { typealias EnvironmentType = ViewControllerComponentContainer.Environment @@ -640,7 +641,7 @@ private final class GiftValueSheetContent: CombinedComponent { component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: theme.chat.inputPanel.panelControlColor ) )), action: { [weak state] _ in diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift index 514b6016..cd942adc 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift @@ -40,6 +40,10 @@ import ChatThemeScreen import ProfileLevelRatingBarComponent import AnimatedTextComponent import InfoParagraphComponent +import ChatMessagePaymentAlertController +import TableComponent +import PeerTableCellComponent +import AvatarComponent private final class GiftViewSheetContent: CombinedComponent { typealias EnvironmentType = ViewControllerComponentContainer.Environment @@ -120,6 +124,8 @@ private final class GiftViewSheetContent: CombinedComponent { let levelsDisposable = MetaDisposable() var nextGiftToUpgrade: ProfileGiftsContext.State.StarGift? + var giftVariantsDisposable = MetaDisposable() + var buyForm: BotPaymentForm? var buyFormDisposable: Disposable? var buyDisposable: Disposable? @@ -374,6 +380,7 @@ private final class GiftViewSheetContent: CombinedComponent { self.buyDisposable?.dispose() self.levelsDisposable.dispose() self.starsTopUpOptionsDisposable?.dispose() + self.giftVariantsDisposable.dispose() } func openPeer(_ peer: EnginePeer, gifts: Bool = false, dismiss: Bool = true) { @@ -625,7 +632,7 @@ private final class GiftViewSheetContent: CombinedComponent { controller?.dismissAnimated() if let navigationController { - Queue.mainQueue().after(0.5) { + Queue.mainQueue().after(2.5) { starsContext.load(force: true) let text: String @@ -730,7 +737,7 @@ private final class GiftViewSheetContent: CombinedComponent { guard let self else { return } - Queue.mainQueue().after(0.5) { + Queue.mainQueue().after(2.5) { starsContext?.load(force: true) } switch self.subject { @@ -1134,7 +1141,7 @@ private final class GiftViewSheetContent: CombinedComponent { guard let peerId = peerIds.first else { return .complete() } - Queue.mainQueue().after(1.5, { + Queue.mainQueue().after(2.5, { if transferStars > 0 { context.starsContext?.load(force: true) } @@ -1350,6 +1357,33 @@ private final class GiftViewSheetContent: CombinedComponent { }) } + + func openUpgradeVariants(attribute: StarGift.UniqueGift.Attribute? = nil) { + guard let controller = self.getController() as? GiftViewScreen, let arguments = self.subject.arguments else { + return + } + var selectedAttributes: [StarGift.UniqueGift.Attribute]? + if case let .unique(uniqueGift) = arguments.gift { + selectedAttributes = uniqueGift.attributes + } + + self.giftVariantsDisposable.set((self.context.engine.payments.getStarGiftUpgradeAttributes(giftId: arguments.gift.giftId) + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] attributes in + guard let self, let attributes else { + return + } + let variantsController = self.context.sharedContext.makeGiftUpgradeVariantsScreen( + context: self.context, + gift: arguments.gift, + attributes: attributes, + selectedAttributes: selectedAttributes, + focusedAttribute: attribute + ) + controller.push(variantsController) + })) + } + func showAttributeInfo(tag: Any, text: String) { guard let controller = self.getController() as? GiftViewScreen else { return @@ -1821,7 +1855,7 @@ private final class GiftViewSheetContent: CombinedComponent { self.updated(transition: .spring(duration: 0.4)) - Queue.mainQueue().after(0.5) { + Queue.mainQueue().after(2.5) { switch finalPrice.currency { case .stars: context.starsContext?.load(force: true) @@ -2114,7 +2148,7 @@ private final class GiftViewSheetContent: CombinedComponent { self.subject = .profileGift(peerId, result) self.updated(transition: .spring(duration: 0.4)) - Queue.mainQueue().after(0.5) { + Queue.mainQueue().after(2.5) { starsContext?.load(force: true) } }) @@ -2233,7 +2267,7 @@ private final class GiftViewSheetContent: CombinedComponent { guard let self else { return } - Queue.mainQueue().after(0.5) { + Queue.mainQueue().after(2.5) { starsContext?.load(force: true) } @@ -2337,6 +2371,7 @@ private final class GiftViewSheetContent: CombinedComponent { guard let gift = self.subject.arguments?.gift, case let .unique(uniqueGift) = gift, case let .peerId(ownerPeerId) = uniqueGift.owner, let controller = self.getController() else { return } + let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: ownerPeerId)) |> deliverOnMainQueue).start(next: { [weak self] peer in guard let self, let peer else { @@ -2346,13 +2381,41 @@ private final class GiftViewSheetContent: CombinedComponent { guard let self else { return } - self.commitGiftBuyOffer(peer: peer, price: amount, duration: duration) + + let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.SendPaidMessageStars(id: peer.id)) + |> deliverOnMainQueue).start(next: { [weak self, weak controller] sendPaidMessageStars in + guard let self else { + return + } + let action: (Int64?) -> Void = { allowPaidStars in + self.commitGiftBuyOffer(peer: peer, price: amount, duration: duration, allowPaidStars: allowPaidStars) + } + if let sendPaidMessageStars, sendPaidMessageStars.value > 0 { + let alertController = chatMessagePaymentAlertController( + context: nil, + presentationData: presentationData, + updatedPresentationData: nil, + peers: [EngineRenderedPeer(peer: peer)], + count: 1, + amount: sendPaidMessageStars, + totalAmount: nil, + hasCheck: false, + navigationController: controller?.navigationController as? NavigationController, + completion: { _ in + action(sendPaidMessageStars.value) + } + ) + controller?.present(alertController, in: .window(.root)) + } else { + action(nil) + } + }) })) controller.push(buyController) }) } - func commitGiftBuyOffer(peer: EnginePeer, price: CurrencyAmount, duration: Int32) { + func commitGiftBuyOffer(peer: EnginePeer, price: CurrencyAmount, duration: Int32, allowPaidStars: Int64?) { guard let gift = self.subject.arguments?.gift, case let .unique(uniqueGift) = gift, let starsContext = self.context.starsContext, let starsState = starsContext.currentState else { return } @@ -2362,13 +2425,13 @@ private final class GiftViewSheetContent: CombinedComponent { guard let self else { return } - self.upgradeDisposable = (context.engine.payments.sendStarGiftOffer(peerId: peer.id, slug: uniqueGift.slug, amount: price, duration: duration, allowPaidStars: nil) + self.upgradeDisposable = (context.engine.payments.sendStarGiftOffer(peerId: peer.id, slug: uniqueGift.slug, amount: price, duration: duration, allowPaidStars: allowPaidStars) |> deliverOnMainQueue).start(error: { _ in }, completed: { [weak self, weak starsContext] in guard let self else { return } - Queue.mainQueue().after(0.5) { + Queue.mainQueue().after(2.5) { starsContext?.load(force: true) } self.openPeer(peer, dismiss: true) @@ -2383,11 +2446,15 @@ private final class GiftViewSheetContent: CombinedComponent { guard let self, let controller = self.getController() else { return } + var finalStars = price.amount.value + if let allowPaidStars { + finalStars += allowPaidStars + } let purchaseController = context.sharedContext.makeStarsPurchaseScreen( context: context, starsContext: starsContext, options: options ?? [], - purpose: .starGiftOffer(requiredStars: price.amount.value), + purpose: .starGiftOffer(requiredStars: finalStars), targetPeerId: nil, customTheme: nil, completion: { [weak self, weak starsContext] stars in @@ -2411,7 +2478,7 @@ private final class GiftViewSheetContent: CombinedComponent { self.inProgress = false self.updated() - self.commitGiftBuyOffer(peer: peer, price: price, duration: duration) + self.commitGiftBuyOffer(peer: peer, price: price, duration: duration, allowPaidStars: allowPaidStars) } else { proceed() } @@ -2480,10 +2547,11 @@ private final class GiftViewSheetContent: CombinedComponent { let upgradeNextButton = Child(PlainButtonComponent.self) let upgradeTitle = Child(MultilineTextComponent.self) - let upgradeDescription = Child(BalancedTextComponent.self) + let upgradeDescription = Child(PlainButtonComponent.self) let upgradePerks = Child(List.self) let upgradeKeepName = Child(PlainButtonComponent.self) let upgradePriceButton = Child(PlainButtonComponent.self) + let variantsMeasureDescription = Child(MultilineTextComponent.self) let spaceRegex = try? NSRegularExpression(pattern: "\\[(.*?)\\]", options: []) @@ -2687,10 +2755,10 @@ private final class GiftViewSheetContent: CombinedComponent { } headerSubject = .unique(state.justUpgraded ? state.upgradePreview?.attributes : nil, uniqueGift) } else if state.inUpgradePreview, let attributes = state.upgradePreview?.attributes { - headerHeight = 258.0 + headerHeight = 246.0 headerSubject = .preview(attributes) } else if case let .upgradePreview(attributes, _) = component.subject { - headerHeight = 258.0 + headerHeight = 246.0 headerSubject = .preview(attributes) } else if case let .wearPreview(_, attributes) = component.subject, let attributes { headerHeight = 200.0 @@ -2944,9 +3012,7 @@ private final class GiftViewSheetContent: CombinedComponent { originY += 16.0 } else if showUpgradePreview { let title: String - let description: String let uniqueText: String - let transferableText: String let tradableText: String if !incoming, case let .profileGift(peerId, _) = subject, let peer = state.peerMap[peerId] { var peerName = peer.compactDisplayTitle @@ -2954,9 +3020,7 @@ private final class GiftViewSheetContent: CombinedComponent { peerName = "\(peerName.prefix(22))…" } title = environment.strings.Gift_Upgrade_GiftTitle - description = environment.strings.Gift_Upgrade_GiftDescription(peerName).string uniqueText = strings.Gift_Upgrade_Unique_GiftDescription(peerName).string - transferableText = strings.Gift_Upgrade_Transferable_GiftDescription(peerName).string tradableText = strings.Gift_Upgrade_Tradable_GiftDescription(peerName).string } else if case let .upgradePreview(_, peerName) = component.subject { var peerName = peerName @@ -2964,15 +3028,11 @@ private final class GiftViewSheetContent: CombinedComponent { peerName = "\(peerName.prefix(22))…" } title = environment.strings.Gift_Upgrade_IncludeTitle - description = environment.strings.Gift_Upgrade_IncludeDescription(peerName).string uniqueText = strings.Gift_Upgrade_Unique_IncludeDescription - transferableText = strings.Gift_Upgrade_Transferable_IncludeDescription tradableText = strings.Gift_Upgrade_Tradable_IncludeDescription } else { title = environment.strings.Gift_Upgrade_Title - description = environment.strings.Gift_Upgrade_Description uniqueText = strings.Gift_Upgrade_Unique_Description - transferableText = strings.Gift_Upgrade_Transferable_Description tradableText = strings.Gift_Upgrade_Tradable_Description } @@ -2990,39 +3050,119 @@ private final class GiftViewSheetContent: CombinedComponent { availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 60.0, height: CGFloat.greatestFiniteMagnitude), transition: .immediate ) - let upgradeDescription = upgradeDescription.update( - component: BalancedTextComponent( - text: .plain(NSAttributedString( - string: description, - font: Font.regular(13.0), - textColor: .white, - paragraphAlignment: .center - )), - horizontalAlignment: .center, - maximumNumberOfLines: 5, - lineSpacing: 0.2, - tintColor: vibrantColor - ), - availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 50.0, height: CGFloat.greatestFiniteMagnitude), - transition: context.transition - ) - let spacing: CGFloat = 6.0 - let totalHeight: CGFloat = upgradeTitle.size.height + spacing + upgradeDescription.size.height - - headerComponents.append({ - context.add(upgradeTitle - .position(CGPoint(x: context.availableSize.width / 2.0, y: floor(212.0 - totalHeight / 2.0 + upgradeTitle.size.height / 2.0))) - .appear(.default(alpha: true)) - .disappear(.default(alpha: true)) + if case let .generic(gift) = component.subject.arguments?.gift, let upgradePreview = state.upgradePreview { + var variant1: GiftItemComponent.Subject = .starGift(gift: gift, price: "") + var variant2: GiftItemComponent.Subject = .starGift(gift: gift, price: "") + var variant3: GiftItemComponent.Subject = .starGift(gift: gift, price: "") + + var i = 0 + for attribute in upgradePreview.attributes { + if case .model = attribute { + switch i { + case 0: + variant1 = .preview(attributes: [attribute], rarity: 0) + case 1: + variant2 = .preview(attributes: [attribute], rarity: 0) + case 2: + variant3 = .preview(attributes: [attribute], rarity: 0) + default: + break + } + i += 1 + } + } + + var buttonColor: UIColor = UIColor.white.withAlphaComponent(0.16) + if let previewPatternColor = giftCompositionExternalState.previewPatternColor { + buttonColor = previewPatternColor + } + + let variantsMeasureDescription = variantsMeasureDescription.update( + component: MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Gift_Upgrade_ViewAllVariants, font: Font.semibold(13.0), textColor: .clear))), + availableSize: context.availableSize, + transition: .immediate + ) + context.add(variantsMeasureDescription + .position(CGPoint(x: -10000.0, y: -10000.0)) ) - context.add(upgradeDescription - .position(CGPoint(x: context.availableSize.width / 2.0, y: floor(212.0 + totalHeight / 2.0 - upgradeDescription.size.height / 2.0))) - .appear(.default(alpha: true)) - .disappear(.default(alpha: true)) + let upgradeDescription = upgradeDescription.update( + component: PlainButtonComponent( + content: AnyComponent( + ZStack([ + AnyComponentWithIdentity(id: "background", component: AnyComponent( + FilledRoundedRectangleComponent(color: buttonColor, cornerRadius: .minEdge, smoothCorners: false) + )), + AnyComponentWithIdentity(id: "label", component: AnyComponent(HStack([ + AnyComponentWithIdentity(id: "icon1", component: AnyComponent( + GiftItemComponent( + context: component.context, + theme: theme, + strings: strings, + peer: nil, + subject: variant1, + isPlaceholder: false, + mode: .tableIcon + ) + )), + AnyComponentWithIdentity(id: "icon2", component: AnyComponent( + GiftItemComponent( + context: component.context, + theme: theme, + strings: strings, + peer: nil, + subject: variant2, + isPlaceholder: false, + mode: .tableIcon + ) + )), + AnyComponentWithIdentity(id: "icon3", component: AnyComponent( + GiftItemComponent( + context: component.context, + theme: theme, + strings: strings, + peer: nil, + subject: variant3, + isPlaceholder: false, + mode: .tableIcon + ) + )), + AnyComponentWithIdentity(id: "text", component: AnyComponent( + MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Gift_Upgrade_ViewAllVariants, font: Font.semibold(13.0), textColor: .white))) + )), + AnyComponentWithIdentity(id: "arrow", component: AnyComponent( + BundleIconComponent(name: "Item List/InlineTextRightArrow", tintColor: .white) + )) + ], spacing: 3.0))) + ]) + ), + action: { [weak state] in + state?.openUpgradeVariants() + }, + animateScale: false + ), + availableSize: CGSize(width: variantsMeasureDescription.size.width + 87.0, height: 24.0), + transition: context.transition ) - }) + + let spacing: CGFloat = 6.0 + let totalHeight: CGFloat = upgradeTitle.size.height + spacing + upgradeDescription.size.height + + headerComponents.append({ + context.add(upgradeTitle + .position(CGPoint(x: context.availableSize.width / 2.0, y: floor(194.0 - totalHeight / 2.0 + upgradeTitle.size.height / 2.0))) + .appear(.default(alpha: true)) + .disappear(.default(alpha: true)) + ) + + context.add(upgradeDescription + .position(CGPoint(x: context.availableSize.width / 2.0, y: floor(198.0 + totalHeight / 2.0 - upgradeDescription.size.height / 2.0))) + .appear(.default(alpha: true)) + .disappear(.default(alpha: true)) + ) + }) + } originY += 24.0 let textColor = theme.actionSheet.primaryTextColor @@ -3044,20 +3184,6 @@ private final class GiftViewSheetContent: CombinedComponent { )) ) ) - items.append( - AnyComponentWithIdentity( - id: "transferable", - component: AnyComponent(InfoParagraphComponent( - title: strings.Gift_Upgrade_Transferable_Title, - titleColor: textColor, - text: transferableText, - textColor: secondaryTextColor, - accentColor: linkColor, - iconName: "Premium/Collectible/Transferable", - iconColor: linkColor - )) - ) - ) items.append( AnyComponentWithIdentity( id: "tradable", @@ -3072,6 +3198,20 @@ private final class GiftViewSheetContent: CombinedComponent { )) ) ) + items.append( + AnyComponentWithIdentity( + id: "wearable", + component: AnyComponent(InfoParagraphComponent( + title: strings.Gift_Upgrade_Wearable_Title, + titleColor: textColor, + text: strings.Gift_Upgrade_Wearable_Text, + textColor: secondaryTextColor, + accentColor: linkColor, + iconName: "Premium/Collectible/Wearable", + iconColor: linkColor + )) + ) + ) let perksSideInset = sideInset + 16.0 let upgradePerks = upgradePerks.update( @@ -3564,7 +3704,7 @@ private final class GiftViewSheetContent: CombinedComponent { id: AnyHashable(0), component: AnyComponent(Button( content: AnyComponent( - PeerCellComponent( + PeerTableCellComponent( context: component.context, theme: theme, strings: strings, @@ -3597,7 +3737,7 @@ private final class GiftViewSheetContent: CombinedComponent { } else { ownerComponent = AnyComponent(Button( content: AnyComponent( - PeerCellComponent( + PeerTableCellComponent( context: component.context, theme: theme, strings: strings, @@ -3648,7 +3788,7 @@ private final class GiftViewSheetContent: CombinedComponent { title: strings.Gift_Unique_Telegram, component: AnyComponent(Button( content: AnyComponent( - PeerCellComponent( + PeerTableCellComponent( context: component.context, theme: theme, strings: strings, @@ -3678,7 +3818,7 @@ private final class GiftViewSheetContent: CombinedComponent { id: AnyHashable(0), component: AnyComponent(Button( content: AnyComponent( - PeerCellComponent( + PeerTableCellComponent( context: component.context, theme: theme, strings: strings, @@ -3708,7 +3848,7 @@ private final class GiftViewSheetContent: CombinedComponent { } else { fromComponent = AnyComponent(Button( content: AnyComponent( - PeerCellComponent( + PeerTableCellComponent( context: component.context, theme: theme, strings: strings, @@ -3733,7 +3873,7 @@ private final class GiftViewSheetContent: CombinedComponent { id: "from_anon", title: strings.Gift_View_From, component: AnyComponent( - PeerCellComponent( + PeerTableCellComponent( context: component.context, theme: theme, strings: strings, @@ -4117,7 +4257,7 @@ private final class GiftViewSheetContent: CombinedComponent { color: theme.list.itemAccentColor )), action: { [weak state] in - state?.showAttributeInfo(tag: tag, text: strings.Gift_Unique_AttributeDescription(formatPercentage(percentage)).string) + state?.openUpgradeVariants(attribute: attribute) } ).tagged(tag)) )) @@ -5164,42 +5304,50 @@ final class GiftViewSheetComponent: CombinedComponent { var headerContent: AnyComponent? if let arguments = context.component.subject.arguments, case .unique = arguments.gift, let fromPeerId = arguments.fromPeerId, var fromPeerName = arguments.fromPeerName, arguments.fromPeerId != context.component.context.account.peerId && !(arguments.fromPeerId?.isTelegramNotifications ?? false) { - let dateString = stringForMediumDate(timestamp: arguments.date, strings: environment.strings, dateTimeFormat: environment.dateTimeFormat, withTime: false) - - if fromPeerName.count > 25 { - fromPeerName = "\(fromPeerName.prefix(25))…" + var showSenderInfo = false + if arguments.incoming { + showSenderInfo = true + } else if arguments.peerId == context.component.context.account.peerId { + showSenderInfo = true + } + if showSenderInfo { + let dateString = stringForMediumDate(timestamp: arguments.date, strings: environment.strings, dateTimeFormat: environment.dateTimeFormat, withTime: false) + + if fromPeerName.count > 25 { + fromPeerName = "\(fromPeerName.prefix(25))…" + } + let rawString = environment.strings.Gift_View_SenderInfo(fromPeerName, dateString).string + let attributedString = parseMarkdownIntoAttributedString(rawString, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: .white), bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: .white), link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: .white), linkAttribute: { _ in return nil })) + + let context = context.component.context + headerContent = AnyComponent( + PlainButtonComponent(content: AnyComponent(HeaderContentComponent(attributedText: attributedString)), action: { + if let controller = controller(), let navigationController = controller.navigationController as? NavigationController { + let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: fromPeerId)) + |> deliverOnMainQueue).start(next: { [weak navigationController] peer in + guard let peer, let navigationController else { + return + } + context.sharedContext.navigateToChatController(NavigateToChatControllerParams( + navigationController: navigationController, + chatController: nil, + context: context, + chatLocation: .peer(peer), + subject: nil, + botStart: nil, + updateTextInputState: nil, + keepStack: .always, + useExisting: true, + purposefulAction: nil, + scrollToEndIfExists: false, + activateMessageSearch: nil, + animated: true + )) + }) + } + }) + ) } - let rawString = environment.strings.Gift_View_SenderInfo(fromPeerName, dateString).string - let attributedString = parseMarkdownIntoAttributedString(rawString, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: .white), bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: .white), link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: .white), linkAttribute: { _ in return nil })) - - let context = context.component.context - headerContent = AnyComponent( - PlainButtonComponent(content: AnyComponent(HeaderContentComponent(attributedText: attributedString)), action: { - if let controller = controller(), let navigationController = controller.navigationController as? NavigationController { - let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: fromPeerId)) - |> deliverOnMainQueue).start(next: { [weak navigationController] peer in - guard let peer, let navigationController else { - return - } - context.sharedContext.navigateToChatController(NavigateToChatControllerParams( - navigationController: navigationController, - chatController: nil, - context: context, - chatLocation: .peer(peer), - subject: nil, - botStart: nil, - updateTextInputState: nil, - keepStack: .always, - useExisting: true, - purposefulAction: nil, - scrollToEndIfExists: false, - activateMessageSearch: nil, - animated: true - )) - }) - } - }) - ) } let sheet = sheet.update( @@ -5742,115 +5890,6 @@ func formatPercentage(_ value: Float) -> String { return String(format: "%0.1f", value).replacingOccurrences(of: ".0", with: "").replacingOccurrences(of: ",0", with: "") + "%" } -final class PeerCellComponent: Component { - let context: AccountContext - let theme: PresentationTheme - let strings: PresentationStrings - let peer: EnginePeer? - - init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, peer: EnginePeer?) { - self.context = context - self.theme = theme - self.strings = strings - self.peer = peer - } - - static func ==(lhs: PeerCellComponent, rhs: PeerCellComponent) -> Bool { - if lhs.context !== rhs.context { - return false - } - if lhs.theme !== rhs.theme { - return false - } - if lhs.strings !== rhs.strings { - return false - } - if lhs.peer != rhs.peer { - return false - } - return true - } - - final class View: UIView { - private let avatarNode: AvatarNode - private let text = ComponentView() - - private var component: PeerCellComponent? - private weak var state: EmptyComponentState? - - override init(frame: CGRect) { - self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 8.0)) - - super.init(frame: frame) - - self.addSubnode(self.avatarNode) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func update(component: PeerCellComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { - self.component = component - self.state = state - - let avatarSize = CGSize(width: 22.0, height: 22.0) - let spacing: CGFloat = 6.0 - - var peerName: String - let avatarOverride: AvatarNodeImageOverride? - if let peerValue = component.peer { - peerName = peerValue.compactDisplayTitle - if peerName.count > 40 { - peerName = "\(peerName.prefix(40))…" - } - avatarOverride = nil - } else { - peerName = component.strings.Gift_View_HiddenName - avatarOverride = .anonymousSavedMessagesIcon(isColored: true) - } - - let avatarNaturalSize = CGSize(width: 40.0, height: 40.0) - self.avatarNode.setPeer(context: component.context, theme: component.theme, peer: component.peer, overrideImage: avatarOverride) - self.avatarNode.bounds = CGRect(origin: .zero, size: avatarNaturalSize) - - let textSize = self.text.update( - transition: .immediate, - component: AnyComponent( - MultilineTextComponent( - text: .plain(NSAttributedString(string: peerName, font: Font.regular(15.0), textColor: component.peer != nil ? component.theme.list.itemAccentColor : component.theme.list.itemPrimaryTextColor, paragraphAlignment: .left)) - ) - ), - environment: {}, - containerSize: CGSize(width: availableSize.width - avatarSize.width - spacing, height: availableSize.height) - ) - - let size = CGSize(width: avatarSize.width + textSize.width + spacing, height: textSize.height) - - let avatarFrame = CGRect(origin: CGPoint(x: 0.0, y: floorToScreenPixels((size.height - avatarSize.height) / 2.0)), size: avatarSize) - self.avatarNode.frame = avatarFrame - - if let view = self.text.view { - if view.superview == nil { - self.addSubview(view) - } - let textFrame = CGRect(origin: CGPoint(x: avatarSize.width + spacing, y: floorToScreenPixels((size.height - textSize.height) / 2.0)), size: textSize) - transition.setFrame(view: view, frame: textFrame) - } - - return size - } - } - - func makeView() -> View { - return View(frame: CGRect()) - } - - func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { - return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) - } -} - final class HeaderContentComponent: Component { let attributedText: NSAttributedString @@ -6165,73 +6204,6 @@ private final class HeaderButtonComponent: CombinedComponent { } } -final class AvatarComponent: Component { - let context: AccountContext - let theme: PresentationTheme - let peer: EnginePeer - - init(context: AccountContext, theme: PresentationTheme, peer: EnginePeer) { - self.context = context - self.theme = theme - self.peer = peer - } - - static func ==(lhs: AvatarComponent, rhs: AvatarComponent) -> Bool { - if lhs.context !== rhs.context { - return false - } - if lhs.theme !== rhs.theme { - return false - } - if lhs.peer != rhs.peer { - return false - } - return true - } - - final class View: UIView { - private let avatarNode: AvatarNode - - private var component: AvatarComponent? - private weak var state: EmptyComponentState? - - override init(frame: CGRect) { - self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 42.0)) - - super.init(frame: frame) - - self.addSubnode(self.avatarNode) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func update(component: AvatarComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { - self.component = component - self.state = state - - self.avatarNode.frame = CGRect(origin: .zero, size: availableSize) - self.avatarNode.setPeer( - context: component.context, - theme: component.theme, - peer: component.peer, - synchronousLoad: true - ) - - return availableSize - } - } - - func makeView() -> View { - return View(frame: CGRect()) - } - - func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { - return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) - } -} - private struct GiftViewConfiguration { public static var defaultValue: GiftViewConfiguration { return GiftViewConfiguration(explorerUrl: "https://tonviewer.com") diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftWithdrawAlertController.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftWithdrawAlertController.swift index 1784432e..e9621f13 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftWithdrawAlertController.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftWithdrawAlertController.swift @@ -14,391 +14,222 @@ import Markdown import GiftItemComponent import StarsAvatarComponent import PasswordSetupUI -import OwnershipTransferController import PresentationDataUtils +import AlertComponent +import AlertTransferHeaderComponent +import AlertInputFieldComponent -private final class GiftWithdrawAlertContentNode: AlertContentNode { - private let context: AccountContext - private let strings: PresentationStrings - private var presentationTheme: PresentationTheme - private let title: String - private let text: String - private let gift: StarGift.UniqueGift - - private let titleNode: ASTextNode - private let giftView = ComponentView() - private let textNode: ASTextNode - private let arrowNode: ASImageNode - private let avatarView = ComponentView() - - private let actionNodesSeparator: ASDisplayNode - private let actionNodes: [TextAlertContentActionNode] - private let actionVerticalSeparators: [ASDisplayNode] - - private var validLayout: CGSize? - - override var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled - } - - init( - context: AccountContext, - theme: AlertControllerTheme, - ptheme: PresentationTheme, - strings: PresentationStrings, - gift: StarGift.UniqueGift, - title: String, - text: String, - actions: [TextAlertAction] - ) { - self.context = context - self.strings = strings - self.presentationTheme = ptheme - self.title = title - self.text = text - self.gift = gift - - self.titleNode = ASTextNode() - self.titleNode.maximumNumberOfLines = 0 - - self.textNode = ASTextNode() - self.textNode.maximumNumberOfLines = 0 - - self.arrowNode = ASImageNode() - self.arrowNode.displaysAsynchronously = false - self.arrowNode.displayWithoutProcessing = true - - self.actionNodesSeparator = ASDisplayNode() - self.actionNodesSeparator.isLayerBacked = true - - self.actionNodes = actions.map { action -> TextAlertContentActionNode in - return TextAlertContentActionNode(theme: theme, action: action) - } - - var actionVerticalSeparators: [ASDisplayNode] = [] - if actions.count > 1 { - for _ in 0 ..< actions.count - 1 { - let separatorNode = ASDisplayNode() - separatorNode.isLayerBacked = true - actionVerticalSeparators.append(separatorNode) - } - } - self.actionVerticalSeparators = actionVerticalSeparators - - super.init() - - self.addSubnode(self.titleNode) - self.addSubnode(self.textNode) - self.addSubnode(self.arrowNode) - - self.addSubnode(self.actionNodesSeparator) - - for actionNode in self.actionNodes { - self.addSubnode(actionNode) - } - - for separatorNode in self.actionVerticalSeparators { - self.addSubnode(separatorNode) - } - - self.updateTheme(theme) - } - - override func updateTheme(_ theme: AlertControllerTheme) { - self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.semibold(17.0), textColor: theme.primaryColor) - self.textNode.attributedText = parseMarkdownIntoAttributedString(self.text, attributes: MarkdownAttributes( - body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor), - bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: theme.primaryColor), - link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor), - linkAttribute: { url in - return ("URL", url) - } - ), textAlignment: .center) - self.arrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Peer Info/AlertArrow"), color: theme.secondaryColor) - - self.actionNodesSeparator.backgroundColor = theme.separatorColor - for actionNode in self.actionNodes { - actionNode.updateTheme(theme) - } - for separatorNode in self.actionVerticalSeparators { - separatorNode.backgroundColor = theme.separatorColor - } - - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) - } - } - - override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - var size = size - size.width = min(size.width, 270.0) - - self.validLayout = size - - var origin: CGPoint = CGPoint(x: 0.0, y: 20.0) - - let avatarSize = CGSize(width: 60.0, height: 60.0) - let giftFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) - 44.0, y: origin.y), size: avatarSize) - - let _ = self.giftView.update( - transition: .immediate, - component: AnyComponent( - GiftItemComponent( - context: self.context, - theme: self.presentationTheme, - strings: self.strings, - peer: nil, - subject: .uniqueGift(gift: self.gift, price: nil), - mode: .thumbnail - ) - ), - environment: {}, - containerSize: avatarSize - ) - if let view = self.giftView.view { - if view.superview == nil { - self.view.addSubview(view) - } - view.frame = giftFrame - } - - if let arrowImage = self.arrowNode.image { - let arrowFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - arrowImage.size.width) / 2.0), y: origin.y + floorToScreenPixels((avatarSize.height - arrowImage.size.height) / 2.0)), size: arrowImage.size) - transition.updateFrame(node: self.arrowNode, frame: arrowFrame) - } - - let avatarFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) + 44.0, y: origin.y), size: avatarSize) - let _ = self.avatarView.update( - transition: .immediate, - component: AnyComponent( - StarsAvatarComponent( - context: self.context, - theme: self.presentationTheme, - peer: .transactionPeer(.fragment), - photo: nil, - media: [], - gift: nil, - backgroundColor: .clear, - size: avatarSize - ) - ), - environment: {}, - containerSize: avatarSize - ) - if let view = self.avatarView.view { - if view.superview == nil { - self.view.addSubview(view) - } - view.frame = avatarFrame - } - - origin.y += avatarSize.height + 17.0 - - let titleSize = self.titleNode.measure(CGSize(width: size.width - 32.0, height: size.height)) - transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize)) - origin.y += titleSize.height + 5.0 - - let textSize = self.textNode.measure(CGSize(width: size.width - 32.0, height: size.height)) - transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize)) - origin.y += textSize.height + 10.0 - - let actionButtonHeight: CGFloat = 44.0 - var minActionsWidth: CGFloat = 0.0 - let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) - let actionTitleInsets: CGFloat = 8.0 - - var effectiveActionLayout = TextAlertContentActionLayout.vertical - if !"".isEmpty { - // Silence the warning - effectiveActionLayout = .horizontal - } - for actionNode in self.actionNodes { - let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) - minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) - } - - let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0) - - let contentWidth = max(size.width, minActionsWidth) - - let actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) - - let resultSize = CGSize(width: contentWidth, height: avatarSize.height + titleSize.height + textSize.height + actionsHeight + 24.0 + insets.top + insets.bottom) - transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - - var actionOffset: CGFloat = 0.0 - let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) - var separatorIndex = -1 - var nodeIndex = 0 - for actionNode in self.actionNodes { - if separatorIndex >= 0 { - let separatorNode = self.actionVerticalSeparators[separatorIndex] - switch effectiveActionLayout { - case .horizontal: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) - case .vertical: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - } - } - separatorIndex += 1 - - let currentActionWidth: CGFloat - switch effectiveActionLayout { - case .horizontal: - if nodeIndex == self.actionNodes.count - 1 { - currentActionWidth = resultSize.width - actionOffset - } else { - currentActionWidth = actionWidth - } - case .vertical: - currentActionWidth = resultSize.width - } - - let actionNodeFrame: CGRect - switch effectiveActionLayout { - case .horizontal: - actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += currentActionWidth - case .vertical: - actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += actionButtonHeight - } - - transition.updateFrame(node: actionNode, frame: actionNodeFrame) - - nodeIndex += 1 - } - - return resultSize - } -} - -public func giftWithdrawAlertController(context: AccountContext, gift: StarGift.UniqueGift, commit: @escaping () -> Void) -> AlertController { +public func giftWithdrawAlertController( + context: AccountContext, + gift: StarGift.UniqueGift, + commit: @escaping () -> Void +) -> ViewController { let presentationData = context.sharedContext.currentPresentationData.with { $0 } let strings = presentationData.strings + + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "header", + component: AnyComponent( + AlertTransferHeaderComponent( + fromComponent: AnyComponentWithIdentity(id: "gift", component: AnyComponent( + GiftItemComponent( + context: context, + theme: presentationData.theme, + strings: strings, + peer: nil, + subject: .uniqueGift(gift: gift, price: nil), + mode: .thumbnail + ) + )), + toComponent: AnyComponentWithIdentity(id: "fragment", component: AnyComponent( + StarsAvatarComponent( + context: context, + theme: presentationData.theme, + peer: .transactionPeer(.fragment), + photo: nil, + media: [], + gift: nil, + backgroundColor: .clear, + size: CGSize(width: 60.0, height: 60.0) + ) + )), + type: .transfer + ) + ) + )) - let title = strings.Gift_Withdraw_Title - let text = strings.Gift_Withdraw_Text("\(gift.title) #\(presentationStringsFormattedNumber(gift.number, presentationData.dateTimeFormat.groupingSeparator))").string - let buttonText = strings.Gift_Withdraw_Proceed - - var dismissImpl: ((Bool) -> Void)? - let actions: [TextAlertAction] = [TextAlertAction(type: .defaultAction, title: buttonText, action: { - dismissImpl?(true) - commit() - }), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - dismissImpl?(true) - })] - - let contentNode = GiftWithdrawAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: strings, gift: gift, title: title, text: text, actions: actions) - - let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode) - dismissImpl = { [weak controller] animated in - if animated { - controller?.dismissAnimated() - } else { - controller?.dismiss() - } - } - return controller + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: strings.Gift_Withdraw_Title) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(strings.Gift_Withdraw_Text("\(gift.title) #\(presentationStringsFormattedNumber(gift.number, presentationData.dateTimeFormat.groupingSeparator))").string)) + ) + )) + + let alertController = AlertScreen( + context: context, + configuration: AlertScreen.Configuration(actionAlignment: .vertical), + content: content, + actions: [ + .init(title: strings.Gift_Withdraw_Proceed, type: .default, action: { + commit() + }), + .init(title: strings.Common_Cancel) + ] + ) + return alertController } -public func confirmGiftWithdrawalController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, reference: StarGiftReference, present: @escaping (ViewController, Any?) -> Void, completion: @escaping (String) -> Void) -> ViewController { - let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } +public func confirmGiftWithdrawalController( + context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, + reference: StarGiftReference, + present: @escaping (ViewController, Any?) -> Void, + completion: @escaping (String) -> Void +) -> ViewController { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let strings = presentationData.strings + + let inputState = AlertInputFieldComponent.ExternalState() + + let doneIsEnabled: Signal = inputState.valueSignal + |> map { value in + return !value.isEmpty + } + + let doneInProgressPromise = ValuePromise(false) + + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: strings.Gift_Withdraw_EnterPassword_Title) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(strings.Gift_Withdraw_EnterPassword_Text)) + ) + )) + + var applyImpl: (() -> Void)? + content.append(AnyComponentWithIdentity( + id: "input", + component: AnyComponent( + AlertInputFieldComponent( + context: context, + placeholder: strings.Channel_OwnershipTransfer_PasswordPlaceholder, + isSecureTextEntry: true, + isInitiallyFocused: true, + externalState: inputState, + returnKeyAction: { + applyImpl?() + } + ) + ) + )) + + var effectiveUpdatedPresentationData: (PresentationData, Signal) + if let updatedPresentationData { + effectiveUpdatedPresentationData = updatedPresentationData + } else { + effectiveUpdatedPresentationData = (presentationData, context.sharedContext.presentationData) + } var dismissImpl: (() -> Void)? - var proceedImpl: (() -> Void)? - - let disposable = MetaDisposable() - - let contentNode = ChannelOwnershipTransferAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, title: presentationData.strings.Gift_Withdraw_EnterPassword_Title, text: presentationData.strings.Gift_Withdraw_EnterPassword_Text, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - dismissImpl?() - }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Gift_Withdraw_EnterPassword_Done, action: { - proceedImpl?() - })]) - - contentNode.complete = { - proceedImpl?() - } - - let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode) - let presentationDataDisposable = (updatedPresentationData?.signal ?? context.sharedContext.presentationData).start(next: { [weak controller, weak contentNode] presentationData in - controller?.theme = AlertControllerTheme(presentationData: presentationData) - contentNode?.theme = presentationData.theme - }) - controller.dismissed = { _ in - presentationDataDisposable.dispose() - disposable.dispose() - } - dismissImpl = { [weak controller, weak contentNode] in - contentNode?.dismissInput() - controller?.dismissAnimated() - } - proceedImpl = { [weak contentNode] in - guard let contentNode = contentNode else { - return - } - contentNode.updateIsChecking(true) - - let signal = context.engine.payments.requestStarGiftWithdrawalUrl(reference: reference, password: contentNode.password) - disposable.set((signal |> deliverOnMainQueue).start(next: { url in + let alertController = AlertScreen( + configuration: AlertScreen.Configuration(allowInputInset: true), + content: content, + actions: [ + .init(title: strings.Common_Cancel), + .init(title: strings.Gift_Withdraw_EnterPassword_Done, type: .default, action: { + applyImpl?() + }, autoDismiss: false, isEnabled: doneIsEnabled, progress: doneInProgressPromise.get()) + ], + updatedPresentationData: effectiveUpdatedPresentationData + ) + applyImpl = { + doneInProgressPromise.set(true) + + let _ = (context.engine.payments.requestStarGiftWithdrawalUrl(reference: reference, password: inputState.value) + |> deliverOnMainQueue).start(next: { url in dismissImpl?() completion(url) - }, error: { [weak contentNode] error in + }, error: { error in var errorTextAndActions: (String, [TextAlertAction])? switch error { - case .invalidPassword: - contentNode?.animateError() - case .limitExceeded: - errorTextAndActions = (presentationData.strings.TwoStepAuth_FloodError, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) - default: - errorTextAndActions = (presentationData.strings.Login_UnknownError, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) + case .invalidPassword: + inputState.animateError() + case .limitExceeded: + errorTextAndActions = (strings.TwoStepAuth_FloodError, [TextAlertAction(type: .defaultAction, title: strings.Common_OK, action: {})]) + default: + errorTextAndActions = (strings.Login_UnknownError, [TextAlertAction(type: .defaultAction, title: strings.Common_OK, action: {})]) } - contentNode?.updateIsChecking(false) - + doneInProgressPromise.set(false) + if let (text, actions) = errorTextAndActions { dismissImpl?() present(textAlertController(context: context, title: nil, text: text, actions: actions), nil) } - })) + }) } - - return controller + dismissImpl = { [weak alertController] in + alertController?.dismiss(completion: nil) + } + return alertController } -public func giftWithdrawalController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, reference: StarGiftReference, initialError: RequestStarGiftWithdrawalError, present: @escaping (ViewController, Any?) -> Void, completion: @escaping (String) -> Void) -> ViewController { +public func giftWithdrawalController( + context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, + reference: StarGiftReference, + initialError: RequestStarGiftWithdrawalError, + present: @escaping (ViewController, Any?) -> Void, + completion: @escaping (String) -> Void +) -> ViewController { let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } - let theme = AlertControllerTheme(presentationData: presentationData) + let strings = presentationData.strings - var title: NSAttributedString? = NSAttributedString(string: presentationData.strings.Gift_Withdraw_SecurityCheck, font: Font.semibold(presentationData.listsFontSize.itemListBaseFontSize), textColor: theme.primaryColor, paragraphAlignment: .center) + var title: String? = strings.Gift_Withdraw_SecurityCheck + var text = strings.Gift_Withdraw_SecurityRequirements - var text = presentationData.strings.Gift_Withdraw_SecurityRequirements - let textFontSize = presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0 - - var actions: [TextAlertAction] = [] + var actions: [AlertScreen.Action] = [ + .init(title: strings.Common_OK, type: .default) + ] switch initialError { case .requestPassword: return confirmGiftWithdrawalController(context: context, updatedPresentationData: updatedPresentationData, reference: reference, present: present, completion: completion) case .twoStepAuthTooFresh, .authSessionTooFresh: text = text + presentationData.strings.Gift_Withdraw_ComeBackLater - actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})] case .twoStepAuthMissing: - actions = [TextAlertAction(type: .genericAction, title: presentationData.strings.Gift_Withdraw_SetupTwoStepAuth, action: { - let controller = SetupTwoStepVerificationController(context: context, initialState: .automatic, stateUpdated: { update, shouldDismiss, controller in - if shouldDismiss { - controller.dismiss() - } - }) - present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) - }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {})] + actions = [ + .init(title: strings.Gift_Withdraw_SetupTwoStepAuth, type: .default, action: { + let controller = SetupTwoStepVerificationController(context: context, initialState: .automatic, stateUpdated: { update, shouldDismiss, controller in + if shouldDismiss { + controller.dismiss() + } + }) + present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + }), + .init(title: strings.Common_Cancel) + ] default: title = nil - text = presentationData.strings.Login_UnknownError - actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})] + text = strings.Login_UnknownError } - let body = MarkdownAttributeSet(font: Font.regular(textFontSize), textColor: theme.primaryColor) - let bold = MarkdownAttributeSet(font: Font.semibold(textFontSize), textColor: theme.primaryColor) - let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .center) - - return richTextAlertController(context: context, title: title, text: attributedText, actions: actions) + return AlertScreen( + context: context, + configuration: AlertScreen.Configuration(actionAlignment: .vertical), + title: title, + text: text, + actions: actions + ) } diff --git a/submodules/TelegramUI/Components/Gifts/PeerTableCellComponent/BUILD b/submodules/TelegramUI/Components/Gifts/PeerTableCellComponent/BUILD new file mode 100644 index 00000000..363e3ee0 --- /dev/null +++ b/submodules/TelegramUI/Components/Gifts/PeerTableCellComponent/BUILD @@ -0,0 +1,26 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "PeerTableCellComponent", + module_name = "PeerTableCellComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/Postbox", + "//submodules/ComponentFlow", + "//submodules/TelegramPresentationData", + "//submodules/TelegramCore", + "//submodules/Components/MultilineTextComponent", + "//submodules/AccountContext", + "//submodules/AvatarNode", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/Gifts/PeerTableCellComponent/Sources/PeerTableCellComponent.swift b/submodules/TelegramUI/Components/Gifts/PeerTableCellComponent/Sources/PeerTableCellComponent.swift new file mode 100644 index 00000000..6ff8042f --- /dev/null +++ b/submodules/TelegramUI/Components/Gifts/PeerTableCellComponent/Sources/PeerTableCellComponent.swift @@ -0,0 +1,123 @@ +import Foundation +import UIKit +import ComponentFlow +import Display +import TelegramCore +import TelegramPresentationData +import MultilineTextComponent +import AvatarNode +import AccountContext + +public final class PeerTableCellComponent: Component { + let context: AccountContext + let theme: PresentationTheme + let strings: PresentationStrings + let peer: EnginePeer? + + public init( + context: AccountContext, + theme: PresentationTheme, + strings: PresentationStrings, + peer: EnginePeer? + ) { + self.context = context + self.theme = theme + self.strings = strings + self.peer = peer + } + + public static func ==(lhs: PeerTableCellComponent, rhs: PeerTableCellComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.theme !== rhs.theme { + return false + } + if lhs.strings !== rhs.strings { + return false + } + if lhs.peer != rhs.peer { + return false + } + return true + } + + public final class View: UIView { + private let avatarNode: AvatarNode + private let text = ComponentView() + + private var component: PeerTableCellComponent? + private weak var state: EmptyComponentState? + + override init(frame: CGRect) { + self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 12.0)) + + super.init(frame: frame) + + self.addSubnode(self.avatarNode) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(component: PeerTableCellComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + self.state = state + + let avatarSize = CGSize(width: 22.0, height: 22.0) + let spacing: CGFloat = 6.0 + + var peerName: String + let avatarOverride: AvatarNodeImageOverride? + if let peerValue = component.peer { + peerName = peerValue.compactDisplayTitle + if peerName.count > 40 { + peerName = "\(peerName.prefix(40))…" + } + avatarOverride = nil + } else { + peerName = component.strings.Gift_View_HiddenName + avatarOverride = .anonymousSavedMessagesIcon(isColored: true) + } + + let avatarNaturalSize = CGSize(width: 40.0, height: 40.0) + self.avatarNode.setPeer(context: component.context, theme: component.theme, peer: component.peer, overrideImage: avatarOverride) + self.avatarNode.bounds = CGRect(origin: .zero, size: avatarNaturalSize) + + let textSize = self.text.update( + transition: .immediate, + component: AnyComponent( + MultilineTextComponent( + text: .plain(NSAttributedString(string: peerName, font: Font.regular(15.0), textColor: component.peer != nil ? component.theme.list.itemAccentColor : component.theme.list.itemPrimaryTextColor, paragraphAlignment: .left)) + ) + ), + environment: {}, + containerSize: CGSize(width: availableSize.width - avatarSize.width - spacing, height: availableSize.height) + ) + + let size = CGSize(width: avatarSize.width + textSize.width + spacing, height: textSize.height) + + let avatarFrame = CGRect(origin: CGPoint(x: 0.0, y: floorToScreenPixels((size.height - avatarSize.height) / 2.0)), size: avatarSize) + self.avatarNode.frame = avatarFrame + + if let view = self.text.view { + if view.superview == nil { + self.addSubview(view) + } + let textFrame = CGRect(origin: CGPoint(x: avatarSize.width + spacing, y: floorToScreenPixels((size.height - textSize.height) / 2.0)), size: textSize) + transition.setFrame(view: view, frame: textFrame) + } + + return size + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/Gifts/TableComponent/BUILD b/submodules/TelegramUI/Components/Gifts/TableComponent/BUILD new file mode 100644 index 00000000..54dc0f8a --- /dev/null +++ b/submodules/TelegramUI/Components/Gifts/TableComponent/BUILD @@ -0,0 +1,23 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "TableComponent", + module_name = "TableComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/Postbox", + "//submodules/ComponentFlow", + "//submodules/TelegramPresentationData", + "//submodules/Components/MultilineTextComponent", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/Gifts/TableComponent/Sources/TableComponent.swift b/submodules/TelegramUI/Components/Gifts/TableComponent/Sources/TableComponent.swift new file mode 100644 index 00000000..af189d8f --- /dev/null +++ b/submodules/TelegramUI/Components/Gifts/TableComponent/Sources/TableComponent.swift @@ -0,0 +1,375 @@ +import Foundation +import UIKit +import ComponentFlow +import Display +import TelegramPresentationData +import MultilineTextComponent + +public final class TableComponent: CombinedComponent { + public class Item: Equatable { + public enum TitleFont { + case regular + case bold + } + + public let id: AnyHashable + public let title: String? + public let titleFont: TitleFont + public let hasBackground: Bool + public let component: AnyComponent + public let insets: UIEdgeInsets? + + public init( + id: IdType, + title: String?, + titleFont: TitleFont = .regular, + hasBackground: Bool = false, + component: AnyComponent, + insets: UIEdgeInsets? = nil + ) { + self.id = AnyHashable(id) + self.title = title + self.titleFont = titleFont + self.hasBackground = hasBackground + self.component = component + self.insets = insets + } + + public static func == (lhs: Item, rhs: Item) -> Bool { + if lhs.id != rhs.id { + return false + } + if lhs.title != rhs.title { + return false + } + if lhs.titleFont != rhs.titleFont { + return false + } + if lhs.hasBackground != rhs.hasBackground { + return false + } + if lhs.component != rhs.component { + return false + } + if lhs.insets != rhs.insets { + return false + } + return true + } + } + + private let theme: PresentationTheme + private let items: [Item] + private let semiTransparent: Bool + + public init(theme: PresentationTheme, items: [Item], semiTransparent: Bool = false) { + self.theme = theme + self.items = items + self.semiTransparent = semiTransparent + } + + public static func ==(lhs: TableComponent, rhs: TableComponent) -> Bool { + if lhs.theme !== rhs.theme { + return false + } + if lhs.items != rhs.items { + return false + } + if lhs.semiTransparent != rhs.semiTransparent { + return false + } + return true + } + + public final class State: ComponentState { + var cachedLastBackgroundImage: (UIImage, PresentationTheme)? + var cachedLeftColumnImage: (UIImage, PresentationTheme)? + var cachedBorderImage: (UIImage, PresentationTheme)? + } + + public func makeState() -> State { + return State() + } + + public static var body: Body { + let leftColumnBackground = Child(Image.self) + let lastBackground = Child(Image.self) + let verticalBorder = Child(Rectangle.self) + let titleChildren = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self) + let valueChildren = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self) + let borderChildren = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self) + let outerBorder = Child(Image.self) + + return { context in + let verticalPadding: CGFloat = 11.0 + let horizontalPadding: CGFloat = 12.0 + let borderWidth: CGFloat = 1.0 + + let borderColor: UIColor + let secondaryBackgroundColor: UIColor + if context.component.semiTransparent { + borderColor = context.component.theme.actionSheet.primaryTextColor.withMultipliedAlpha(0.1) + secondaryBackgroundColor = context.component.theme.actionSheet.primaryTextColor.withMultipliedAlpha(0.05) + } else { + let backgroundColor = context.component.theme.actionSheet.opaqueItemBackgroundColor + borderColor = backgroundColor.mixedWith(context.component.theme.list.itemBlocksSeparatorColor, alpha: 0.6) + secondaryBackgroundColor = context.component.theme.overallDarkAppearance ? context.component.theme.list.itemModalBlocksBackgroundColor : context.component.theme.list.itemInputField.backgroundColor + } + + var leftColumnWidth: CGFloat = 0.0 + + var updatedTitleChildren: [Int: _UpdatedChildComponent] = [:] + var updatedValueChildren: [(_UpdatedChildComponent, UIEdgeInsets)] = [] + var updatedBorderChildren: [_UpdatedChildComponent] = [] + + var i = 0 + for item in context.component.items { + guard let title = item.title else { + i += 1 + continue + } + let titleChild = titleChildren[item.id].update( + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: title, font: item.titleFont == .bold ? Font.semibold(15.0) : Font.regular(15.0), textColor: context.component.theme.list.itemPrimaryTextColor)) + )), + availableSize: context.availableSize, + transition: context.transition + ) + updatedTitleChildren[i] = titleChild + + if titleChild.size.width > leftColumnWidth { + leftColumnWidth = titleChild.size.width + } + i += 1 + } + + leftColumnWidth = max(100.0, leftColumnWidth + horizontalPadding * 2.0) + let rightColumnWidth = context.availableSize.width - leftColumnWidth + + i = 0 + var rowHeights: [Int: CGFloat] = [:] + var totalHeight: CGFloat = 0.0 + var innerTotalHeight: CGFloat = 0.0 + var innerTotalOffset: CGFloat = 0.0 + var hasRowBackground = false + var rowBackgroundIsLast = false + var hasStraightSide = false + + for item in context.component.items { + let insets: UIEdgeInsets + if let customInsets = item.insets { + insets = customInsets + } else { + insets = UIEdgeInsets(top: 0.0, left: horizontalPadding, bottom: 0.0, right: horizontalPadding) + } + + var titleHeight: CGFloat = 0.0 + if let titleChild = updatedTitleChildren[i] { + titleHeight = titleChild.size.height + } + + let availableValueWidth: CGFloat + if titleHeight > 0.0 { + availableValueWidth = rightColumnWidth + } else { + availableValueWidth = context.availableSize.width + } + + let valueChild = valueChildren[item.id].update( + component: item.component, + availableSize: CGSize(width: availableValueWidth - insets.left - insets.right, height: context.availableSize.height), + transition: context.transition + ) + updatedValueChildren.append((valueChild, insets)) + + let rowHeight = max(40.0, max(titleHeight, valueChild.size.height) + verticalPadding * 2.0) + rowHeights[i] = rowHeight + totalHeight += rowHeight + if titleHeight > 0.0 { + innerTotalHeight += rowHeight + } else if i == 0 { + innerTotalOffset += rowHeight + } + + if i < context.component.items.count - 1 { + let borderChild = borderChildren[item.id].update( + component: AnyComponent(Rectangle(color: borderColor)), + availableSize: CGSize(width: context.availableSize.width, height: borderWidth), + transition: context.transition + ) + updatedBorderChildren.append(borderChild) + } + + if item.hasBackground { + if i != 0 { + rowBackgroundIsLast = true + } + hasRowBackground = true + } + if item.title == nil { + if i != 0 { + rowBackgroundIsLast = true + } + hasStraightSide = true + } + + i += 1 + } + + let borderRadius: CGFloat = 14.0 + + if hasRowBackground { + let lastBackgroundImage: UIImage + if let (currentImage, theme) = context.state.cachedLastBackgroundImage, theme === context.component.theme { + lastBackgroundImage = currentImage + } else { + lastBackgroundImage = generateImage(CGSize(width: borderRadius * 2.0 + 4.0, height: borderRadius * 2.0 + 4.0), rotatedContext: { size, context in + let bounds = CGRect(origin: .zero, size: CGSize(width: size.width, height: size.height + borderRadius)) + context.clear(bounds) + + let path = CGPath(roundedRect: bounds.insetBy(dx: borderWidth / 2.0, dy: borderWidth / 2.0).insetBy(dx: 0.0, dy: rowBackgroundIsLast ? -borderRadius * 2.0 : 0.0), cornerWidth: borderRadius, cornerHeight: borderRadius, transform: nil) + context.setFillColor(secondaryBackgroundColor.cgColor) + context.addPath(path) + context.fillPath() + })!.stretchableImage(withLeftCapWidth: Int(borderRadius), topCapHeight: Int(borderRadius)) + context.state.cachedLastBackgroundImage = (lastBackgroundImage, context.component.theme) + } + + let lastRowHeight: CGFloat + let position: CGFloat + if !rowBackgroundIsLast { + lastRowHeight = rowHeights[0] ?? 0 + position = lastRowHeight / 2.0 + } else { + lastRowHeight = rowHeights[i - 1] ?? 0 + position = totalHeight - lastRowHeight / 2.0 + } + let lastBackground = lastBackground.update( + component: Image(image: lastBackgroundImage), + availableSize: CGSize(width: context.availableSize.width, height: lastRowHeight), + transition: context.transition + ) + + context.add( + lastBackground + .position(CGPoint(x: context.availableSize.width / 2.0, y: position)) + ) + } + + let leftColumnImage: UIImage + if let (currentImage, theme) = context.state.cachedLeftColumnImage, theme === context.component.theme { + leftColumnImage = currentImage + } else { + leftColumnImage = generateImage(CGSize(width: borderRadius * 2.0 + 4.0, height: borderRadius * 2.0 + 4.0), rotatedContext: { size, context in + var bounds = CGRect(origin: .zero, size: CGSize(width: size.width + borderRadius, height: size.height)) + context.clear(bounds) + + var offset: CGFloat = 0.0 + if hasStraightSide { + offset = rowBackgroundIsLast ? 0.0 : -borderRadius + + bounds.origin.y += offset + bounds.size.height += borderRadius + } + + let path = CGPath(roundedRect: bounds.insetBy(dx: borderWidth / 2.0, dy: borderWidth / 2.0), cornerWidth: borderRadius, cornerHeight: borderRadius, transform: nil) + context.setFillColor(secondaryBackgroundColor.cgColor) + context.addPath(path) + context.fillPath() + })!.stretchableImage(withLeftCapWidth: Int(borderRadius), topCapHeight: Int(borderRadius)) + context.state.cachedLeftColumnImage = (leftColumnImage, context.component.theme) + } + + let leftColumnBackground = leftColumnBackground.update( + component: Image(image: leftColumnImage), + availableSize: CGSize(width: leftColumnWidth, height: innerTotalHeight), + transition: context.transition + ) + context.add(leftColumnBackground + .position(CGPoint(x: leftColumnWidth / 2.0, y: innerTotalOffset + innerTotalHeight / 2.0)) + ) + + let borderImage: UIImage + if let (currentImage, theme) = context.state.cachedBorderImage, theme === context.component.theme { + borderImage = currentImage + } else { + borderImage = generateImage(CGSize(width: borderRadius * 2.0 + 4.0, height: borderRadius * 2.0 + 4.0), rotatedContext: { size, context in + let bounds = CGRect(origin: .zero, size: size) + context.clear(bounds) + + let path = CGPath(roundedRect: bounds.insetBy(dx: borderWidth / 2.0, dy: borderWidth / 2.0), cornerWidth: borderRadius, cornerHeight: borderRadius, transform: nil) + context.setBlendMode(.clear) + context.addPath(path) + context.fillPath() + + context.setBlendMode(.normal) + context.setStrokeColor(borderColor.cgColor) + context.setLineWidth(borderWidth) + context.addPath(path) + context.strokePath() + })!.stretchableImage(withLeftCapWidth: Int(borderRadius), topCapHeight: Int(borderRadius)) + context.state.cachedBorderImage = (borderImage, context.component.theme) + } + + let outerBorder = outerBorder.update( + component: Image(image: borderImage), + availableSize: CGSize(width: context.availableSize.width, height: totalHeight), + transition: context.transition + ) + context.add(outerBorder + .position(CGPoint(x: context.availableSize.width / 2.0, y: totalHeight / 2.0)) + ) + + let verticalBorder = verticalBorder.update( + component: Rectangle(color: borderColor), + availableSize: CGSize(width: borderWidth, height: innerTotalHeight), + transition: context.transition + ) + context.add( + verticalBorder + .position(CGPoint(x: leftColumnWidth - borderWidth / 2.0, y: innerTotalOffset + innerTotalHeight / 2.0)) + ) + + i = 0 + var originY: CGFloat = 0.0 + for (valueChild, valueInsets) in updatedValueChildren { + let rowHeight = rowHeights[i] ?? 0.0 + + let valueFrame: CGRect + if let titleChild = updatedTitleChildren[i] { + let titleFrame = CGRect(origin: CGPoint(x: horizontalPadding, y: originY + verticalPadding), size: titleChild.size) + context.add(titleChild + .position(titleFrame.center) + ) + valueFrame = CGRect(origin: CGPoint(x: leftColumnWidth + valueInsets.left, y: originY + verticalPadding), size: valueChild.size) + } else { + if hasRowBackground && rowBackgroundIsLast { + valueFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((context.availableSize.width - valueChild.size.width) / 2.0), y: originY + verticalPadding), size: valueChild.size) + } else { + valueFrame = CGRect(origin: CGPoint(x: horizontalPadding, y: originY + verticalPadding), size: valueChild.size) + } + } + + context.add(valueChild + .position(valueFrame.center) + .appear(.default(alpha: true)) + .disappear(.default(alpha: true)) + ) + + if i < updatedBorderChildren.count { + let borderChild = updatedBorderChildren[i] + context.add(borderChild + .position(CGPoint(x: context.availableSize.width / 2.0, y: originY + rowHeight - borderWidth / 2.0)) + .appear(.default(alpha: true)) + .disappear(.default(alpha: true)) + ) + } + + originY += rowHeight + i += 1 + } + + return CGSize(width: context.availableSize.width, height: totalHeight) + } + } +} diff --git a/submodules/TelegramUI/Components/GlassBackgroundComponent/BUILD b/submodules/TelegramUI/Components/GlassBackgroundComponent/BUILD index 216445b2..03e0c56d 100644 --- a/submodules/TelegramUI/Components/GlassBackgroundComponent/BUILD +++ b/submodules/TelegramUI/Components/GlassBackgroundComponent/BUILD @@ -15,6 +15,7 @@ swift_library( "//submodules/Components/ComponentDisplayAdapters", "//submodules/UIKitRuntimeUtils", "//submodules/AppBundle", + "//submodules/TelegramUI/Components/MeshTransform", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift b/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift index 4c1852d9..ce271441 100644 --- a/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift +++ b/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/GlassBackgroundComponent.swift @@ -292,16 +292,18 @@ public class GlassBackgroundView: UIView { public let isDark: Bool public let tintColor: TintColor public let isInteractive: Bool + public let isVisible: Bool - init(shape: Shape, isDark: Bool, tintColor: TintColor, isInteractive: Bool) { + init(shape: Shape, isDark: Bool, tintColor: TintColor, isInteractive: Bool, isVisible: Bool) { self.shape = shape self.isDark = isDark self.tintColor = tintColor self.isInteractive = isInteractive + self.isVisible = isVisible } } - private let backgroundNode: NavigationBackgroundNode? + private let legacyView: LegacyGlassView? private let nativeView: UIVisualEffectView? private let nativeViewClippingContext: ClippingShapeContext? @@ -330,7 +332,7 @@ public class GlassBackgroundView: UIView { public override init(frame: CGRect) { if #available(iOS 26.0, *), !GlassBackgroundView.useCustomGlassImpl { - self.backgroundNode = nil + self.legacyView = nil let glassEffect = UIGlassEffect(style: .regular) glassEffect.isInteractive = false @@ -346,8 +348,7 @@ public class GlassBackgroundView: UIView { self.foregroundView = nil self.shadowView = nil } else { - let backgroundNode = NavigationBackgroundNode(color: .black, enableBlur: true, customBlurRadius: 8.0) - self.backgroundNode = backgroundNode + self.legacyView = LegacyGlassView(frame: CGRect()) self.nativeView = nil self.nativeViewClippingContext = nil self.nativeParamsView = nil @@ -375,8 +376,8 @@ public class GlassBackgroundView: UIView { if let nativeParamsView = self.nativeParamsView { self.addSubview(nativeParamsView) } - if let backgroundNode = self.backgroundNode { - self.addSubview(backgroundNode.view) + if let legacyView = self.legacyView { + self.addSubview(legacyView) } if let foregroundView = self.foregroundView { self.addSubview(foregroundView) @@ -390,6 +391,15 @@ public class GlassBackgroundView: UIView { } override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if !self.isUserInteractionEnabled { + return nil + } + if self.isHidden { + return nil + } + if self.alpha == 0.0 { + return nil + } if let nativeView = self.nativeView { if let result = nativeView.hitTest(self.convert(point, to: nativeView), with: event) { return result @@ -402,11 +412,11 @@ public class GlassBackgroundView: UIView { return nil } - public func update(size: CGSize, cornerRadius: CGFloat, isDark: Bool, tintColor: TintColor, isInteractive: Bool = false, transition: ComponentTransition) { - self.update(size: size, shape: .roundedRect(cornerRadius: cornerRadius), isDark: isDark, tintColor: tintColor, isInteractive: isInteractive, transition: transition) + public func update(size: CGSize, cornerRadius: CGFloat, isDark: Bool, tintColor: TintColor, isInteractive: Bool = false, isVisible: Bool = true, transition: ComponentTransition) { + self.update(size: size, shape: .roundedRect(cornerRadius: cornerRadius), isDark: isDark, tintColor: tintColor, isInteractive: isInteractive, isVisible: isVisible, transition: transition) } - public func update(size: CGSize, shape: Shape, isDark: Bool, tintColor: TintColor, isInteractive: Bool = false, transition: ComponentTransition) { + public func update(size: CGSize, shape: Shape, isDark: Bool, tintColor: TintColor, isInteractive: Bool = false, isVisible: Bool = true, transition: ComponentTransition) { if let nativeView = self.nativeView, let nativeViewClippingContext = self.nativeViewClippingContext, (nativeView.bounds.size != size || nativeViewClippingContext.shape != shape) { nativeViewClippingContext.update(shape: shape, size: size, transition: transition) @@ -416,15 +426,15 @@ public class GlassBackgroundView: UIView { let nativeFrame = CGRect(origin: CGPoint(), size: size) transition.setFrame(view: nativeView, frame: nativeFrame) } + nativeView.overrideUserInterfaceStyle = isDark ? .dark : .light } - if let backgroundNode = self.backgroundNode { - backgroundNode.updateColor(color: .clear, forceKeepBlur: tintColor.color.alpha != 1.0, transition: transition.containedViewLayoutTransition) - + if let legacyView = self.legacyView { switch shape { case let .roundedRect(cornerRadius): - backgroundNode.update(size: size, cornerRadius: cornerRadius, transition: transition.containedViewLayoutTransition) + legacyView.update(size: size, cornerRadius: cornerRadius, transition: transition) } - transition.setFrame(view: backgroundNode.view, frame: CGRect(origin: CGPoint(), size: size)) + transition.setFrame(view: legacyView, frame: CGRect(origin: CGPoint(), size: size)) + transition.setAlpha(view: legacyView, alpha: isVisible ? 1.0 : 0.0) } let shadowInset: CGFloat = 32.0 @@ -468,7 +478,7 @@ public class GlassBackgroundView: UIView { innerBackgroundView.removeFromSuperview() } - let params = Params(shape: shape, isDark: isDark, tintColor: tintColor, isInteractive: isInteractive) + let params = Params(shape: shape, isDark: isDark, tintColor: tintColor, isInteractive: isInteractive, isVisible: isVisible) if self.params != params { self.params = params @@ -491,36 +501,73 @@ public class GlassBackgroundView: UIView { context.setBlendMode(.copy) context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowInset + shadowInnerInset, y: shadowInset + shadowInnerInset), size: CGSize(width: size.width - shadowInset * 2.0 - shadowInnerInset * 2.0, height: size.height - shadowInset * 2.0 - shadowInnerInset * 2.0))) })?.stretchableImage(withLeftCapWidth: Int(shadowInset + outerCornerRadius), topCapHeight: Int(shadowInset + outerCornerRadius)) + transition.setAlpha(view: shadowView, alpha: isVisible ? 1.0 : 0.0) } if let foregroundView = self.foregroundView { - foregroundView.image = GlassBackgroundView.generateLegacyGlassImage(size: CGSize(width: outerCornerRadius * 2.0, height: outerCornerRadius * 2.0), inset: shadowInset, isDark: isDark, fillColor: tintColor.color) + let fillColor: UIColor + switch tintColor.kind { + case .panel: + if isDark { + fillColor = UIColor(white: 1.0, alpha: 1.0).mixedWith(.black, alpha: 1.0 - 0.11).withAlphaComponent(0.85) + } else { + fillColor = UIColor(white: 1.0, alpha: 0.7) + } + case .custom: + fillColor = tintColor.color + } + foregroundView.image = GlassBackgroundView.generateLegacyGlassImage(size: CGSize(width: outerCornerRadius * 2.0, height: outerCornerRadius * 2.0), inset: shadowInset, isDark: isDark, fillColor: fillColor) + transition.setAlpha(view: foregroundView, alpha: isVisible ? 1.0 : 0.0) } else { if let nativeParamsView = self.nativeParamsView, let nativeView = self.nativeView { if #available(iOS 26.0, *) { - let glassEffect = UIGlassEffect(style: .regular) - switch tintColor.kind { - case .panel: - glassEffect.tintColor = UIColor(white: isDark ? 0.0 : 1.0, alpha: 0.1) - case .custom: - glassEffect.tintColor = tintColor.color - } - glassEffect.isInteractive = params.isInteractive + var glassEffect: UIGlassEffect? - if transition.animation.isImmediate { - nativeView.effect = glassEffect + if isVisible { + let glassEffectValue = UIGlassEffect(style: .regular) + switch tintColor.kind { + case .panel: + if isDark { + glassEffectValue.tintColor = UIColor(white: 1.0, alpha: 0.025) + } else { + glassEffectValue.tintColor = UIColor(white: 1.0, alpha: 0.1) + } + case .custom: + glassEffectValue.tintColor = tintColor.color + } + glassEffectValue.isInteractive = params.isInteractive + glassEffect = glassEffectValue + } + + if glassEffect == nil { + if nativeView.effect is UIGlassEffect { + if transition.animation.isImmediate { + nativeView.effect = nil + } else { + UIView.animate { + nativeView.effect = nil + } + } + } } else { - UIView.animate(withDuration: 0.2, animations: { + if transition.animation.isImmediate { nativeView.effect = glassEffect - }) + } else { + if let glassEffect, let currentEffect = nativeView.effect as? UIGlassEffect, currentEffect.tintColor == glassEffect.tintColor, currentEffect.isInteractive == glassEffect.isInteractive { + } else { + UIView.animate { + nativeView.effect = glassEffect + } + } + } } if isDark { nativeParamsView.lumaMin = 0.0 nativeParamsView.lumaMax = 0.15 } else { - nativeParamsView.lumaMin = 0.6 - nativeParamsView.lumaMax = 0.61 + nativeParamsView.lumaMin = 0.8 + nativeParamsView.lumaMax = 0.801 } } } @@ -537,6 +584,10 @@ public class GlassBackgroundView: UIView { } transition.setFrame(view: self.contentContainer, frame: CGRect(origin: CGPoint(), size: size)) } + + override public func willMove(toWindow newWindow: UIWindow?) { + super.willMove(toWindow: newWindow) + } } public final class GlassBackgroundContainerView: UIView { @@ -595,9 +646,25 @@ public final class GlassBackgroundContainerView: UIView { } override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if self.alpha.isZero { + return nil + } + if self.isHidden { + return nil + } + if !self.isUserInteractionEnabled { + return nil + } + for view in self.contentView.subviews.reversed() { + if let result = view.hitTest(self.convert(point, to: view), with: event), result.isUserInteractionEnabled { + return result + } + } + guard let result = self.contentView.hitTest(point, with: event) else { return nil } + return result } @@ -609,8 +676,8 @@ public final class GlassBackgroundContainerView: UIView { nativeParamsView.lumaMin = 0.0 nativeParamsView.lumaMax = 0.15 } else { - nativeParamsView.lumaMin = 0.6 - nativeParamsView.lumaMax = 0.61 + nativeParamsView.lumaMin = 0.8 + nativeParamsView.lumaMax = 0.801 } transition.setFrame(view: nativeView, frame: CGRect(origin: CGPoint(), size: size)) @@ -618,6 +685,10 @@ public final class GlassBackgroundContainerView: UIView { transition.setFrame(view: legacyView, frame: CGRect(origin: CGPoint(), size: size)) } } + + override public func willMove(toWindow newWindow: UIWindow?) { + super.willMove(toWindow: newWindow) + } } private extension CGContext { @@ -736,8 +807,8 @@ public extension GlassBackgroundView { } } - addShadow(context, true, CGPoint(), 10.0, 0.0, UIColor(white: 0.0, alpha: 0.06), .normal) - addShadow(context, true, CGPoint(), 20.0, 0.0, UIColor(white: 0.0, alpha: 0.06), .normal) + addShadow(context, true, CGPoint(), 30.0, 0.0, UIColor(white: 0.0, alpha: 0.045), .normal) + addShadow(context, true, CGPoint(), 20.0, 0.0, UIColor(white: 0.0, alpha: 0.01), .normal) var a: CGFloat = 0.0 var b: CGFloat = 0.0 @@ -745,7 +816,7 @@ public extension GlassBackgroundView { fillColor.getHue(nil, saturation: &s, brightness: &b, alpha: &a) let innerImage: UIImage - if size == CGSize(width: 40.0 + inset * 2.0, height: 40.0 + inset * 2.0), b >= 0.2 { + /*if size == CGSize(width: 40.0 + inset * 2.0, height: 40.0 + inset * 2.0), b >= 0.2 { innerImage = UIGraphicsImageRenderer(size: size).image { ctx in let context = ctx.cgContext @@ -798,10 +869,78 @@ public extension GlassBackgroundView { addShadow(context, false, CGPoint(x: 0.64, y: 0.64), 1.2, 0.0, UIColor(white: 1.0, alpha: edgeAlpha), .normal) } } - } + }*/ - context.addEllipse(in: CGRect(origin: CGPoint(x: inset, y: inset), size: innerSize)) - context.clip() + innerImage = UIGraphicsImageRenderer(size: size).image { ctx in + let context = ctx.cgContext + + context.setFillColor(fillColor.cgColor) + var ellipseRect = CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset) + context.fillEllipse(in: ellipseRect) + + let lineWidth: CGFloat = isDark ? 0.8 : 0.8 + let strokeColor: UIColor + let blendMode: CGBlendMode + let baseAlpha: CGFloat = isDark ? 0.3 : 0.6 + + if s == 0.0 && abs(a - 0.7) < 0.1 && !isDark { + blendMode = .normal + strokeColor = UIColor(white: 1.0, alpha: baseAlpha) + } else if s <= 0.3 && !isDark { + blendMode = .normal + strokeColor = UIColor(white: 1.0, alpha: 0.7 * baseAlpha) + } else if b >= 0.2 { + let maxAlpha: CGFloat = isDark ? 0.7 : 0.8 + blendMode = .overlay + strokeColor = UIColor(white: 1.0, alpha: max(0.5, min(1.0, maxAlpha * s)) * baseAlpha) + } else { + blendMode = .normal + strokeColor = UIColor(white: 1.0, alpha: 0.5 * baseAlpha) + } + + context.setStrokeColor(strokeColor.cgColor) + ellipseRect = CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset) + context.addEllipse(in: ellipseRect) + context.clip() + + ellipseRect = CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset).insetBy(dx: lineWidth * 0.5, dy: lineWidth * 0.5) + + context.setBlendMode(blendMode) + + let radius = ellipseRect.height * 0.5 + let smallerRadius = radius - lineWidth * 1.33 + context.move(to: CGPoint(x: ellipseRect.minX, y: ellipseRect.minY + radius)) + // Top-left corner (regular radius) + context.addArc(tangent1End: CGPoint(x: ellipseRect.minX, y: ellipseRect.minY), tangent2End: CGPoint(x: ellipseRect.minX + radius, y: ellipseRect.minY), radius: radius) + context.addLine(to: CGPoint(x: ellipseRect.maxX - smallerRadius, y: ellipseRect.minY)) + // Top-right corner (smaller radius) + context.addArc(tangent1End: CGPoint(x: ellipseRect.maxX, y: ellipseRect.minY), tangent2End: CGPoint(x: ellipseRect.maxX, y: ellipseRect.minY + smallerRadius), radius: smallerRadius) + context.addLine(to: CGPoint(x: ellipseRect.maxX, y: ellipseRect.maxY - radius)) + // Bottom-right corner (regular radius) + context.addArc(tangent1End: CGPoint(x: ellipseRect.maxX, y: ellipseRect.maxY), tangent2End: CGPoint(x: ellipseRect.maxX - radius, y: ellipseRect.maxY), radius: radius) + context.addLine(to: CGPoint(x: ellipseRect.minX + smallerRadius, y: ellipseRect.maxY)) + // Bottom-left corner (smaller radius) + context.addArc(tangent1End: CGPoint(x: ellipseRect.minX, y: ellipseRect.maxY), tangent2End: CGPoint(x: ellipseRect.minX, y: ellipseRect.maxY - smallerRadius), radius: smallerRadius) + context.closePath() + context.strokePath() + + context.resetClip() + context.setBlendMode(.normal) + + //let image = makeInnerShadowPillImageExact(size: CGSize(width: size.width - inset * 2.0, height: size.height - inset * 2.0), scale: UIScreenScale, glossColor: UIColor(white: 1.0, alpha: 1.0), borderWidth: 1.33) + /*let image = generateCircleImage(diameter: size.width - inset * 2.0, lineWidth: 0.5, color: UIColor(white: 1.0, alpha: 1.0))! + + if s == 0.0 && abs(a - 0.7) < 0.1 && !isDark { + image.draw(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset), blendMode: .normal, alpha: 1.0) + } else if s <= 0.3 && !isDark { + image.draw(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset), blendMode: .normal, alpha: 0.7) + } else if b >= 0.2 { + let maxAlpha: CGFloat = isDark ? 0.7 : 0.8 + image.draw(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset), blendMode: .overlay, alpha: max(0.5, min(1.0, maxAlpha * s))) + } else { + image.draw(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: inset, dy: inset), blendMode: .normal, alpha: 0.5) + }*/ + } innerImage.draw(in: CGRect(origin: CGPoint(), size: size)) }.stretchableImage(withLeftCapWidth: Int(size.width * 0.5), topCapHeight: Int(size.height * 0.5)) } @@ -892,12 +1031,20 @@ public final class GlassBackgroundComponent: Component { private let cornerRadius: CGFloat private let isDark: Bool private let tintColor: GlassBackgroundView.TintColor + private let isInteractive: Bool - public init(size: CGSize, cornerRadius: CGFloat, isDark: Bool, tintColor: GlassBackgroundView.TintColor) { + public init( + size: CGSize, + cornerRadius: CGFloat, + isDark: Bool, + tintColor: GlassBackgroundView.TintColor, + isInteractive: Bool = false + ) { self.size = size self.cornerRadius = cornerRadius self.isDark = isDark self.tintColor = tintColor + self.isInteractive = isInteractive } public static func == (lhs: GlassBackgroundComponent, rhs: GlassBackgroundComponent) -> Bool { @@ -913,12 +1060,15 @@ public final class GlassBackgroundComponent: Component { if lhs.tintColor != rhs.tintColor { return false } + if lhs.isInteractive != rhs.isInteractive { + return false + } return true } public final class View: GlassBackgroundView { func update(component: GlassBackgroundComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { - self.update(size: component.size, cornerRadius: component.cornerRadius, isDark: component.isDark, tintColor: component.tintColor, transition: transition) + self.update(size: component.size, cornerRadius: component.cornerRadius, isDark: component.isDark, tintColor: component.tintColor, isInteractive: component.isInteractive, transition: transition) return component.size } diff --git a/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/LegacyGlassView.swift b/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/LegacyGlassView.swift new file mode 100644 index 00000000..b688f816 --- /dev/null +++ b/submodules/TelegramUI/Components/GlassBackgroundComponent/Sources/LegacyGlassView.swift @@ -0,0 +1,178 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import MeshTransform + +private let backdropLayerClass: NSObject? = { + let name = ("CA" as NSString).appendingFormat("BackdropLayer") + if let cls = NSClassFromString(name as String) as AnyObject as? NSObject { + return cls + } + return nil +}() + +@inline(__always) +private func getMethod(object: NSObject, selector: String) -> T? { + guard let method = object.method(for: NSSelectorFromString(selector)) else { + return nil + } + return unsafeBitCast(method, to: T.self) +} + +private var cachedBackdropLayerAllocMethod: (@convention(c) (AnyObject, Selector) -> NSObject?, Selector)? +private func invokeBackdropLayerCreateMethod() -> NSObject? { + guard let backdropLayerClass = backdropLayerClass else { + return nil + } + if let cachedBackdropLayerAllocMethod { + return cachedBackdropLayerAllocMethod.0(backdropLayerClass, cachedBackdropLayerAllocMethod.1) + } else { + let method: (@convention(c) (AnyObject, Selector) -> NSObject?)? = getMethod(object: backdropLayerClass, selector: "alloc") + if let method { + let selector = NSSelectorFromString("alloc") + cachedBackdropLayerAllocMethod = (method, selector) + return method(backdropLayerClass, selector) + } else { + return nil + } + } +} + +private var cachedBackdropLayerInitMethod: (@convention(c) (NSObject, Selector) -> NSObject?, Selector)? +private func invokeBackdropLayerInitMethod(object: NSObject) -> NSObject? { + if let cachedBackdropLayerInitMethod { + return cachedBackdropLayerInitMethod.0(object, cachedBackdropLayerInitMethod.1) + } else { + let method: (@convention(c) (AnyObject, Selector) -> NSObject?)? = getMethod(object: object, selector: "init") + if let method { + let selector = NSSelectorFromString("init") + cachedBackdropLayerInitMethod = (method, selector) + return method(object, selector) + } else { + return nil + } + } +} + +private var cachedBackdropLayerSetScaleMethod: (@convention(c) (NSObject, Selector, Double) -> Void, Selector)? +private func invokeBackdropLayerSetScaleMethod(object: NSObject, scale: Double) { + if let cachedBackdropLayerSetScaleMethod { + cachedBackdropLayerSetScaleMethod.0(object, cachedBackdropLayerSetScaleMethod.1, scale) + } else { + let method: (@convention(c) (AnyObject, Selector, Double) -> Void)? = getMethod(object: object, selector: "setScale:") + if let method { + let selector = NSSelectorFromString("setScale:") + cachedBackdropLayerSetScaleMethod = (method, selector) + return method(object, selector, scale) + } + } +} + +private final class BackdropLayerDelegate: NSObject, CALayerDelegate { + func action(for layer: CALayer, forKey event: String) -> CAAction? { + return nullAction + } +} + +final class LegacyGlassView: UIView { + private struct Params: Equatable { + let size: CGSize + let cornerRadius: CGFloat + + init(size: CGSize, cornerRadius: CGFloat) { + self.size = size + self.cornerRadius = cornerRadius + } + } + + private var params: Params? + + private let backdropLayer: CALayer? + private let backdropLayerDelegate: BackdropLayerDelegate + + override init(frame: CGRect) { + self.backdropLayerDelegate = BackdropLayerDelegate() + self.backdropLayer = invokeBackdropLayerCreateMethod().flatMap(invokeBackdropLayerInitMethod) as? CALayer + + super.init(frame: frame) + + self.layer.cornerCurve = .circular + self.clipsToBounds = true + + if let backdropLayer = self.backdropLayer { + self.layer.addSublayer(backdropLayer) + backdropLayer.delegate = self.backdropLayerDelegate + + let blur: CGFloat + let scale: CGFloat + + blur = 2.0 + scale = 1.0 + + invokeBackdropLayerSetScaleMethod(object: backdropLayer, scale: scale) + backdropLayer.rasterizationScale = scale + + if let blurFilter = CALayer.blur() { + blurFilter.setValue(blur as NSNumber, forKey: "inputRadius") + backdropLayer.filters = [blurFilter] + } + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(size: CGSize, cornerRadius: CGFloat, transition: ComponentTransition) { + let params = Params(size: size, cornerRadius: cornerRadius) + if self.params == params { + return + } + self.params = params + + guard let backdropLayer = self.backdropLayer else { + return + } + + transition.setCornerRadius(layer: self.layer, cornerRadius: cornerRadius) + transition.setFrame(layer: backdropLayer, frame: CGRect(origin: CGPoint(), size: size)) + + if !"".isEmpty { + let size = CGSize(width: max(1.0, size.width), height: max(1.0, size.height)) + let cornerRadius = min(min(size.width, size.height) * 0.5, cornerRadius) + let displacementMagnitudePoints: CGFloat = 20.0 + let displacementMagnitudeU = displacementMagnitudePoints / size.width + let displacementMagnitudeV = displacementMagnitudePoints / size.height + let outerEdgeDistance = 2.0 + + if let displacementMap = generateDisplacementMap(size: size, cornerRadius: cornerRadius, edgeDistance: min(12.0, cornerRadius), scale: 1.0) { + let meshTransform = generateGlassMeshFromDisplacementMap( + size: size, + cornerRadius: cornerRadius, + displacementMap: displacementMap, + displacementMagnitudeU: displacementMagnitudeU, + displacementMagnitudeV: displacementMagnitudeV, + cornerResolution: 12, + outerEdgeDistance: outerEdgeDistance, + bezier: DisplacementBezier( + x1: 0.816137566137566, + y1: 0.20502645502645533, + x2: 0.5806878306878306, + y2: 0.873015873015873 + ) + ).mesh.makeValue() + + if let meshTransform { + if !transition.animation.isImmediate, let previousTransform = backdropLayer.value(forKey: "meshTransform") as? NSObject { + backdropLayer.removeAnimation(forKey: "meshTransform") + backdropLayer.setValue(meshTransform, forKey: "meshTransform") + transition.animateMeshTransform(layer: backdropLayer, from: previousTransform, to: meshTransform) + } else { + backdropLayer.setValue(meshTransform, forKey: "meshTransform") + } + } + } + } + } +} diff --git a/submodules/TelegramUI/Components/GlassBarButtonComponent/Sources/GlassBarButtonComponent.swift b/submodules/TelegramUI/Components/GlassBarButtonComponent/Sources/GlassBarButtonComponent.swift index 6b012d10..daa97d1b 100644 --- a/submodules/TelegramUI/Components/GlassBarButtonComponent/Sources/GlassBarButtonComponent.swift +++ b/submodules/TelegramUI/Components/GlassBarButtonComponent/Sources/GlassBarButtonComponent.swift @@ -13,29 +13,35 @@ public final class GlassBarButtonComponent: Component { } public let size: CGSize? - public let backgroundColor: UIColor + public let backgroundColor: UIColor? public let isDark: Bool public let state: DisplayState? public let isEnabled: Bool + public let animateScale: Bool public let component: AnyComponentWithIdentity public let action: ((UIView) -> Void)? + public let tag: AnyObject? public init( size: CGSize?, - backgroundColor: UIColor, + backgroundColor: UIColor?, isDark: Bool, state: DisplayState? = nil, isEnabled: Bool = true, + animateScale: Bool = true, component: AnyComponentWithIdentity, - action: ((UIView) -> Void)? + action: ((UIView) -> Void)?, + tag: AnyObject? = nil ) { self.size = size self.backgroundColor = backgroundColor self.isDark = isDark self.state = state self.isEnabled = isEnabled + self.animateScale = animateScale self.component = component self.action = action + self.tag = tag } public static func ==(lhs: GlassBarButtonComponent, rhs: GlassBarButtonComponent) -> Bool { @@ -54,14 +60,30 @@ public final class GlassBarButtonComponent: Component { if lhs.isEnabled != rhs.isEnabled { return false } + if lhs.animateScale != rhs.animateScale { + return false + } if lhs.component != rhs.component { return false } + if lhs.tag !== rhs.tag { + return false + } return true } - public final class View: HighlightTrackingButton { - private let containerView: UIView + public final class View: UIView, ComponentTaggedView { + public func matches(tag: Any) -> Bool { + if let component = self.component, let componentTag = component.tag { + let tag = tag as AnyObject + if componentTag === tag { + return true + } + } + return false + } + + private let containerView: HighlightTrackingButton private let genericContainerView: UIView private let genericBackgroundView: SimpleGlassView private let glassContainerView: UIView @@ -71,26 +93,24 @@ public final class GlassBarButtonComponent: Component { private var component: GlassBarButtonComponent? public override init(frame: CGRect) { - self.containerView = UIView() + self.containerView = HighlightTrackingButton() self.genericContainerView = UIView() self.genericBackgroundView = SimpleGlassView() self.glassContainerView = UIView() super.init(frame: frame) - self.containerView.isUserInteractionEnabled = false self.containerView.layer.rasterizationScale = UIScreenScale - self.addSubview(self.containerView) - self.containerView.addSubview(self.genericContainerView) - self.containerView.addSubview(self.glassContainerView) + self.addSubview(self.genericContainerView) + self.addSubview(self.glassContainerView) self.genericContainerView.addSubview(self.genericBackgroundView) - self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside) + self.containerView.addTarget(self, action: #selector(self.pressed), for: .touchUpInside) - self.highligthedChanged = { [weak self] highlighted in - guard let self else { + self.containerView.highligthedChanged = { [weak self] highlighted in + guard let self, let component = self.component, component.animateScale else { return } if highlighted { @@ -112,11 +132,30 @@ public final class GlassBarButtonComponent: Component { action(self) } + @objc private func onTapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + guard let component = self.component, let action = component.action else { + return + } + action(self) + } + } + + override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + guard let result = super.hitTest(point, with: event) else { + return nil + } + if result === self.glassContainerView || result === self.genericContainerView { + return self.containerView + } + return result + } + func update(component: GlassBarButtonComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { let previousComponent = self.component self.component = component - self.isEnabled = component.isEnabled + self.containerView.isEnabled = component.isEnabled var componentView: ComponentView var animateAppearance = false @@ -159,6 +198,7 @@ public final class GlassBarButtonComponent: Component { let componentFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((containerSize.width - componentSize.width) / 2.0), y: floorToScreenPixels((containerSize.height - componentSize.height) / 2.0)), size: componentSize) if let view = componentView.view { if view.superview == nil { + view.isUserInteractionEnabled = false self.containerView.addSubview(view) if animateAppearance { transition.animateScale(view: view, from: 0.01, to: 1.0) @@ -168,7 +208,11 @@ public final class GlassBarButtonComponent: Component { componentTransition.setFrame(view: view, frame: componentFrame) } - let effectiveState = component.state ?? .glass + let effectiveState: DisplayState = component.state ?? .glass + /*if "".isEmpty { + effectiveState = .glass + }*/ + var genericAlpha: CGFloat = 1.0 var glassAlpha: CGFloat = 1.0 switch effectiveState { @@ -181,8 +225,9 @@ public final class GlassBarButtonComponent: Component { } let cornerRadius = containerSize.height * 0.5 - self.genericBackgroundView.update(size: containerSize, cornerRadius: cornerRadius, isDark: component.isDark, tintColor: .init(kind: .custom, color: component.backgroundColor), transition: transition) - + if let backgroundColor = component.backgroundColor { + self.genericBackgroundView.update(size: containerSize, cornerRadius: cornerRadius, isDark: component.isDark, tintColor: .init(kind: .custom, color: backgroundColor), transition: transition) + } let bounds = CGRect(origin: .zero, size: containerSize) transition.setFrame(view: self.containerView, frame: bounds) @@ -196,7 +241,7 @@ public final class GlassBarButtonComponent: Component { transition.setFrame(view: self.genericBackgroundView, frame: bounds) - if glassAlpha == 1.0 { + if glassAlpha == 1.0, let backgroundColor = component.backgroundColor { let glassBackgroundView: GlassBackgroundView var glassBackgroundTransition = transition if let current = self.glassBackgroundView { @@ -204,13 +249,31 @@ public final class GlassBarButtonComponent: Component { } else { glassBackgroundTransition = .immediate glassBackgroundView = GlassBackgroundView() - glassBackgroundView.isUserInteractionEnabled = false self.glassContainerView.addSubview(glassBackgroundView) self.glassBackgroundView = glassBackgroundView + glassBackgroundView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.onTapGesture(_:)))) + transition.animateAlpha(view: glassBackgroundView, from: 0.0, to: 1.0) } - glassBackgroundView.update(size: containerSize, cornerRadius: cornerRadius, isDark: component.isDark, tintColor: .init(kind: effectiveState == .tintedGlass ? .custom : .panel , color: component.backgroundColor.withMultipliedAlpha(effectiveState == .tintedGlass ? 1.0 : 0.7)), transition: glassBackgroundTransition) + glassBackgroundView.update(size: containerSize, cornerRadius: cornerRadius, isDark: component.isDark, tintColor: .init(kind: effectiveState == .tintedGlass ? .custom : .panel , color: backgroundColor.withMultipliedAlpha(effectiveState == .tintedGlass ? 1.0 : 0.7)), isInteractive: true, transition: glassBackgroundTransition) + glassBackgroundTransition.setFrame(view: glassBackgroundView, frame: bounds) + } else if case .glass = component.state { + let glassBackgroundView: GlassBackgroundView + var glassBackgroundTransition = transition + if let current = self.glassBackgroundView { + glassBackgroundView = current + } else { + glassBackgroundTransition = .immediate + glassBackgroundView = GlassBackgroundView() + self.glassContainerView.addSubview(glassBackgroundView) + self.glassBackgroundView = glassBackgroundView + + glassBackgroundView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.onTapGesture(_:)))) + + transition.animateAlpha(view: glassBackgroundView, from: 0.0, to: 1.0) + } + glassBackgroundView.update(size: containerSize, cornerRadius: cornerRadius, isDark: component.isDark, tintColor: .init(kind: .panel, color: UIColor(white: component.isDark ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: glassBackgroundTransition) glassBackgroundTransition.setFrame(view: glassBackgroundView, frame: bounds) } else if let glassBackgroundView = self.glassBackgroundView { self.glassBackgroundView = nil @@ -219,6 +282,14 @@ public final class GlassBarButtonComponent: Component { }) } + if let glassBackgroundView = self.glassBackgroundView { + if self.containerView.superview !== glassBackgroundView.contentView { + glassBackgroundView.contentView.addSubview(self.containerView) + } + } else if self.containerView.superview !== self { + self.addSubview(self.containerView) + } + return containerSize } } @@ -310,7 +381,7 @@ public final class BarComponentHostNode: ASDisplayNode { transition.animateScale(view: view, from: 0.01, to: 1.0) } } - view.frame = CGRect(origin: CGPoint(x: 0.0, y: 3.0), size: self.size) + view.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: self.size) } } } diff --git a/submodules/TelegramUI/Components/GlassControls/Sources/GlassControlGroup.swift b/submodules/TelegramUI/Components/GlassControls/Sources/GlassControlGroup.swift index f4d3c15e..6b5638b3 100644 --- a/submodules/TelegramUI/Components/GlassControls/Sources/GlassControlGroup.swift +++ b/submodules/TelegramUI/Components/GlassControls/Sources/GlassControlGroup.swift @@ -148,13 +148,13 @@ public final class GlassControlGroupComponent: Component { )) case let .text(string): content = AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: string, font: Font.semibold(15.0), textColor: component.background == .activeTint ? component.theme.list.itemCheckColors.foregroundColor : component.theme.chat.inputPanel.panelControlColor)) + text: .plain(NSAttributedString(string: string, font: Font.medium(17.0), textColor: component.background == .activeTint ? component.theme.list.itemCheckColors.foregroundColor : component.theme.chat.inputPanel.panelControlColor)) )) itemInsets.left = 10.0 itemInsets.right = itemInsets.left } - var minItemWidth: CGFloat = 40.0 + var minItemWidth: CGFloat = availableSize.height if component.items.count == 1 { minItemWidth = max(minItemWidth, component.minWidth) } @@ -163,7 +163,7 @@ public final class GlassControlGroupComponent: Component { transition: itemTransition, component: AnyComponent(PlainButtonComponent( content: content, - minSize: CGSize(width: minItemWidth, height: 40.0), + minSize: CGSize(width: minItemWidth, height: availableSize.height), contentInsets: itemInsets, action: { item.action?() @@ -186,8 +186,9 @@ public final class GlassControlGroupComponent: Component { itemComponentView.alpha = 0.0 } itemTransition.setFrame(view: itemComponentView, frame: itemFrame) + alphaTransition.setAlpha(view: itemComponentView, alpha: item.action != nil ? 1.0 : 0.5) + if animateIn { - alphaTransition.setAlpha(view: itemComponentView, alpha: 1.0) alphaTransition.animateBlur(layer: itemComponentView.layer, fromRadius: 8.0, toRadius: 0.0) } } @@ -220,6 +221,7 @@ public final class GlassControlGroupComponent: Component { tintColor = .init(kind: .panel, color: component.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7), innerColor: component.theme.list.itemCheckColors.fillColor) } transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: size)) + isInteractive = true self.backgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: component.theme.overallDarkAppearance, tintColor: tintColor, isInteractive: isInteractive, transition: transition) return size diff --git a/submodules/TelegramUI/Components/GlassControls/Sources/GlassControlPanel.swift b/submodules/TelegramUI/Components/GlassControls/Sources/GlassControlPanel.swift index 471f2c47..09861db2 100644 --- a/submodules/TelegramUI/Components/GlassControls/Sources/GlassControlPanel.swift +++ b/submodules/TelegramUI/Components/GlassControls/Sources/GlassControlPanel.swift @@ -9,10 +9,12 @@ public final class GlassControlPanelComponent: Component { public final class Item: Equatable { public let items: [GlassControlGroupComponent.Item] public let background: GlassControlGroupComponent.Background + public let keepWide: Bool - public init(items: [GlassControlGroupComponent.Item], background: GlassControlGroupComponent.Background) { + public init(items: [GlassControlGroupComponent.Item], background: GlassControlGroupComponent.Background, keepWide: Bool = false) { self.items = items self.background = background + self.keepWide = keepWide } public static func ==(lhs: Item, rhs: Item) -> Bool { @@ -22,6 +24,9 @@ public final class GlassControlPanelComponent: Component { if lhs.background != rhs.background { return false } + if lhs.keepWide != rhs.keepWide { + return false + } return true } } @@ -30,17 +35,20 @@ public final class GlassControlPanelComponent: Component { public let leftItem: Item? public let rightItem: Item? public let centralItem: Item? + public let centerAlignmentIfPossible: Bool public init( theme: PresentationTheme, leftItem: Item?, centralItem: Item?, - rightItem: Item? + rightItem: Item?, + centerAlignmentIfPossible: Bool = false ) { self.theme = theme self.leftItem = leftItem self.centralItem = centralItem self.rightItem = rightItem + self.centerAlignmentIfPossible = centerAlignmentIfPossible } public static func ==(lhs: GlassControlPanelComponent, rhs: GlassControlPanelComponent) -> Bool { @@ -56,6 +64,9 @@ public final class GlassControlPanelComponent: Component { if lhs.rightItem != rhs.rightItem { return false } + if lhs.centerAlignmentIfPossible != rhs.centerAlignmentIfPossible { + return false + } return true } @@ -118,7 +129,7 @@ public final class GlassControlPanelComponent: Component { theme: component.theme, background: leftItem.background, items: leftItem.items, - minWidth: 40.0 + minWidth: availableSize.height )), environment: {}, containerSize: CGSize(width: availableSize.width, height: availableSize.height) @@ -167,7 +178,7 @@ public final class GlassControlPanelComponent: Component { theme: component.theme, background: rightItem.background, items: rightItem.items, - minWidth: 40.0 + minWidth: availableSize.height )), environment: {}, containerSize: CGSize(width: availableSize.width, height: availableSize.height) @@ -233,12 +244,19 @@ public final class GlassControlPanelComponent: Component { theme: component.theme, background: centralItem.background, items: centralItem.items, - minWidth: 165.0 + minWidth: centralItem.keepWide ? 165.0 : availableSize.height )), environment: {}, containerSize: maxCentralItemSize ) - let centralItemFrameValue = CGRect(origin: CGPoint(x: centralLeftInset + floor((availableSize.width - centralLeftInset - centralRightInset - centralItemSize.width) * 0.5), y: 0.0), size: centralItemSize) + var centralItemFrameValue = CGRect(origin: CGPoint(x: centralLeftInset + floor((availableSize.width - centralLeftInset - centralRightInset - centralItemSize.width) * 0.5), y: 0.0), size: centralItemSize) + if component.centerAlignmentIfPossible { + let maxInset = max(centralLeftInset, centralRightInset) + if availableSize.width - maxInset * 2.0 > centralItemSize.width { + centralItemFrameValue.origin.x = maxInset + floor((availableSize.width - maxInset * 2.0 - centralItemSize.width) * 0.5) + } + } + if let centralItemComponentView = centralItemComponent.view { var animateIn = false if centralItemComponentView.superview == nil { diff --git a/submodules/TelegramUI/Components/GlobalControlPanelsContext/BUILD b/submodules/TelegramUI/Components/GlobalControlPanelsContext/BUILD new file mode 100644 index 00000000..a32c2d38 --- /dev/null +++ b/submodules/TelegramUI/Components/GlobalControlPanelsContext/BUILD @@ -0,0 +1,24 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "GlobalControlPanelsContext", + module_name = "GlobalControlPanelsContext", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/TelegramCore", + "//submodules/AccountContext", + "//submodules/TelegramUIPreferences", + "//submodules/TelegramCallsUI", + "//submodules/Display", + "//submodules/UndoUI", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/GlobalControlPanelsContext/Sources/GlobalControlPanelsContext.swift b/submodules/TelegramUI/Components/GlobalControlPanelsContext/Sources/GlobalControlPanelsContext.swift new file mode 100644 index 00000000..d42d3910 --- /dev/null +++ b/submodules/TelegramUI/Components/GlobalControlPanelsContext/Sources/GlobalControlPanelsContext.swift @@ -0,0 +1,698 @@ +import Foundation +import UIKit +import SwiftSignalKit +import TelegramCore +import AccountContext +import TelegramUIPreferences +import TelegramCallsUI +import Display +import UndoUI + +public final class GlobalControlPanelsContext { + public final class MediaPlayback: Equatable { + public let version: Int + public let item: SharedMediaPlaylistItem + public let previousItem: SharedMediaPlaylistItem? + public let nextItem: SharedMediaPlaylistItem? + public let playbackOrder: MusicPlaybackSettingsOrder + public let kind: MediaManagerPlayerType + public let playlistLocation: SharedMediaPlaylistLocation + public let account: Account + + public init(version: Int, item: SharedMediaPlaylistItem, previousItem: SharedMediaPlaylistItem?, nextItem: SharedMediaPlaylistItem?, playbackOrder: MusicPlaybackSettingsOrder, kind: MediaManagerPlayerType, playlistLocation: SharedMediaPlaylistLocation, account: Account) { + self.version = version + self.item = item + self.previousItem = previousItem + self.nextItem = nextItem + self.playbackOrder = playbackOrder + self.kind = kind + self.playlistLocation = playlistLocation + self.account = account + } + + public static func ==(lhs: MediaPlayback, rhs: MediaPlayback) -> Bool { + if lhs.version != rhs.version { + return false + } + return true + } + } + + public enum LiveLocationMode { + case all + case peer(EnginePeer.Id) + } + + public final class LiveLocation: Equatable { + public let mode: LiveLocationMode + public let peers: [EnginePeer] + public let messages: [EngineMessage.Id: EngineMessage] + public let canClose: Bool + public let version: Int + + public init(mode: LiveLocationMode, peers: [EnginePeer], messages: [EngineMessage.Id: EngineMessage], canClose: Bool, version: Int) { + self.mode = mode + self.peers = peers + self.messages = messages + self.canClose = canClose + self.version = version + } + + public static func ==(lhs: LiveLocation, rhs: LiveLocation) -> Bool { + if lhs.version != rhs.version { + return false + } + return true + } + } + + public enum ChatListNotice: Equatable { + case clearStorage(sizeFraction: Double) + case setupPassword + case premiumUpgrade(discount: Int32) + case premiumAnnualDiscount(discount: Int32) + case premiumRestore(discount: Int32) + case xmasPremiumGift + case setupBirthday + case birthdayPremiumGift(peers: [EnginePeer], birthdays: [EnginePeer.Id: TelegramBirthday]) + case reviewLogin(newSessionReview: NewSessionReview, totalCount: Int) + case premiumGrace + case starsSubscriptionLowBalance(amount: StarsAmount, peers: [EnginePeer]) + case setupPhoto(EnginePeer) + case accountFreeze + case link(id: String, url: String, title: ServerSuggestionInfo.Item.Text, subtitle: ServerSuggestionInfo.Item.Text) + } + + public final class GroupCall: Equatable { + public let peerId: EnginePeer.Id + public let isChannel: Bool + public let info: GroupCallInfo + public let topParticipants: [GroupCallParticipantsContext.Participant] + public let participantCount: Int + public let activeSpeakers: Set + public let groupCall: PresentationGroupCall? + + public init( + peerId: EnginePeer.Id, + isChannel: Bool, + info: GroupCallInfo, + topParticipants: [GroupCallParticipantsContext.Participant], + participantCount: Int, + activeSpeakers: Set, + groupCall: PresentationGroupCall? + ) { + self.peerId = peerId + self.isChannel = isChannel + self.info = info + self.topParticipants = topParticipants + self.participantCount = participantCount + self.activeSpeakers = activeSpeakers + self.groupCall = groupCall + } + + public static func ==(lhs: GroupCall, rhs: GroupCall) -> Bool { + if lhs.peerId != rhs.peerId { + return false + } + if lhs.isChannel != rhs.isChannel { + return false + } + if lhs.info != rhs.info { + return false + } + if lhs.topParticipants != rhs.topParticipants { + return false + } + if lhs.participantCount != rhs.participantCount { + return false + } + if lhs.activeSpeakers != rhs.activeSpeakers { + return false + } + if lhs.groupCall !== rhs.groupCall { + return false + } + return true + } + } + + public final class State { + public let mediaPlayback: MediaPlayback? + public let liveLocation: LiveLocation? + public let chatListNotice: ChatListNotice? + public let groupCall: GroupCall? + + public init( + mediaPlayback: MediaPlayback?, + liveLocation: LiveLocation?, + chatListNotice: ChatListNotice?, + groupCall: GroupCall? + ) { + self.mediaPlayback = mediaPlayback + self.liveLocation = liveLocation + self.chatListNotice = chatListNotice + self.groupCall = groupCall + } + } + + private final class Impl { + let queue: Queue + let context: AccountContext + + private(set) var stateValue: State + let statePipe = ValuePipe() + + private var nextVersion: Int = 0 + + var tempVoicePlaylistEnded: (() -> Void)? + var tempVoicePlaylistItemChanged: ((SharedMediaPlaylistItem?, SharedMediaPlaylistItem?) -> Void)? + var tempVoicePlaylistCurrentItem: SharedMediaPlaylistItem? + + var playlistStateAndType: (SharedMediaPlaylistItem, SharedMediaPlaylistItem?, SharedMediaPlaylistItem?, MusicPlaybackSettingsOrder, MediaManagerPlayerType, Account, SharedMediaPlaylistLocation, Int)? + var mediaStatusDisposable: Disposable? + + var liveLocationState: (mode: LiveLocationMode, peers: [EnginePeer], messages: [EngineMessage.Id: EngineMessage], canClose: Bool, version: Int)? + var liveLocationDisposable: Disposable? + + var chatListNotice: ChatListNotice? + var suggestedChatListNoticeDisposable: Disposable? + + var groupCall: GroupCall? + var currentGroupCallDisposable: Disposable? + + init(queue: Queue, context: AccountContext, mediaPlayback: Bool, liveLocationMode: LiveLocationMode?, groupCalls: EnginePeer.Id?, chatListNotices: Bool) { + self.queue = queue + self.context = context + + self.stateValue = State(mediaPlayback: nil, liveLocation: nil, chatListNotice: nil, groupCall: nil) + + if mediaPlayback { + self.mediaStatusDisposable = (context.sharedContext.mediaManager.globalMediaPlayerState + |> mapToSignal { playlistStateAndType -> Signal<(Account, SharedMediaPlayerItemPlaybackState, MediaManagerPlayerType)?, NoError> in + if let (account, state, type) = playlistStateAndType { + switch state { + case let .state(state): + return .single((account, state, type)) + case .loading: + return .single(nil) |> delay(0.2, queue: .mainQueue()) + } + } else { + return .single(nil) + } + } + |> deliverOnMainQueue).start(next: { [weak self] playlistStateAndType in + guard let strongSelf = self else { + return + } + if !arePlaylistItemsEqual(strongSelf.playlistStateAndType?.0, playlistStateAndType?.1.item) || + !arePlaylistItemsEqual(strongSelf.playlistStateAndType?.1, playlistStateAndType?.1.previousItem) || + !arePlaylistItemsEqual(strongSelf.playlistStateAndType?.2, playlistStateAndType?.1.nextItem) || + strongSelf.playlistStateAndType?.3 != playlistStateAndType?.1.order || strongSelf.playlistStateAndType?.4 != playlistStateAndType?.2 { + var previousVoiceItem: SharedMediaPlaylistItem? + if let playlistStateAndType = strongSelf.playlistStateAndType, playlistStateAndType.4 == .voice { + previousVoiceItem = playlistStateAndType.0 + } + + var updatedVoiceItem: SharedMediaPlaylistItem? + if let playlistStateAndType = playlistStateAndType, playlistStateAndType.2 == .voice { + updatedVoiceItem = playlistStateAndType.1.item + } + + strongSelf.tempVoicePlaylistCurrentItem = updatedVoiceItem + strongSelf.tempVoicePlaylistItemChanged?(previousVoiceItem, updatedVoiceItem) + if let playlistStateAndType = playlistStateAndType { + strongSelf.playlistStateAndType = (playlistStateAndType.1.item, playlistStateAndType.1.previousItem, playlistStateAndType.1.nextItem, playlistStateAndType.1.order, playlistStateAndType.2, playlistStateAndType.0, playlistStateAndType.1.playlistLocation, 0) + } else { + var voiceEnded = false + if strongSelf.playlistStateAndType?.4 == .voice { + voiceEnded = true + } + strongSelf.playlistStateAndType = nil + if voiceEnded { + strongSelf.tempVoicePlaylistEnded?() + } + } + strongSelf.playlistStateAndType?.7 = strongSelf.nextVersion + strongSelf.nextVersion += 1 + strongSelf.notifyStateUpdated() + } + }) + } + + if let liveLocationMode, let liveLocationManager = context.liveLocationManager { + let signal: Signal<([EnginePeer]?, [EngineMessage.Id: EngineMessage]?), NoError> + switch liveLocationMode { + case let .peer(peerId): + signal = combineLatest(liveLocationManager.summaryManager.peersBroadcastingTo(peerId: peerId), liveLocationManager.summaryManager.broadcastingToMessages()) + |> map { peersAndMessages, outgoingMessages in + var peers = peersAndMessages?.map { $0.0 } + for message in outgoingMessages.values { + if message.id.peerId == peerId, let author = message.author { + if peers == nil { + peers = [] + } + peers?.append(author) + } + } + return (peers, outgoingMessages) + } + case .all: + signal = liveLocationManager.summaryManager.broadcastingToMessages() + |> map { messages -> ([EnginePeer]?, [EngineMessage.Id: EngineMessage]?) in + if messages.isEmpty { + return (nil, nil) + } else { + var peers: [EnginePeer] = [] + for message in messages.values.sorted(by: { $0.index < $1.index }) { + if let peer = message.peers[message.id.peerId] { + peers.append(EnginePeer(peer)) + } + } + return (peers, messages) + } + } + } + + self.liveLocationDisposable = (signal + |> deliverOnMainQueue).start(next: { [weak self] peers, messages in + guard let self else { + return + } + var updated = false + if let current = self.liveLocationState?.peers, let peers { + updated = current != peers + } else if (self.liveLocationState != nil) != (peers != nil) { + updated = true + } + + if updated { + if let peers, let messages { + var canClose = true + if case let .peer(peerId) = liveLocationMode { + canClose = false + for messageId in messages.keys { + if messageId.peerId == peerId { + canClose = true + } + } + } + + self.liveLocationState = ( + mode: liveLocationMode, + peers: peers, + messages: messages, + canClose: canClose, + version: self.nextVersion + ) + self.nextVersion += 1 + } else { + self.liveLocationState = nil + } + self.notifyStateUpdated() + } + }) + } + + if chatListNotices { + let twoStepData: Signal = .single(nil) |> then(context.engine.auth.twoStepVerificationConfiguration() |> map(Optional.init)) + + let accountFreezeConfiguration = (context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration]) + |> map { view -> AppConfiguration in + let appConfiguration: AppConfiguration = view.values[PreferencesKeys.appConfiguration]?.get(AppConfiguration.self) ?? AppConfiguration.defaultValue + return appConfiguration + } + |> distinctUntilChanged + |> map { appConfiguration -> AccountFreezeConfiguration in + return AccountFreezeConfiguration.with(appConfiguration: appConfiguration) + }) + + let starsSubscriptionsContextPromise = Promise(nil) + + let suggestedChatListNoticeSignal: Signal = combineLatest( + context.engine.notices.getServerProvidedSuggestions(), + context.engine.notices.getServerDismissedSuggestions(), + twoStepData, + newSessionReviews(postbox: context.account.postbox), + context.engine.data.subscribe( + TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId), + TelegramEngine.EngineData.Item.Peer.Birthday(id: context.account.peerId) + ), + context.account.stateManager.contactBirthdays, + starsSubscriptionsContextPromise.get(), + accountFreezeConfiguration + ) + |> mapToSignal { suggestions, dismissedSuggestions, configuration, newSessionReviews, data, birthdays, starsSubscriptionsContext, accountFreezeConfiguration -> Signal in + let (accountPeer, birthday) = data + + if let newSessionReview = newSessionReviews.first { + return .single(.reviewLogin(newSessionReview: newSessionReview, totalCount: newSessionReviews.count)) + } + if suggestions.contains(.setupPassword), let configuration { + var notSet = false + switch configuration { + case let .notSet(pendingEmail): + if pendingEmail == nil { + notSet = true + } + case .set: + break + } + if notSet { + return .single(.setupPassword) + } + } + + let today = Calendar(identifier: .gregorian).component(.day, from: Date()) + var todayBirthdayPeerIds: [EnginePeer.Id] = [] + for (peerId, birthday) in birthdays { + if birthday.day == today { + todayBirthdayPeerIds.append(peerId) + } + } + todayBirthdayPeerIds.sort { lhs, rhs in + return lhs < rhs + } + + if dismissedSuggestions.contains(ServerProvidedSuggestion.todayBirthdays.id) { + todayBirthdayPeerIds = [] + } + + if let _ = accountFreezeConfiguration.freezeUntilDate { + return .single(.accountFreeze) + } else if suggestions.contains(.starsSubscriptionLowBalance) { + if let starsSubscriptionsContext { + return starsSubscriptionsContext.state + |> map { state in + if state.balance > StarsAmount.zero && !state.subscriptions.isEmpty { + return .starsSubscriptionLowBalance( + amount: state.balance, + peers: state.subscriptions.map { $0.peer } + ) + } else { + return nil + } + } + } else { + starsSubscriptionsContextPromise.set(.single(context.engine.payments.peerStarsSubscriptionsContext(starsContext: nil, missingBalance: true))) + return .single(nil) + } + } else if suggestions.contains(.setupPhoto), let accountPeer, accountPeer.smallProfileImage == nil { + return .single(.setupPhoto(accountPeer)) + } else if suggestions.contains(.gracePremium) { + return .single(.premiumGrace) + } else if suggestions.contains(.xmasPremiumGift) { + return .single(.xmasPremiumGift) + } else if suggestions.contains(.annualPremium) || suggestions.contains(.upgradePremium) || suggestions.contains(.restorePremium), let inAppPurchaseManager = context.inAppPurchaseManager { + return inAppPurchaseManager.availableProducts + |> map { products -> ChatListNotice? in + if products.count > 1 { + let shortestOptionPrice: (Int64, NSDecimalNumber) + if let product = products.first(where: { $0.id.hasSuffix(".monthly") }) { + shortestOptionPrice = (Int64(Float(product.priceCurrencyAndAmount.amount)), product.priceValue) + } else { + shortestOptionPrice = (1, NSDecimalNumber(decimal: 1)) + } + for product in products { + if product.id.hasSuffix(".annual") { + let fraction = Float(product.priceCurrencyAndAmount.amount) / Float(12) / Float(shortestOptionPrice.0) + let discount = Int32(round((1.0 - fraction) * 20.0) * 5.0) + if discount > 0 { + if suggestions.contains(.restorePremium) { + return .premiumRestore(discount: discount) + } else if suggestions.contains(.annualPremium) { + return .premiumAnnualDiscount(discount: discount) + } else if suggestions.contains(.upgradePremium) { + return .premiumUpgrade(discount: discount) + } + } + break + } + } + return nil + } else { + if !GlobalExperimentalSettings.isAppStoreBuild { + if suggestions.contains(.restorePremium) { + return .premiumRestore(discount: 0) + } else if suggestions.contains(.annualPremium) { + return .premiumAnnualDiscount(discount: 0) + } else if suggestions.contains(.upgradePremium) { + return .premiumUpgrade(discount: 0) + } + } + return nil + } + } + } else if !todayBirthdayPeerIds.isEmpty { + return context.engine.data.get( + EngineDataMap(todayBirthdayPeerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:))) + ) + |> map { result -> ChatListNotice? in + var todayBirthdayPeers: [EnginePeer] = [] + for (peerId, _) in birthdays { + if let maybePeer = result[peerId], let peer = maybePeer { + todayBirthdayPeers.append(peer) + } + } + return .birthdayPremiumGift(peers: todayBirthdayPeers, birthdays: birthdays) + } + } else if suggestions.contains(.setupBirthday) && birthday == nil { + return .single(.setupBirthday) + } else if case let .link(id, url, title, subtitle) = suggestions.first(where: { if case .link = $0 { return true } else { return false} }) { + return .single(.link(id: id, url: url, title: title, subtitle: subtitle)) + } else { + return .single(nil) + } + } + |> distinctUntilChanged + + self.suggestedChatListNoticeDisposable = (suggestedChatListNoticeSignal + |> deliverOn(self.queue)).startStrict(next: { [weak self] chatListNotice in + guard let self else { + return + } + if self.chatListNotice != chatListNotice { + self.chatListNotice = chatListNotice + self.notifyStateUpdated() + } + }) + } + + if let callManager = context.sharedContext.callManager, let peerId = groupCalls { + let currentGroupCall: Signal = callManager.currentGroupCallSignal + |> distinctUntilChanged(isEqual: { lhs, rhs in + return lhs == rhs + }) + |> map { call -> PresentationGroupCall? in + guard case let .group(call) = call else { + return nil + } + guard call.peerId == peerId && call.account.peerId == context.account.peerId else { + return nil + } + return call + } + + let availableGroupCall: Signal + if let peerId = groupCalls { + availableGroupCall = context.account.viewTracker.peerView(peerId) + |> map { peerView -> (CachedChannelData.ActiveCall?, EnginePeer?) in + let peer = peerView.peers[peerId].flatMap(EnginePeer.init) + if let cachedData = peerView.cachedData as? CachedChannelData { + return (cachedData.activeCall, peer) + } else if let cachedData = peerView.cachedData as? CachedGroupData { + return (cachedData.activeCall, peer) + } else { + return (nil, peer) + } + } + |> distinctUntilChanged(isEqual: { lhs, rhs in + return lhs.0 == rhs.0 + }) + |> mapToSignal { activeCall, peer -> Signal in + guard let activeCall = activeCall else { + return .single(nil) + } + + var isChannel = false + if let peer = peer, case let .channel(channel) = peer, case .broadcast = channel.info { + isChannel = true + } + + return Signal { [weak context] subscriber in + guard let context = context, let callContextCache = context.cachedGroupCallContexts as? AccountGroupCallContextCacheImpl else { + return EmptyDisposable + } + + let disposable = MetaDisposable() + + callContextCache.impl.syncWith { impl in + let callContext = impl.get(account: context.account, engine: context.engine, peerId: peerId, isChannel: isChannel, call: EngineGroupCallDescription(activeCall)) + disposable.set((callContext.context.panelData + |> deliverOnMainQueue).start(next: { panelData in + callContext.keep() + var updatedPanelData = panelData + if let panelData { + var updatedInfo = panelData.info + updatedInfo.subscribedToScheduled = activeCall.subscribedToScheduled + updatedPanelData = panelData.withInfo(updatedInfo) + } + subscriber.putNext(updatedPanelData) + })) + } + + return ActionDisposable { + disposable.dispose() + } + } + |> runOn(.mainQueue()) + } + } else { + availableGroupCall = .single(nil) + } + + let previousCurrentGroupCall = Atomic(value: nil) + self.currentGroupCallDisposable = combineLatest(queue: .mainQueue(), availableGroupCall, currentGroupCall).start(next: { [weak self] availableState, currentGroupCall in + guard let self else { + return + } + + let previousCurrentGroupCall = previousCurrentGroupCall.swap(currentGroupCall) + + let panelData: AccountGroupCallContextImpl.GroupCallPanelData? + if previousCurrentGroupCall != nil && currentGroupCall == nil && availableState?.participantCount == 1 { + panelData = nil + } else { + panelData = currentGroupCall != nil || (availableState?.participantCount == 0 && availableState?.info.scheduleTimestamp == nil && availableState?.info.isStream == false) ? nil : availableState + } + + let groupCall = panelData.flatMap { panelData in + return GroupCall( + peerId: panelData.peerId, + isChannel: panelData.isChannel, + info: panelData.info, + topParticipants: panelData.topParticipants, + participantCount: panelData.participantCount, + activeSpeakers: panelData.activeSpeakers, + groupCall: panelData.groupCall + ) + } + if self.groupCall != groupCall { + self.groupCall = groupCall + self.notifyStateUpdated() + } + }) + } + } + + deinit { + self.mediaStatusDisposable?.dispose() + self.liveLocationDisposable?.dispose() + self.suggestedChatListNoticeDisposable?.dispose() + self.currentGroupCallDisposable?.dispose() + } + + private func notifyStateUpdated() { + self.stateValue = State( + mediaPlayback: self.playlistStateAndType.flatMap { playlistStateAndType in + return MediaPlayback( + version: playlistStateAndType.7, + item: playlistStateAndType.0, + previousItem: playlistStateAndType.1, + nextItem: playlistStateAndType.2, + playbackOrder: playlistStateAndType.3, + kind: playlistStateAndType.4, + playlistLocation: playlistStateAndType.6, + account: playlistStateAndType.5 + ) + }, + liveLocation: self.liveLocationState.flatMap { liveLocationState in + return GlobalControlPanelsContext.LiveLocation( + mode: liveLocationState.mode, + peers: liveLocationState.peers, + messages: liveLocationState.messages, + canClose: liveLocationState.canClose, + version: liveLocationState.version + ) + }, + chatListNotice: self.chatListNotice, + groupCall: self.groupCall + ) + self.statePipe.putNext(self.stateValue) + } + + func dismissChatListNotice(parentController: ViewController, notice: ChatListNotice) { + let presentationData = self.context.sharedContext.currentPresentationData.with({ $0 }) + switch notice { + case .xmasPremiumGift: + let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.xmasPremiumGift.id).startStandalone() + parentController.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gift", scale: 0.058, colors: ["__allcolors__": UIColor.white], title: nil, text: presentationData.strings.ChatList_PremiumGiftInSettingsInfo, customUndoText: nil, timeout: 5.0), elevatedLayout: false, action: { _ in + return true + }), in: .current) + case .setupBirthday: + let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupBirthday.id).startStandalone() + parentController.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gift", scale: 0.058, colors: ["__allcolors__": UIColor.white], title: nil, text: presentationData.strings.ChatList_BirthdayInSettingsInfo, customUndoText: nil, timeout: 5.0), elevatedLayout: false, action: { _ in + return true + }), in: .current) + case .birthdayPremiumGift: + let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.todayBirthdays.id).startStandalone() + parentController.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gift", scale: 0.058, colors: ["__allcolors__": UIColor.white], title: nil, text: presentationData.strings.ChatList_PremiumGiftInSettingsInfo, customUndoText: nil, timeout: 5.0), elevatedLayout: false, action: { _ in + return true + }), in: .current) + case .premiumGrace: + let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.gracePremium.id).startStandalone() + case .setupPhoto: + let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupPhoto.id).startStandalone() + case .starsSubscriptionLowBalance: + let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.starsSubscriptionLowBalance.id).startStandalone() + case let .link(id, _, _, _): + let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: id).startStandalone() + default: + break + } + } + } + + private let impl: QueueLocalObject + public var state: Signal { + return self.impl.signalWith { impl, subscriber in + subscriber.putNext(impl.stateValue) + return impl.statePipe.signal().start(next: subscriber.putNext) + } + } + + public init(context: AccountContext, mediaPlayback: Bool, liveLocationMode: LiveLocationMode?, groupCalls: EnginePeer.Id?, chatListNotices: Bool) { + self.impl = QueueLocalObject(queue: .mainQueue(), generate: { + return Impl(queue: .mainQueue(), context: context, mediaPlayback: mediaPlayback, liveLocationMode: liveLocationMode, groupCalls: groupCalls, chatListNotices: chatListNotices) + }) + } + + public func dismissChatListNotice(parentController: ViewController, notice: ChatListNotice) { + self.impl.with { impl in + impl.dismissChatListNotice(parentController: parentController, notice: notice) + } + } + + public func setTempVoicePlaylistEnded(_ f: (() -> Void)?) { + self.impl.with { impl in + return impl.tempVoicePlaylistEnded = f + } + } + + public func setTempVoicePlaylistItemChanged(_ f: ((SharedMediaPlaylistItem?, SharedMediaPlaylistItem?) -> Void)?) { + self.impl.with { impl in + return impl.tempVoicePlaylistItemChanged = f + } + } + + public var tempVoicePlaylistCurrentItem: SharedMediaPlaylistItem? { + return self.impl.syncWith { impl in + return impl.tempVoicePlaylistCurrentItem + } + } + + public var playlistStateAndType: (SharedMediaPlaylistItem, SharedMediaPlaylistItem?, SharedMediaPlaylistItem?, MusicPlaybackSettingsOrder, MediaManagerPlayerType, Account, SharedMediaPlaylistLocation, Int)? { + return self.impl.syncWith { impl in + return impl.playlistStateAndType + } + } +} diff --git a/submodules/TelegramUI/Components/GroupCallHeaderPanelComponent/BUILD b/submodules/TelegramUI/Components/GroupCallHeaderPanelComponent/BUILD new file mode 100644 index 00000000..f3a92b45 --- /dev/null +++ b/submodules/TelegramUI/Components/GroupCallHeaderPanelComponent/BUILD @@ -0,0 +1,38 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "GroupCallHeaderPanelComponent", + module_name = "GroupCallHeaderPanelComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Display", + "//submodules/AsyncDisplayKit", + "//submodules/TelegramPresentationData", + "//submodules/AccountContext", + "//submodules/TelegramCore", + "//submodules/Postbox", + "//submodules/Components/ComponentDisplayAdapters", + "//submodules/TelegramUIPreferences", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/ComponentFlow", + "//submodules/TelegramUI/Components/GlobalControlPanelsContext", + "//submodules/PresentationDataUtils", + "//submodules/TextFormat", + "//submodules/Markdown", + "//submodules/LocalizedPeerData", + "//submodules/LiveLocationTimerNode", + "//submodules/TelegramStringFormatting", + "//submodules/AppBundle", + "//submodules/AnimatedAvatarSetNode", + "//submodules/AudioBlob", + "//submodules/TelegramCallsUI", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/GroupCallHeaderPanelComponent/Sources/GroupCallHeaderPanelComponent.swift b/submodules/TelegramUI/Components/GroupCallHeaderPanelComponent/Sources/GroupCallHeaderPanelComponent.swift new file mode 100644 index 00000000..bd3d0802 --- /dev/null +++ b/submodules/TelegramUI/Components/GroupCallHeaderPanelComponent/Sources/GroupCallHeaderPanelComponent.swift @@ -0,0 +1,124 @@ +import Foundation +import UIKit +import Display +import TelegramPresentationData +import ComponentFlow +import ComponentDisplayAdapters +import AccountContext +import TelegramCore +import GlobalControlPanelsContext +import SwiftSignalKit +import Postbox +import PresentationDataUtils + +public final class GroupCallHeaderPanelComponent: Component { + public let context: AccountContext + public let theme: PresentationTheme + public let strings: PresentationStrings + public let data: GlobalControlPanelsContext.GroupCall + public let onTapAction: () -> Void + public let onNotifyScheduledTapAction: () -> Void + + public init( + context: AccountContext, + theme: PresentationTheme, + strings: PresentationStrings, + data: GlobalControlPanelsContext.GroupCall, + onTapAction: @escaping () -> Void, + onNotifyScheduledTapAction: @escaping () -> Void + ) { + self.context = context + self.theme = theme + self.strings = strings + self.data = data + self.onTapAction = onTapAction + self.onNotifyScheduledTapAction = onNotifyScheduledTapAction + } + + public static func ==(lhs: GroupCallHeaderPanelComponent, rhs: GroupCallHeaderPanelComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.theme !== rhs.theme { + return false + } + if lhs.strings !== rhs.strings { + return false + } + if lhs.data != rhs.data { + return false + } + return true + } + + public final class View: UIView { + private var panel: GroupCallNavigationAccessoryPanel? + + private var component: GroupCallHeaderPanelComponent? + private weak var state: EmptyComponentState? + + public override init(frame: CGRect) { + super.init(frame: frame) + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + } + + func update(component: GroupCallHeaderPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + let themeUpdated = self.component?.theme !== component.theme + + self.component = component + self.state = state + + let presentationData = component.context.sharedContext.currentPresentationData.with ({ $0 }).withUpdated(theme: component.theme) + + let panel: GroupCallNavigationAccessoryPanel + if let current = self.panel { + panel = current + } else { + panel = GroupCallNavigationAccessoryPanel( + context: component.context, + presentationData: presentationData, + tapAction: { [weak self] in + guard let self, let component = self.component else { + return + } + component.onTapAction() + }, + notifyScheduledTapAction: { [weak self] in + guard let self, let component = self.component else { + return + } + component.onNotifyScheduledTapAction() + } + ) + self.panel = panel + self.addSubview(panel.view) + } + + let size = CGSize(width: availableSize.width, height: 50.0) + let panelFrame = CGRect(origin: CGPoint(), size: size) + transition.setFrame(view: panel.view, frame: panelFrame) + panel.updateLayout(size: panelFrame.size, leftInset: 0.0, rightInset: 0.0, isHidden: false, transition: transition.containedViewLayoutTransition) + panel.update(data: component.data) + + if themeUpdated { + panel.updatePresentationData(presentationData) + } + + return size + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/GroupCallHeaderPanelComponent/Sources/GroupCallNavigationAccessoryPanel.swift b/submodules/TelegramUI/Components/GroupCallHeaderPanelComponent/Sources/GroupCallNavigationAccessoryPanel.swift new file mode 100644 index 00000000..643569e2 --- /dev/null +++ b/submodules/TelegramUI/Components/GroupCallHeaderPanelComponent/Sources/GroupCallNavigationAccessoryPanel.swift @@ -0,0 +1,808 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import TelegramCore +import Postbox +import TelegramPresentationData +import TelegramUIPreferences +import TelegramStringFormatting +import AccountContext +import AppBundle +import SwiftSignalKit +import AnimatedAvatarSetNode +import AudioBlob +import TelegramCallsUI +import GlobalControlPanelsContext + +func textForTimeout(value: Int32) -> String { + if value < 3600 { + let minutes = value / 60 + let seconds = value % 60 + let secondsPadding = seconds < 10 ? "0" : "" + return "\(minutes):\(secondsPadding)\(seconds)" + } else { + let hours = value / 3600 + let minutes = (value % 3600) / 60 + let minutesPadding = minutes < 10 ? "0" : "" + let seconds = value % 60 + let secondsPadding = seconds < 10 ? "0" : "" + return "\(hours):\(minutesPadding)\(minutes):\(secondsPadding)\(seconds)" + } +} + +private let titleFont = Font.semibold(15.0) +private let subtitleFont = Font.regular(13.0) + +private final class FakeAudioLevelGenerator { + private var isFirstTime: Bool = true + private var nextTarget: Float = 0.0 + private var nextTargetProgress: Float = 0.0 + private var nextTargetProgressNorm: Float = 1.0 + + func get() -> Float { + let wasFirstTime = self.isFirstTime + self.isFirstTime = false + + self.nextTargetProgress *= 0.82 + if self.nextTargetProgress <= 0.01 { + if Int.random(in: 0 ... 4) <= 1 && !wasFirstTime { + self.nextTarget = 0.0 + self.nextTargetProgressNorm = Float.random(in: 0.1 ..< 0.3) + } else { + self.nextTarget = Float.random(in: 0.0 ..< 20.0) + self.nextTargetProgressNorm = Float.random(in: 0.2 ..< 0.7) + } + self.nextTargetProgress = self.nextTargetProgressNorm + return self.nextTarget + } else { + let value = self.nextTarget * max(0.0, self.nextTargetProgress / self.nextTargetProgressNorm) + return value + } + } +} + +final class GroupCallNavigationAccessoryPanel: ASDisplayNode { + private let context: AccountContext + private var theme: PresentationTheme + private var strings: PresentationStrings + private var dateTimeFormat: PresentationDateTimeFormat + + private let tapAction: () -> Void + private let notifyScheduledTapAction: () -> Void + + private let contentNode: ASDisplayNode + + private let tapButton: HighlightTrackingButtonNode + + private let joinButton: HighlightableButtonNode + private let joinButtonTitleNode: ImmediateTextNode + private let joinButtonBackgroundNode: ASImageNode + + private var previewImageNode: ASImageNode? + private var previewImage: UIImage? + + private var audioLevelView: VoiceBlobView? + + private let micButton: HighlightTrackingButtonNode + private let micButtonForegroundNode: VoiceChatMicrophoneNode + private let micButtonBackgroundNode: ASImageNode + private var micButtonBackgroundNodeIsMuted: Bool? + + let titleNode: ImmediateTextNode + let textNode: ImmediateTextNode + private var textIsActive = false + private let muteIconNode: ASImageNode + + private var isScheduled = false + private var isLate = false + private var currentText: String = "" + private var updateTimer: SwiftSignalKit.Timer? + + private let avatarsContext: AnimatedAvatarSetContext + private var avatarsContent: AnimatedAvatarSetContext.Content? + private let avatarsNode: AnimatedAvatarSetNode + private var audioLevelGenerators: [PeerId: FakeAudioLevelGenerator] = [:] + private var audioLevelGeneratorTimer: SwiftSignalKit.Timer? + + private let backgroundNode: ASDisplayNode + private let separatorNode: ASDisplayNode + + private let membersDisposable = MetaDisposable() + private let isMutedDisposable = MetaDisposable() + private let audioLevelDisposable = MetaDisposable() + private var imageDisposable: Disposable? + + private var callState: PresentationGroupCallState? + + private let hapticFeedback = HapticFeedback() + + private var currentData: GlobalControlPanelsContext.GroupCall? + private var validLayout: (CGSize, CGFloat, CGFloat, Bool)? + + public init(context: AccountContext, presentationData: PresentationData, tapAction: @escaping () -> Void, notifyScheduledTapAction: @escaping () -> Void) { + self.context = context + self.theme = presentationData.theme + self.strings = presentationData.strings + self.dateTimeFormat = presentationData.dateTimeFormat + + self.tapAction = tapAction + self.notifyScheduledTapAction = notifyScheduledTapAction + + self.contentNode = ASDisplayNode() + + self.tapButton = HighlightTrackingButtonNode() + + self.joinButton = HighlightableButtonNode() + self.joinButtonTitleNode = ImmediateTextNode() + self.joinButtonBackgroundNode = ASImageNode() + self.joinButtonBackgroundNode.clipsToBounds = true + self.joinButtonBackgroundNode.displaysAsynchronously = false + self.joinButtonBackgroundNode.cornerRadius = 14.0 + + self.micButton = HighlightTrackingButtonNode() + self.micButtonForegroundNode = VoiceChatMicrophoneNode() + self.micButtonBackgroundNode = ASImageNode() + + self.titleNode = ImmediateTextNode() + self.textNode = ImmediateTextNode() + + self.muteIconNode = ASImageNode() + + self.avatarsContext = AnimatedAvatarSetContext() + self.avatarsNode = AnimatedAvatarSetNode() + + self.backgroundNode = ASDisplayNode() + + self.separatorNode = ASDisplayNode() + self.separatorNode.isLayerBacked = true + + super.init() + + self.clipsToBounds = true + + self.addSubnode(self.contentNode) + + self.contentNode.addSubnode(self.backgroundNode) + + self.tapButton.highligthedChanged = { [weak self] highlighted in + if let strongSelf = self { + if highlighted { + strongSelf.titleNode.layer.removeAnimation(forKey: "opacity") + strongSelf.titleNode.alpha = 0.4 + strongSelf.textNode.layer.removeAnimation(forKey: "opacity") + strongSelf.textNode.alpha = 0.4 + } else { + strongSelf.titleNode.alpha = 1.0 + strongSelf.titleNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + strongSelf.textNode.alpha = 1.0 + strongSelf.textNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + } + } + } + + self.contentNode.addSubnode(self.titleNode) + self.contentNode.addSubnode(self.textNode) + + self.contentNode.addSubnode(self.avatarsNode) + + self.tapButton.addTarget(self, action: #selector(self.tapped), forControlEvents: [.touchUpInside]) + self.contentNode.addSubnode(self.tapButton) + + self.joinButton.addSubnode(self.joinButtonBackgroundNode) + self.joinButton.addSubnode(self.joinButtonTitleNode) + self.contentNode.addSubnode(self.joinButton) + self.joinButton.addTarget(self, action: #selector(self.joinTapped), forControlEvents: [.touchUpInside]) + + self.micButton.addSubnode(self.micButtonBackgroundNode) + self.micButton.addSubnode(self.micButtonForegroundNode) + self.contentNode.addSubnode(self.micButton) + self.micButton.addTarget(self, action: #selector(self.micTapped), forControlEvents: [.touchUpInside]) + + self.contentNode.addSubnode(self.separatorNode) + + self.updatePresentationData(presentationData) + } + + deinit { + self.membersDisposable.dispose() + self.isMutedDisposable.dispose() + self.audioLevelGeneratorTimer?.invalidate() + self.updateTimer?.invalidate() + self.imageDisposable?.dispose() + self.audioLevelDisposable.dispose() + } + + public override func didLoad() { + super.didLoad() + + let longTapRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.micButtonPressGesture(_:))) + longTapRecognizer.minimumPressDuration = 0.01 + self.micButton.view.addGestureRecognizer(longTapRecognizer) + } + + @objc private func tapped() { + self.tapAction() + } + + @objc private func joinTapped() { + if let info = self.currentData?.info, let _ = info.scheduleTimestamp, !info.subscribedToScheduled { + self.notifyScheduledTapAction() + } else { + self.tapAction() + } + } + + @objc private func micTapped() { + guard let call = self.currentData?.groupCall else { + return + } + call.toggleIsMuted() + } + + private var actionButtonPressGestureStartTime: Double = 0.0 + + @objc private func micButtonPressGesture(_ gestureRecognizer: UILongPressGestureRecognizer) { + guard let call = self.currentData?.groupCall, let callState = self.callState else { + return + } + switch gestureRecognizer.state { + case .began: + self.hapticFeedback.impact(.veryLight) + + self.actionButtonPressGestureStartTime = CACurrentMediaTime() + if callState.muteState != nil { + call.setIsMuted(action: .muted(isPushToTalkActive: true)) + } + case .ended, .cancelled: + self.hapticFeedback.impact(.veryLight) + + let timestamp = CACurrentMediaTime() + if callState.muteState != nil || timestamp - self.actionButtonPressGestureStartTime < 0.1 { + call.toggleIsMuted() + } else { + call.setIsMuted(action: .muted(isPushToTalkActive: false)) + } + default: + break + } + } + + public func updatePresentationData(_ presentationData: PresentationData) { + self.theme = presentationData.theme + self.strings = presentationData.strings + self.dateTimeFormat = presentationData.dateTimeFormat + + self.separatorNode.backgroundColor = presentationData.theme.chat.historyNavigation.strokeColor + + self.joinButtonTitleNode.attributedText = NSAttributedString(string: self.joinButtonTitleNode.attributedText?.string ?? "", font: Font.with(size: 15.0, design: .round, weight: .semibold, traits: [.monospacedNumbers]), textColor: self.isScheduled ? .white : presentationData.theme.chat.inputPanel.actionControlForegroundColor) + self.textNode.attributedText = NSAttributedString(string: self.textNode.attributedText?.string ?? "", font: Font.regular(13.0), textColor: presentationData.theme.chat.inputPanel.secondaryTextColor) + + self.muteIconNode.image = PresentationResourcesChat.chatTitleMuteIcon(presentationData.theme) + + self.updateJoinButton() + + if let (size, leftInset, rightInset, isHidden) = self.validLayout { + self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, isHidden: isHidden, transition: .immediate) + } + } + + private func updateJoinButton() { + if self.isScheduled { + let purple = UIColor(rgb: 0x5d4ed1) + let pink = UIColor(rgb: 0xea436f) + let latePurple = UIColor(rgb: 0xaa56a6) + let latePink = UIColor(rgb: 0xef476f) + let colors: [UIColor] + if self.isLate { + colors = [latePurple, latePink] + } else { + colors = [purple, pink] + } + if self.joinButtonBackgroundNode.image != nil, let snapshotView = self.joinButtonBackgroundNode.view.snapshotContentTree() { + self.joinButtonBackgroundNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.joinButtonBackgroundNode.view) + + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 1.0, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + } + + self.joinButtonBackgroundNode.image = generateGradientImage(size: CGSize(width: 100.0, height: 1.0), colors: colors, locations: [0.0, 1.0], direction: .horizontal) + self.joinButtonBackgroundNode.backgroundColor = nil + } else { + self.joinButtonBackgroundNode.image = nil + self.joinButtonBackgroundNode.backgroundColor = self.theme.chat.inputPanel.actionControlFillColor + } + } + + private func animateTextChange() { + if let snapshotView = self.textNode.view.snapshotContentTree() { + let offset: CGFloat = self.textIsActive ? -7.0 : 7.0 + self.textNode.view.superview?.insertSubview(snapshotView, belowSubview: self.textNode.view) + + snapshotView.frame = self.textNode.frame + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + snapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -offset), duration: 0.2, removeOnCompletion: false, additive: true) + + self.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + self.textNode.layer.animatePosition(from: CGPoint(x: 0.0, y: offset), to: CGPoint(), duration: 0.2, additive: true) + } + } + + public func update(data: GlobalControlPanelsContext.GroupCall) { + let previousData = self.currentData + self.currentData = data + + var updateAudioLevels = false + + if previousData?.groupCall !== data.groupCall { + let membersText: String + if data.participantCount == 0 { + membersText = self.strings.VoiceChat_Panel_TapToJoin + } else if let groupCall = data.groupCall, groupCall.isStream { + membersText = self.strings.LiveStream_ViewerCount(Int32(data.participantCount)) + } else { + membersText = self.strings.VoiceChat_Panel_Members(Int32(data.participantCount)) + } + self.currentText = membersText + + if data.info.isStream { + self.avatarsContent = self.avatarsContext.update(peers: [], animated: false) + } else { + self.avatarsContent = self.avatarsContext.update(peers: data.topParticipants.compactMap { $0.peer }, animated: false) + + if let imageDisposable = self.imageDisposable { + self.imageDisposable = nil + imageDisposable.dispose() + } + } + + self.textNode.attributedText = NSAttributedString(string: membersText, font: Font.regular(13.0), textColor: self.theme.chat.inputPanel.secondaryTextColor) + + self.callState = nil + + self.membersDisposable.set(nil) + self.isMutedDisposable.set(nil) + + if let groupCall = data.groupCall { + self.membersDisposable.set((groupCall.summaryState + |> deliverOnMainQueue).start(next: { [weak self] summaryState in + guard let strongSelf = self, let summaryState = summaryState else { + return + } + + let membersText: String + if summaryState.participantCount == 0 { + membersText = strongSelf.strings.VoiceChat_Panel_TapToJoin + } else if let info = summaryState.info, info.isStream { + membersText = strongSelf.strings.LiveStream_ViewerCount(Int32(summaryState.participantCount)) + } else { + membersText = strongSelf.strings.VoiceChat_Panel_Members(Int32(summaryState.participantCount)) + } + strongSelf.currentText = membersText + + if let info = summaryState.info, info.isStream { + strongSelf.avatarsContent = strongSelf.avatarsContext.update(peers: [], animated: false) + } else { + strongSelf.avatarsContent = strongSelf.avatarsContext.update(peers: summaryState.topParticipants.compactMap { $0.peer }, animated: false) + } + + if let (size, leftInset, rightInset, isHidden) = strongSelf.validLayout { + strongSelf.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, isHidden: isHidden, transition: .immediate) + } + })) + + self.isMutedDisposable.set((groupCall.state + |> deliverOnMainQueue).start(next: { [weak self] callState in + guard let strongSelf = self else { + return + } + + var transition: ContainedViewLayoutTransition = .immediate + if strongSelf.callState != nil { + transition = .animated(duration: 0.3, curve: .spring) + } + + strongSelf.callState = callState + + if let (size, leftInset, rightInset, isHidden) = strongSelf.validLayout { + strongSelf.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, isHidden: isHidden, transition: transition) + } + })) + + self.audioLevelDisposable.set((groupCall.myAudioLevel + |> deliverOnMainQueue).start(next: { [weak self] value in + guard let strongSelf = self else { + return + } + + if strongSelf.audioLevelView == nil, strongSelf.context.sharedContext.energyUsageSettings.fullTranslucency { + let blobFrame = CGRect(origin: CGPoint(), size: CGSize(width: 36.0, height: 36.0)).insetBy(dx: -12.0, dy: -12.0) + + let audioLevelView = VoiceBlobView( + frame: blobFrame, + maxLevel: 0.3, + smallBlobRange: (0, 0), + mediumBlobRange: (0.7, 0.8), + bigBlobRange: (0.8, 0.9) + ) + + let maskRect = CGRect(origin: .zero, size: blobFrame.size) + let playbackMaskLayer = CAShapeLayer() + playbackMaskLayer.frame = maskRect + playbackMaskLayer.fillRule = .evenOdd + let maskPath = UIBezierPath() + maskPath.append(UIBezierPath(roundedRect: maskRect.insetBy(dx: 12, dy: 12), cornerRadius: 22)) + maskPath.append(UIBezierPath(rect: maskRect)) + playbackMaskLayer.path = maskPath.cgPath + audioLevelView.layer.mask = playbackMaskLayer + + audioLevelView.setColor(UIColor(rgb: 0x30B251)) + strongSelf.audioLevelView = audioLevelView + + strongSelf.micButton.view.insertSubview(audioLevelView, at: 0) + } + + strongSelf.audioLevelView?.updateLevel(CGFloat(value) * 2.0) + if value > 0.0 { + strongSelf.audioLevelView?.startAnimating() + } else { + strongSelf.audioLevelView?.stopAnimating(duration: 0.5) + } + })) + } + } else if data.groupCall == nil { + self.audioLevelDisposable.set(nil) + + let membersText: String + if data.participantCount == 0 { + membersText = self.strings.VoiceChat_Panel_TapToJoin + } else if data.info.isStream { + membersText = self.strings.LiveStream_ViewerCount(Int32(data.participantCount)) + } else { + membersText = self.strings.VoiceChat_Panel_Members(Int32(data.participantCount)) + } + self.currentText = membersText + + if data.info.isStream { + self.avatarsContent = self.avatarsContext.update(peers: [], animated: false) + } else { + self.avatarsContent = self.avatarsContext.update(peers: data.topParticipants.compactMap { $0.peer }, animated: false) + } + + updateAudioLevels = true + } + + #if DEBUG + if data.info.isStream, !"".isEmpty { + if self.imageDisposable == nil { + let engine = self.context.engine + let info = data.info + self.imageDisposable = (engine.calls.getAudioBroadcastDataSource(callId: info.id, accessHash: info.accessHash) + |> mapToSignal { source -> Signal in + guard let source else { + return .single(nil) + } + + let time = engine.calls.requestStreamState(dataSource: source, callId: info.id, accessHash: info.accessHash) + |> map { state -> Int64? in + guard let state else { + return nil + } + return state.channels.first?.latestTimestamp + } + + return time + |> mapToSignal { latestTimestamp -> Signal in + guard let latestTimestamp else { + return .single(nil) + } + + let durationMilliseconds: Int64 = 32000 + let bufferOffset: Int64 = 1 * durationMilliseconds + let timestampId = (latestTimestamp / durationMilliseconds) * durationMilliseconds - bufferOffset + + return engine.calls.getVideoBroadcastPart(dataSource: source, callId: info.id, accessHash: info.accessHash, timestampIdMilliseconds: timestampId, durationMilliseconds: durationMilliseconds, channelId: 2, quality: 0) + |> mapToSignal { result -> Signal in + switch result.status { + case let .data(data): + return .single(data) + case .notReady, .resyncNeeded, .rejoinNeeded: + return .single(nil) + } + } + } + } + |> deliverOnMainQueue).start(next: { [weak self] data in + guard let self, let data else { + return + } + + var image: UIImage? + for i in 0 ..< 100 { + image = UIImage(data: data.subdata(in: i ..< data.count)) + if image != nil { + break + } + } + self.previewImage = image + if let (size, leftInset, rightInset, isHidden) = self.validLayout { + self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, isHidden: isHidden, transition: .animated(duration: 0.2, curve: .easeInOut)) + } + }) + } + } + #endif + + if let (size, leftInset, rightInset, isHidden) = self.validLayout { + self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, isHidden: isHidden, transition: .animated(duration: 0.2, curve: .easeInOut)) + } + + if updateAudioLevels { + for peerId in data.activeSpeakers { + if self.audioLevelGenerators[peerId] == nil { + self.audioLevelGenerators[peerId] = FakeAudioLevelGenerator() + } + } + var removeGenerators: [PeerId] = [] + for peerId in self.audioLevelGenerators.keys { + if !data.activeSpeakers.contains(peerId) { + removeGenerators.append(peerId) + } + } + for peerId in removeGenerators { + self.audioLevelGenerators.removeValue(forKey: peerId) + } + + if self.audioLevelGenerators.isEmpty { + self.audioLevelGeneratorTimer?.invalidate() + self.audioLevelGeneratorTimer = nil + self.avatarsNode.updateAudioLevels(color: self.theme.chat.inputPanel.actionControlFillColor, backgroundColor: self.theme.chat.inputPanel.actionControlFillColor, levels: [:]) + } else if self.audioLevelGeneratorTimer == nil { + let audioLevelGeneratorTimer = SwiftSignalKit.Timer(timeout: 1.0 / 30.0, repeat: true, completion: { [weak self] in + self?.sampleAudioGenerators() + }, queue: .mainQueue()) + self.audioLevelGeneratorTimer = audioLevelGeneratorTimer + audioLevelGeneratorTimer.start() + } + } + } + + private func sampleAudioGenerators() { + var levels: [PeerId: Float] = [:] + for (peerId, generator) in self.audioLevelGenerators { + levels[peerId] = generator.get() + } + self.avatarsNode.updateAudioLevels(color: self.theme.chat.inputPanel.actionControlFillColor, backgroundColor: self.theme.chat.inputPanel.actionControlFillColor, levels: levels) + } + + public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, isHidden: Bool, transition: ContainedViewLayoutTransition) { + self.validLayout = (size, leftInset, rightInset, isHidden) + + let staticTransition: ContainedViewLayoutTransition = .immediate + + let panelHeight = size.height + + transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: isHidden ? -size.height : 0.0), size: size)) + transition.updateAlpha(node: self.contentNode, alpha: isHidden ? 0.0 : 1.0) + + self.tapButton.frame = CGRect(origin: CGPoint(), size: CGSize(width: size.width - 7.0 - 36.0 - 7.0, height: panelHeight)) + + if let avatarsContent = self.avatarsContent { + var avatarsTransition = transition + if self.avatarsNode.bounds.isEmpty { + avatarsTransition = .immediate + } + + let avatarsSize = self.avatarsNode.update(context: self.context, content: avatarsContent, itemSize: CGSize(width: 32.0, height: 32.0), animated: avatarsTransition.isAnimated, synchronousLoad: true) + avatarsTransition.updateFrame(node: self.avatarsNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarsSize.width) / 2.0), y: floor((size.height - avatarsSize.height) / 2.0)), size: avatarsSize)) + } + + var joinText = self.strings.VoiceChat_PanelJoin + var title = self.strings.VoiceChat_Title + var isChannel = false + if let currentData = self.currentData { + if currentData.isChannel || currentData.info.isStream { + if let titleValue = currentData.info.title, !titleValue.isEmpty { + title = titleValue + } else { + title = self.strings.VoiceChatChannel_Title + } + isChannel = true + } + } + var text = self.currentText + var isScheduled = false + var isLate = false + if let info = self.currentData?.info, let scheduleTime = info.scheduleTimestamp { + isScheduled = true + if let voiceChatTitle = self.currentData?.info.title { + title = voiceChatTitle + text = humanReadableStringForTimestamp(strings: self.strings, dateTimeFormat: self.dateTimeFormat, timestamp: scheduleTime, alwaysShowTime: true, format: HumanReadableStringFormat(dateFormatString: { isChannel ? self.strings.Conversation_ScheduledLiveStreamStartsOn($0) : self.strings.Conversation_ScheduledVoiceChatStartsOn($0) }, tomorrowFormatString: { isChannel ? self.strings.Conversation_ScheduledLiveStreamStartsTomorrow($0) : self.strings.Conversation_ScheduledVoiceChatStartsTomorrow($0) }, todayFormatString: { isChannel ? self.strings.Conversation_ScheduledLiveStreamStartsToday($0) : self.strings.Conversation_ScheduledVoiceChatStartsToday($0) })).string + } else { + title = isChannel ? self.strings.Conversation_ScheduledLiveStream : self.strings.Conversation_ScheduledVoiceChat + text = humanReadableStringForTimestamp(strings: self.strings, dateTimeFormat: self.dateTimeFormat, timestamp: scheduleTime, alwaysShowTime: true, format: HumanReadableStringFormat(dateFormatString: { self.strings.Conversation_ScheduledVoiceChatStartsOnShort($0) }, tomorrowFormatString: { self.strings.Conversation_ScheduledVoiceChatStartsTomorrowShort($0) }, todayFormatString: { self.strings.Conversation_ScheduledVoiceChatStartsTodayShort($0) })).string + } + + if info.subscribedToScheduled { + let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) + let elapsedTime = scheduleTime - currentTime + if elapsedTime >= 86400 { + joinText = scheduledTimeIntervalString(strings: strings, value: elapsedTime).uppercased() + } else if elapsedTime < 0 { + joinText = "-\(textForTimeout(value: abs(elapsedTime)))".uppercased() + isLate = true + } else { + joinText = textForTimeout(value: elapsedTime).uppercased() + } + } else { + joinText = strings.Chat_TitleVideochatPanel_NotifyScheduledButton + } + + if self.updateTimer == nil { + let timer = SwiftSignalKit.Timer(timeout: 0.5, repeat: true, completion: { [weak self] in + if let strongSelf = self, let (size, leftInset, rightInset, isHidden) = strongSelf.validLayout { + strongSelf.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, isHidden: isHidden, transition: .immediate) + } + }, queue: Queue.mainQueue()) + self.updateTimer = timer + timer.start() + } + } else { + if let timer = self.updateTimer { + self.updateTimer = nil + timer.invalidate() + } + if let voiceChatTitle = self.currentData?.info.title, voiceChatTitle.count < 15 { + title = voiceChatTitle + } + } + + if self.isScheduled != isScheduled || self.isLate != isLate { + self.isScheduled = isScheduled + self.isLate = isLate + self.updateJoinButton() + } + + self.joinButtonTitleNode.attributedText = NSAttributedString(string: joinText, font: Font.with(size: 15.0, design: .round, weight: .semibold, traits: [.monospacedNumbers]), textColor: isScheduled ? .white : self.theme.chat.inputPanel.actionControlForegroundColor) + + let joinButtonTitleSize = self.joinButtonTitleNode.updateLayout(CGSize(width: 150.0, height: .greatestFiniteMagnitude)) + let joinButtonSize = CGSize(width: joinButtonTitleSize.width + 20.0, height: 28.0) + let joinButtonFrame = CGRect(origin: CGPoint(x: size.width - rightInset - 7.0 - joinButtonSize.width, y: floor((panelHeight - joinButtonSize.height) / 2.0)), size: joinButtonSize) + staticTransition.updateFrame(node: self.joinButton, frame: joinButtonFrame) + staticTransition.updateFrame(node: self.joinButtonBackgroundNode, frame: CGRect(origin: CGPoint(), size: joinButtonFrame.size)) + staticTransition.updateFrame(node: self.joinButtonTitleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((joinButtonFrame.width - joinButtonTitleSize.width) / 2.0), y: floorToScreenPixels((joinButtonFrame.height - joinButtonTitleSize.height) / 2.0)), size: joinButtonTitleSize)) + + if let previewImage = self.previewImage { + let previewImageNode: ASImageNode + if let current = self.previewImageNode { + previewImageNode = current + } else { + previewImageNode = ASImageNode() + previewImageNode.clipsToBounds = true + previewImageNode.cornerRadius = 8.0 + previewImageNode.contentMode = .scaleAspectFill + self.previewImageNode = previewImageNode + self.contentNode.addSubnode(previewImageNode) + } + previewImageNode.image = previewImage + let previewSize = CGSize(width: 40.0, height: 40.0) + previewImageNode.frame = CGRect(origin: CGPoint(x: joinButtonFrame.minX - previewSize.width - 8.0, y: joinButtonFrame.minY + floor((joinButtonFrame.height - previewSize.height) / 2.0)), size: previewSize) + } else if let previewImageNode = self.previewImageNode { + self.previewImageNode = nil + previewImageNode.removeFromSupernode() + } + + let micButtonSize = CGSize(width: 36.0, height: 36.0) + let micButtonFrame = CGRect(origin: CGPoint(x: size.width - rightInset - 7.0 - micButtonSize.width, y: floor((panelHeight - micButtonSize.height) / 2.0)), size: micButtonSize) + staticTransition.updateFrame(node: self.micButton, frame: micButtonFrame) + staticTransition.updateFrame(node: self.micButtonBackgroundNode, frame: CGRect(origin: CGPoint(), size: micButtonFrame.size)) + + let animationSize = CGSize(width: 36.0, height: 36.0) + staticTransition.updateFrame(node: self.micButtonForegroundNode, frame: CGRect(origin: CGPoint(x: floor((micButtonFrame.width - animationSize.width) / 2.0), y: floor((micButtonFrame.height - animationSize.height) / 2.0)), size: animationSize)) + + var isMuted = true + if let _ = self.callState?.muteState { + isMuted = true + } else { + isMuted = false + } + self.micButtonForegroundNode.update(state: VoiceChatMicrophoneNode.State(muted: isMuted, filled: false, color: UIColor.white), animated: transition.isAnimated) + + if isMuted != self.micButtonBackgroundNodeIsMuted { + self.micButtonBackgroundNodeIsMuted = isMuted + let updatedImage = generateStretchableFilledCircleImage(diameter: 36.0, color: isMuted ? UIColor(rgb: 0xb6b6bb) : UIColor(rgb: 0x30b251)) + + if let updatedImage = updatedImage, let previousImage = self.micButtonBackgroundNode.image?.cgImage, transition.isAnimated { + self.micButtonBackgroundNode.image = updatedImage + self.micButtonBackgroundNode.layer.animate(from: previousImage, to: updatedImage.cgImage!, keyPath: "contents", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.25, delay: 0.0) + } else { + self.micButtonBackgroundNode.image = updatedImage + } + } + + self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(15.0), textColor: self.theme.chat.inputPanel.primaryTextColor) + + self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(13.0), textColor: self.theme.chat.inputPanel.secondaryTextColor) + + var constrainedWidth = size.width - leftInset - rightInset - 32.0 - joinButtonSize.width - 60.0 + if isScheduled { + constrainedWidth = size.width - 100.0 + } + + let titleSize = self.titleNode.updateLayout(CGSize(width: constrainedWidth, height: .greatestFiniteMagnitude)) + let textSize = self.textNode.updateLayout(CGSize(width: size.width, height: .greatestFiniteMagnitude)) + + let titleFrame = CGRect(origin: CGPoint(x: leftInset + 16.0, y: 9.0), size: titleSize) + staticTransition.updateFrame(node: self.titleNode, frame: titleFrame) + staticTransition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: leftInset + 16.0, y: titleFrame.maxY + 1.0), size: textSize)) + + if let image = self.muteIconNode.image { + staticTransition.updateFrame(node: self.muteIconNode, frame: CGRect(origin: CGPoint(x: titleFrame.maxX + 4.0, y: titleFrame.minY + 5.0), size: image.size)) + } + self.muteIconNode.isHidden = self.currentData?.groupCall != nil + self.joinButton.isHidden = self.currentData?.groupCall != nil + self.micButton.isHidden = self.currentData?.groupCall == nil + + transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: UIScreenPixel))) + transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: panelHeight))) + } + + public func animateIn(_ transition: ContainedViewLayoutTransition) { + let contentPosition = self.contentNode.layer.position + transition.animatePosition(node: self.contentNode, from: CGPoint(x: contentPosition.x, y: contentPosition.y - 50.0)) + + guard let (size, _, _, _) = self.validLayout else { + return + } + + transition.animatePositionAdditive(node: self.separatorNode, offset: CGPoint(x: 0.0, y: size.height)) + } + + public func animateOut(_ transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) { + let contentPosition = self.contentNode.layer.position + transition.animatePosition(node: self.contentNode, to: CGPoint(x: contentPosition.x, y: contentPosition.y - 50.0), removeOnCompletion: false, completion: { _ in + completion() + }) + + guard let (size, _, _, _) = self.validLayout else { + return + } + + transition.updatePosition(node: self.separatorNode, position: self.separatorNode.position.offsetBy(dx: 0.0, dy: size.height)) + } + + func rightButtonSnapshotViews() -> (background: UIView, foreground: UIView)? { + if !self.joinButton.isHidden { + if let foregroundView = self.joinButtonTitleNode.view.snapshotContentTree() { + let backgroundFrame = self.joinButtonBackgroundNode.view.convert(self.joinButtonBackgroundNode.bounds, to: nil) + let foregroundFrame = self.joinButtonTitleNode.view.convert(self.joinButtonTitleNode.bounds, to: nil) + + let backgroundView = UIView() + backgroundView.backgroundColor = self.theme.chat.inputPanel.actionControlFillColor + backgroundView.frame = backgroundFrame + backgroundView.layer.cornerRadius = backgroundFrame.height / 2.0 + + foregroundView.frame = foregroundFrame + return (backgroundView, foregroundView) + } + } else if !self.micButton.isHidden { + if let foregroundView = self.micButtonForegroundNode.view.snapshotContentTree() { + let backgroundFrame = self.micButtonBackgroundNode.view.convert(self.micButtonBackgroundNode.bounds, to: nil) + let foregroundFrame = self.micButtonForegroundNode.view.convert(self.micButtonForegroundNode.bounds, to: nil) + + let backgroundView = UIView() + backgroundView.backgroundColor = (self.micButtonBackgroundNodeIsMuted ?? true) ? UIColor(rgb: 0xb6b6bb) : UIColor(rgb: 0x30b251) + backgroundView.frame = backgroundFrame + backgroundView.layer.cornerRadius = backgroundFrame.height / 2.0 + + foregroundView.frame = foregroundFrame + return (backgroundView, foregroundView) + } + } + + return nil + } +} diff --git a/submodules/TelegramUI/Components/GroupStickerPackSetupController/BUILD b/submodules/TelegramUI/Components/GroupStickerPackSetupController/BUILD index 8b9993c9..442a3f6a 100644 --- a/submodules/TelegramUI/Components/GroupStickerPackSetupController/BUILD +++ b/submodules/TelegramUI/Components/GroupStickerPackSetupController/BUILD @@ -24,6 +24,11 @@ swift_library( "//submodules/SearchUI:SearchUI", "//submodules/MergeLists:MergeLists", "//submodules/UndoUI:UndoUI", + "//submodules/TelegramUI/Components/GlassBackgroundComponent", + "//submodules/ComponentFlow", + "//submodules/Components/ComponentDisplayAdapters", + "//submodules/AppBundle", + "//submodules/ActivityIndicator", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/GroupStickerPackSetupController/Sources/GroupStickerPackCurrentItem.swift b/submodules/TelegramUI/Components/GroupStickerPackSetupController/Sources/GroupStickerPackCurrentItem.swift index a536dab8..622412a7 100644 --- a/submodules/TelegramUI/Components/GroupStickerPackSetupController/Sources/GroupStickerPackCurrentItem.swift +++ b/submodules/TelegramUI/Components/GroupStickerPackSetupController/Sources/GroupStickerPackCurrentItem.swift @@ -170,7 +170,7 @@ class GroupStickerPackCurrentItemNode: ItemListRevealOptionsItemNode { self.removeButtonIcon = ASImageNode() self.removeButtonIcon.displaysAsynchronously = false - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) if let placeholderNode = self.placeholderNode { self.addSubnode(placeholderNode) diff --git a/submodules/TelegramUI/Components/GroupStickerPackSetupController/Sources/GroupStickerSearchItem.swift b/submodules/TelegramUI/Components/GroupStickerPackSetupController/Sources/GroupStickerSearchItem.swift index 7f275d26..4b215b5e 100644 --- a/submodules/TelegramUI/Components/GroupStickerPackSetupController/Sources/GroupStickerSearchItem.swift +++ b/submodules/TelegramUI/Components/GroupStickerPackSetupController/Sources/GroupStickerSearchItem.swift @@ -105,8 +105,8 @@ private final class GroupStickerSearchItemNode: ItemListControllerSearchNode { } override func updateLayout(layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { - transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight), size: CGSize(width: layout.size.width, height: layout.size.height - navigationBarHeight))) - self.containerNode.containerLayoutUpdated(layout.withUpdatedSize(CGSize(width: layout.size.width, height: layout.size.height - navigationBarHeight)), navigationBarHeight: 0.0, transition: transition) + transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height))) + self.containerNode.containerLayoutUpdated(layout.withUpdatedSize(CGSize(width: layout.size.width, height: layout.size.height)), navigationBarHeight: navigationBarHeight, transition: transition) } override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { diff --git a/submodules/TelegramUI/Components/GroupStickerPackSetupController/Sources/GroupStickerSearchNavigationContentNode.swift b/submodules/TelegramUI/Components/GroupStickerPackSetupController/Sources/GroupStickerSearchNavigationContentNode.swift index 408019f9..d8dcb0c7 100644 --- a/submodules/TelegramUI/Components/GroupStickerPackSetupController/Sources/GroupStickerSearchNavigationContentNode.swift +++ b/submodules/TelegramUI/Components/GroupStickerPackSetupController/Sources/GroupStickerSearchNavigationContentNode.swift @@ -8,34 +8,94 @@ import TelegramPresentationData import ItemListUI import PresentationDataUtils import SearchBarNode +import GlassBackgroundComponent +import ComponentFlow +import ComponentDisplayAdapters +import AppBundle +import ActivityIndicator private let searchBarFont = Font.regular(17.0) final class GroupStickerSearchNavigationContentNode: NavigationBarContentNode, ItemListControllerSearchNavigationContentNode { + private struct Params: Equatable { + let size: CGSize + let leftInset: CGFloat + let rightInset: CGFloat + + init(size: CGSize, leftInset: CGFloat, rightInset: CGFloat) { + self.size = size + self.leftInset = leftInset + self.rightInset = rightInset + } + } + private var theme: PresentationTheme private let strings: PresentationStrings private let cancel: () -> Void + private let backgroundContainer: GlassBackgroundContainerView + private let backgroundView: GlassBackgroundView + private let iconView: UIImageView + private var activityIndicator: ActivityIndicator? private let searchBar: SearchBarNode + private let close: (background: GlassBackgroundView, icon: UIImageView) + + private var params: Params? private var queryUpdated: ((String) -> Void)? var activity: Bool = false { didSet { - self.searchBar.activity = activity + if self.activity != oldValue { + if let params = self.params { + let _ = self.updateLayout(size: params.size, leftInset: params.leftInset, rightInset: params.rightInset, transition: .immediate) + } + } } } + init(theme: PresentationTheme, strings: PresentationStrings, cancel: @escaping () -> Void, updateActivity: @escaping(@escaping(Bool)->Void) -> Void) { self.theme = theme self.strings = strings self.cancel = cancel - self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: theme, hasSeparator: false), strings: strings, fieldStyle: .modern, displayBackground: false) + self.backgroundContainer = GlassBackgroundContainerView() + self.backgroundView = GlassBackgroundView() + self.backgroundContainer.contentView.addSubview(self.backgroundView) + self.iconView = UIImageView() + self.backgroundView.contentView.addSubview(self.iconView) + + self.close = (GlassBackgroundView(), UIImageView()) + self.close.background.contentView.addSubview(self.close.icon) + + self.searchBar = SearchBarNode( + theme: SearchBarNodeTheme( + background: .clear, + separator: .clear, + inputFill: .clear, + primaryText: theme.chat.inputPanel.panelControlColor, + placeholder: theme.chat.inputPanel.inputPlaceholderColor, + inputIcon: theme.chat.inputPanel.inputControlColor, + inputClear: theme.chat.inputPanel.panelControlColor, + accent: theme.chat.inputPanel.panelControlAccentColor, + keyboard: theme.rootController.keyboardColor + ), + presentationTheme: theme, + strings: strings, + fieldStyle: .inlineNavigation, + forceSeparator: false, + displayBackground: false, + cancelText: nil + ) super.init() - self.addSubnode(self.searchBar) + self.view.addSubview(self.backgroundContainer) + self.backgroundView.contentView.addSubview(self.searchBar.view) + + self.backgroundContainer.contentView.addSubview(self.close.background) + self.close.background.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.onCloseTapGesture(_:)))) self.searchBar.cancel = { [weak self] in self?.searchBar.deactivate(clear: false) @@ -47,19 +107,28 @@ final class GroupStickerSearchNavigationContentNode: NavigationBarContentNode, I } updateActivity({ [weak self] value in - self?.activity = value + guard let self else { + return + } + self.activity = value }) self.updatePlaceholder() } + @objc private func onCloseTapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + self.searchBar.cancel?() + } + } + func setQueryUpdated(_ f: @escaping (String) -> Void) { self.queryUpdated = f } func updateTheme(_ theme: PresentationTheme) { self.theme = theme - self.searchBar.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: self.theme), strings: self.strings) + self.searchBar.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: self.theme), presentationTheme: self.theme, strings: self.strings) self.updatePlaceholder() } @@ -68,13 +137,87 @@ final class GroupStickerSearchNavigationContentNode: NavigationBarContentNode, I } override var nominalHeight: CGFloat { - return 54.0 + return 60.0 } - override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { - let searchBarFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - self.nominalHeight), size: CGSize(width: size.width, height: 54.0)) - self.searchBar.frame = searchBarFrame - self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition) + override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { + self.params = Params(size: size, leftInset: leftInset, rightInset: rightInset) + + let transition = ComponentTransition(transition) + + let backgroundFrame = CGRect(origin: CGPoint(x: leftInset + 16.0, y: 6.0), size: CGSize(width: size.width - 16.0 * 2.0 - leftInset - rightInset - 44.0 - 8.0, height: 44.0)) + let closeFrame = CGRect(origin: CGPoint(x: size.width - 16.0 - rightInset - 44.0, y: backgroundFrame.minY), size: CGSize(width: 44.0, height: 44.0)) + + transition.setFrame(view: self.backgroundContainer, frame: CGRect(origin: CGPoint(), size: size)) + self.backgroundContainer.update(size: size, isDark: self.theme.overallDarkAppearance, transition: transition) + + transition.setFrame(view: self.backgroundView, frame: backgroundFrame) + self.backgroundView.update(size: backgroundFrame.size, cornerRadius: backgroundFrame.height * 0.5, isDark: self.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition) + + if self.iconView.image == nil { + self.iconView.image = UIImage(bundleImageName: "Navigation/Search")?.withRenderingMode(.alwaysTemplate) + } + transition.setTintColor(view: self.iconView, color: self.theme.rootController.navigationSearchBar.inputIconColor) + + if let image = self.iconView.image { + let imageSize: CGSize + let iconFrame: CGRect + let iconFraction: CGFloat = 0.8 + imageSize = CGSize(width: image.size.width * iconFraction, height: image.size.height * iconFraction) + iconFrame = CGRect(origin: CGPoint(x: 12.0, y: floor((backgroundFrame.height - imageSize.height) * 0.5)), size: imageSize) + transition.setPosition(view: self.iconView, position: iconFrame.center) + transition.setBounds(view: self.iconView, bounds: CGRect(origin: CGPoint(), size: iconFrame.size)) + } + + if self.activity { + let activityIndicator: ActivityIndicator + if let current = self.activityIndicator { + activityIndicator = current + } else { + activityIndicator = ActivityIndicator(type: .custom(self.theme.chat.inputPanel.inputControlColor, 14.0, 14.0, false)) + self.activityIndicator = activityIndicator + self.backgroundView.contentView.addSubview(activityIndicator.view) + } + let indicatorSize = activityIndicator.measure(CGSize(width: 32.0, height: 32.0)) + let indicatorFrame = CGRect(origin: CGPoint(x: 15.0, y: floorToScreenPixels((backgroundFrame.height - indicatorSize.height) * 0.5)), size: indicatorSize) + transition.setPosition(view: activityIndicator.view, position: indicatorFrame.center) + transition.setBounds(view: activityIndicator.view, bounds: CGRect(origin: CGPoint(), size: indicatorFrame.size)) + } else if let activityIndicator = self.activityIndicator { + self.activityIndicator = nil + activityIndicator.view.removeFromSuperview() + } + self.iconView.isHidden = self.activity + + let searchBarFrame = CGRect(origin: CGPoint(x: 36.0, y: 0.0), size: CGSize(width: backgroundFrame.width - 36.0 - 4.0, height: 44.0)) + transition.setFrame(view: self.searchBar.view, frame: searchBarFrame) + self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: 0.0, rightInset: 0.0, transition: transition.containedViewLayoutTransition) + + if self.close.icon.image == nil { + self.close.icon.image = generateImage(CGSize(width: 40.0, height: 40.0), contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + context.setLineWidth(2.0) + context.setLineCap(.round) + context.setStrokeColor(UIColor.white.cgColor) + + context.beginPath() + context.move(to: CGPoint(x: 12.0, y: 12.0)) + context.addLine(to: CGPoint(x: size.width - 12.0, y: size.height - 12.0)) + context.move(to: CGPoint(x: size.width - 12.0, y: 12.0)) + context.addLine(to: CGPoint(x: 12.0, y: size.height - 12.0)) + context.strokePath() + })?.withRenderingMode(.alwaysTemplate) + } + + if let image = close.icon.image { + self.close.icon.frame = image.size.centered(in: CGRect(origin: CGPoint(), size: closeFrame.size)) + } + self.close.icon.tintColor = self.theme.chat.inputPanel.panelControlColor + + transition.setFrame(view: self.close.background, frame: closeFrame) + self.close.background.update(size: closeFrame.size, cornerRadius: closeFrame.height * 0.5, isDark: self.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition) + + return size } func activate() { @@ -85,4 +228,3 @@ final class GroupStickerSearchNavigationContentNode: NavigationBarContentNode, I self.searchBar.deactivate(clear: false) } } - diff --git a/submodules/TelegramUI/Components/HeaderPanelContainerComponent/BUILD b/submodules/TelegramUI/Components/HeaderPanelContainerComponent/BUILD new file mode 100644 index 00000000..c509cc4c --- /dev/null +++ b/submodules/TelegramUI/Components/HeaderPanelContainerComponent/BUILD @@ -0,0 +1,22 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "HeaderPanelContainerComponent", + module_name = "HeaderPanelContainerComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Display", + "//submodules/ComponentFlow", + "//submodules/TelegramPresentationData", + "//submodules/Components/ComponentDisplayAdapters", + "//submodules/TelegramUI/Components/GlassBackgroundComponent", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/HeaderPanelContainerComponent/Sources/HeaderPanelContainerComponent.swift b/submodules/TelegramUI/Components/HeaderPanelContainerComponent/Sources/HeaderPanelContainerComponent.swift new file mode 100644 index 00000000..b48bdf91 --- /dev/null +++ b/submodules/TelegramUI/Components/HeaderPanelContainerComponent/Sources/HeaderPanelContainerComponent.swift @@ -0,0 +1,267 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import TelegramPresentationData +import GlassBackgroundComponent + +public protocol HeaderPanelContainerChildView: UIView { + func setOverlayContainerView(overlayContainerView: UIView) +} + +public final class HeaderPanelContainerComponent: Component { + public final class Panel: Equatable { + public let key: AnyHashable + public let orderIndex: Int + public let component: AnyComponent + + public init(key: AnyHashable, orderIndex: Int, component: AnyComponent) { + self.key = key + self.orderIndex = orderIndex + self.component = component + } + + public static func ==(lhs: Panel, rhs: Panel) -> Bool { + if lhs.key != rhs.key { + return false + } + if lhs.orderIndex != rhs.orderIndex { + return false + } + if lhs.component != rhs.component { + return false + } + return true + } + } + + public let theme: PresentationTheme + public let tabs: AnyComponent? + public let panels: [Panel] + + public init( + theme: PresentationTheme, + tabs: AnyComponent?, + panels: [Panel] + ) { + self.theme = theme + self.tabs = tabs + self.panels = panels + } + + public static func ==(lhs: HeaderPanelContainerComponent, rhs: HeaderPanelContainerComponent) -> Bool { + if lhs.theme !== rhs.theme { + return false + } + if lhs.tabs != rhs.tabs { + return false + } + if lhs.panels != rhs.panels { + return false + } + return true + } + + private final class PanelItemView: UIView { + let view = ComponentView() + let separator = SimpleLayer() + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + } + + public final class View: UIView { + private let backgroundContainer: GlassBackgroundContainerView + private let backgroundView: GlassBackgroundView + private let contentContainer: UIView + + private var tabsView: ComponentView? + private var panelViews: [AnyHashable: PanelItemView] = [:] + + private var component: HeaderPanelContainerComponent? + private weak var state: EmptyComponentState? + + public var tabs: UIView? { + return self.tabsView?.view + } + + public func panel(forKey key: AnyHashable) -> UIView? { + return self.panelViews[key]?.view.view + } + + override init(frame: CGRect) { + self.backgroundContainer = GlassBackgroundContainerView() + self.backgroundView = GlassBackgroundView() + self.contentContainer = UIView() + self.contentContainer.clipsToBounds = true + + super.init(frame: frame) + + self.backgroundContainer.contentView.addSubview(self.backgroundView) + self.addSubview(self.backgroundContainer) + + self.backgroundView.contentView.addSubview(self.contentContainer) + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(component: HeaderPanelContainerComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + var isAnimatingReplacement = false + if let previousComponent = self.component { + isAnimatingReplacement = !component.panels.contains(where: { panel in previousComponent.panels.contains(where: { $0.key == panel.key }) }) + } + + self.component = component + self.state = state + + let sideInset: CGFloat = 16.0 + + var size = CGSize(width: availableSize.width, height: 0.0) + + var isFirstPanel = true + + if let tabs = component.tabs { + let tabsView: ComponentView + var tabsTransition = transition + if let current = self.tabsView { + tabsView = current + } else { + tabsTransition = tabsTransition.withAnimation(.none) + tabsView = ComponentView() + self.tabsView = tabsView + } + let tabsSize = tabsView.update( + transition: tabsTransition, + component: tabs, + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 40.0) + ) + let tabsFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height), size: tabsSize) + if let tabsComponentView = tabsView.view { + if tabsComponentView.superview == nil { + self.contentContainer.addSubview(tabsComponentView) + if let tabsComponentView = tabsComponentView as? HeaderPanelContainerChildView { + tabsComponentView.setOverlayContainerView(overlayContainerView: self.backgroundContainer.contentView) + } + transition.animateAlpha(view: tabsComponentView, from: 0.0, to: 1.0) + } + tabsTransition.setFrame(view: tabsComponentView, frame: tabsFrame) + } + size.height += tabsSize.height + isFirstPanel = false + } else if let tabsView = self.tabsView { + self.tabsView = nil + if let tabsComponentView = tabsView.view { + transition.setAlpha(view: tabsComponentView, alpha: 0.0, completion: { [weak tabsComponentView] _ in + tabsComponentView?.removeFromSuperview() + }) + } + } + + var validPanelKeys: [AnyHashable] = [] + for panel in component.panels { + validPanelKeys.append(panel.key) + + var panelTransition = transition + let panelView: PanelItemView + if let current = self.panelViews[panel.key] { + panelView = current + } else { + panelTransition = panelTransition.withAnimation(.none) + panelView = PanelItemView() + self.panelViews[panel.key] = panelView + self.contentContainer.layer.insertSublayer(panelView.separator, at: 0) + self.contentContainer.addSubview(panelView) + } + + let panelSize = panelView.view.update( + transition: panelTransition, + component: panel.component, + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 10000.0) + ) + let panelFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height), size: panelSize) + if let panelComponentView = panelView.view.view { + if panelComponentView.superview == nil { + panelView.addSubview(panelComponentView) + transition.animateAlpha(view: panelView, from: 0.0, to: 1.0) + panelView.separator.opacity = 0.0 + panelView.clipsToBounds = true + if isAnimatingReplacement { + panelView.frame = panelFrame + } else { + panelView.frame = CGRect(origin: panelFrame.origin, size: CGSize(width: panelFrame.width, height: 0.0)) + } + } + + panelView.separator.backgroundColor = component.theme.list.itemPlainSeparatorColor.cgColor + + let isFrameUpdated = panelComponentView.frame != panelFrame + transition.setFrame(view: panelView, frame: panelFrame, completion: { [weak panelView] completed in + if let panelView, completed, isFrameUpdated { + panelView.clipsToBounds = false + } + }) + panelTransition.setFrame(view: panelComponentView, frame: CGRect(origin: CGPoint(), size: panelFrame.size)) + panelTransition.setFrame(layer: panelView.separator, frame: CGRect(origin: panelFrame.origin, size: CGSize(width: panelFrame.width, height: UIScreenPixel))) + + transition.setAlpha(layer: panelView.separator, alpha: isFirstPanel ? 0.0 : 1.0) + } + size.height += panelSize.height + isFirstPanel = false + } + + var removedPanelKeys: [AnyHashable] = [] + for (key, panelView) in self.panelViews { + if !validPanelKeys.contains(key) { + removedPanelKeys.append(key) + transition.setAlpha(view: panelView, alpha: 0.0, completion: { [weak panelView] _ in + panelView?.removeFromSuperview() + }) + let separator = panelView.separator + transition.setAlpha(layer: separator, alpha: 0.0, completion: { [weak separator] _ in + separator?.removeFromSuperlayer() + }) + if !isAnimatingReplacement { + panelView.clipsToBounds = true + transition.setFrame(view: panelView, frame: CGRect(origin: panelView.frame.origin, size: CGSize(width: panelView.bounds.width, height: 0.0))) + } + } + } + for key in removedPanelKeys { + self.panelViews.removeValue(forKey: key) + } + + let backgroundSize = CGSize(width: size.width, height: max(40.0, size.height)) + + transition.setFrame(view: self.backgroundContainer, frame: CGRect(origin: CGPoint(), size: backgroundSize)) + self.backgroundContainer.update(size: backgroundSize, isDark: component.theme.overallDarkAppearance, transition: transition) + + let backgroundFrame = CGRect(origin: CGPoint(x: sideInset, y: 0.0), size: CGSize(width: size.width - sideInset * 2.0, height: backgroundSize.height)) + transition.setFrame(view: self.backgroundView, frame: backgroundFrame) + self.backgroundView.update(size: backgroundFrame.size, cornerRadius: 20.0, isDark: component.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: component.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition) + + transition.setAlpha(view: self.backgroundContainer, alpha: (component.tabs != nil || !component.panels.isEmpty) ? 1.0 : 0.0) + + transition.setFrame(view: self.contentContainer, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size)) + self.contentContainer.layer.cornerRadius = 20.0 + + return size + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/HorizontalTabsComponent/BUILD b/submodules/TelegramUI/Components/HorizontalTabsComponent/BUILD new file mode 100644 index 00000000..7f31dd2d --- /dev/null +++ b/submodules/TelegramUI/Components/HorizontalTabsComponent/BUILD @@ -0,0 +1,29 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "HorizontalTabsComponent", + module_name = "HorizontalTabsComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Display", + "//submodules/ComponentFlow", + "//submodules/TelegramPresentationData", + "//submodules/AccountContext", + "//submodules/TelegramCore", + "//submodules/AsyncDisplayKit", + "//submodules/AnimationUI", + "//submodules/Components/ComponentDisplayAdapters", + "//submodules/Components/MultilineTextWithEntitiesComponent", + "//submodules/TelegramUI/Components/LiquidLens", + "//submodules/TelegramUI/Components/TextBadgeComponent", + "//submodules/TelegramUI/Components/HeaderPanelContainerComponent", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/HorizontalTabsComponent/Sources/HorizontalTabsComponent.swift b/submodules/TelegramUI/Components/HorizontalTabsComponent/Sources/HorizontalTabsComponent.swift new file mode 100644 index 00000000..789b2613 --- /dev/null +++ b/submodules/TelegramUI/Components/HorizontalTabsComponent/Sources/HorizontalTabsComponent.swift @@ -0,0 +1,1270 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import TelegramPresentationData +import AccountContext +import TelegramCore +import MultilineTextWithEntitiesComponent +import TextBadgeComponent +import LiquidLens +import HeaderPanelContainerComponent + +private class ReorderingGestureRecognizerTimerTarget: NSObject { + private let f: () -> Void + + init(_ f: @escaping () -> Void) { + self.f = f + + super.init() + } + + @objc func timerEvent() { + self.f() + } +} + +private final class InternalGestureRecognizerDelegate: NSObject, UIGestureRecognizerDelegate { + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool { + if otherGestureRecognizer is UIPanGestureRecognizer { + return true + } else { + return false + } + } +} + +private final class ReorderingGestureRecognizer: UIGestureRecognizer, UIGestureRecognizerDelegate { + private let internalDelegate = InternalGestureRecognizerDelegate() + + private let shouldBegin: (CGPoint) -> Bool + private let began: (CGPoint) -> Void + private let ended: () -> Void + private let moved: (CGFloat) -> Void + + private var initialLocation: CGPoint? + private var delayTimer: Foundation.Timer? + + var currentLocation: CGPoint? + + init(shouldBegin: @escaping (CGPoint) -> Bool, began: @escaping (CGPoint) -> Void, ended: @escaping () -> Void, moved: @escaping (CGFloat) -> Void) { + self.shouldBegin = shouldBegin + self.began = began + self.ended = ended + self.moved = moved + + super.init(target: nil, action: nil) + + self.delegate = self.internalDelegate + } + + override func reset() { + super.reset() + + self.initialLocation = nil + self.delayTimer?.invalidate() + self.delayTimer = nil + self.currentLocation = nil + } + + override func touchesBegan(_ touches: Set, with event: UIEvent) { + super.touchesBegan(touches, with: event) + + guard let location = touches.first?.location(in: self.view) else { + self.state = .failed + return + } + + if self.state == .possible { + if self.delayTimer == nil { + if !self.shouldBegin(location) { + self.state = .failed + return + } + self.initialLocation = location + let timer = Foundation.Timer(timeInterval: 0.2, target: ReorderingGestureRecognizerTimerTarget { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.delayTimer = nil + strongSelf.state = .began + strongSelf.began(location) + }, selector: #selector(ReorderingGestureRecognizerTimerTarget.timerEvent), userInfo: nil, repeats: false) + self.delayTimer = timer + RunLoop.main.add(timer, forMode: .common) + } else { + self.state = .failed + } + } + } + + override func touchesEnded(_ touches: Set, with event: UIEvent) { + super.touchesEnded(touches, with: event) + + self.delayTimer?.invalidate() + + if self.state == .began || self.state == .changed { + self.ended() + } + + self.state = .failed + } + + override func touchesCancelled(_ touches: Set, with event: UIEvent) { + super.touchesCancelled(touches, with: event) + + if self.state == .began || self.state == .changed { + self.delayTimer?.invalidate() + self.ended() + self.state = .failed + } + } + + override func touchesMoved(_ touches: Set, with event: UIEvent) { + super.touchesMoved(touches, with: event) + + guard let initialLocation = self.initialLocation, let location = touches.first?.location(in: self.view) else { + return + } + let offset = location.x - initialLocation.x + self.currentLocation = location + + if self.delayTimer != nil { + if abs(offset) > 4.0 { + self.delayTimer?.invalidate() + self.state = .failed + return + } + } else { + if self.state == .began || self.state == .changed { + self.state = .changed + self.moved(offset) + } + } + } +} + + +public final class HorizontalTabsComponent: Component { + public final class Tab: Equatable { + public typealias Id = AnyHashable + + public struct Badge: Equatable { + public var title: String + public var isAccent: Bool + + public init(title: String, isAccent: Bool) { + self.title = title + self.isAccent = isAccent + } + } + + public struct Title: Equatable { + public let text: String + public let entities: [MessageTextEntity] + public let enableAnimations: Bool + + public init(text: String, entities: [MessageTextEntity], enableAnimations: Bool) { + self.text = text + self.entities = entities + self.enableAnimations = enableAnimations + } + } + + public enum Content: Equatable { + case title(Title) + case custom(AnyComponent) + } + + public let id: AnyHashable + public let content: Content + public let badge: Badge? + public let action: () -> Void + public let contextAction: ((ContextExtractedContentContainingView, ContextGesture?) -> Void)? + public let deleteAction: (() -> Void)? + + public init(id: AnyHashable, content: Content, badge: Badge?, action: @escaping () -> Void, contextAction: ((ContextExtractedContentContainingView, ContextGesture?) -> Void)?, deleteAction: (() -> Void)?) { + self.id = id + self.content = content + self.badge = badge + self.action = action + self.contextAction = contextAction + self.deleteAction = deleteAction + } + + public static func ==(lhs: Tab, rhs: Tab) -> Bool { + if lhs.id != rhs.id { + return false + } + if lhs.content != rhs.content { + return false + } + if lhs.badge != rhs.badge { + return false + } + if (lhs.contextAction == nil) != (rhs.contextAction == nil) { + return false + } + if (lhs.deleteAction == nil) != (rhs.deleteAction == nil) { + return false + } + return true + } + } + + public enum Layout { + case fit + case fill + } + + public let context: AccountContext? + public let theme: PresentationTheme + public let tabs: [Tab] + public let selectedTab: Tab.Id? + public let isEditing: Bool + public let layout: Layout + public let liftWhileSwitching: Bool + + public init( + context: AccountContext?, + theme: PresentationTheme, + tabs: [Tab], + selectedTab: Tab.Id?, + isEditing: Bool, + layout: Layout = .fill, + liftWhileSwitching: Bool = true + ) { + self.context = context + self.theme = theme + self.tabs = tabs + self.selectedTab = selectedTab + self.isEditing = isEditing + self.layout = layout + self.liftWhileSwitching = liftWhileSwitching + } + + public static func ==(lhs: HorizontalTabsComponent, rhs: HorizontalTabsComponent) -> Bool { + if lhs.theme !== rhs.theme { + return false + } + if lhs.tabs != rhs.tabs { + return false + } + if lhs.selectedTab != rhs.selectedTab { + return false + } + if lhs.isEditing != rhs.isEditing { + return false + } + if lhs.layout != rhs.layout { + return false + } + if lhs.liftWhileSwitching != rhs.liftWhileSwitching { + return false + } + return true + } + + private final class ScrollView: UIScrollView { + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + return super.hitTest(point, with: event) + } + + override func touchesShouldCancel(in view: UIView) -> Bool { + return true + } + } + + private struct LayoutData { + var size: CGSize + var selectedItemFrame: CGRect + + init(size: CGSize, selectedItemFrame: CGRect) { + self.size = size + self.selectedItemFrame = selectedItemFrame + } + } + + private final class ItemView { + var frame: CGRect = CGRect() + var selectionFrame: CGRect = CGRect() + let regularView = ComponentView() + let selectedView = ComponentView() + + init() { + } + } + + public final class View: UIView, UIScrollViewDelegate, HeaderPanelContainerChildView { + private let lensView: LiquidLensView + private let scrollView: ScrollView + private let selectedScrollView: UIView + private var itemViews: [Tab.Id: ItemView] = [:] + + private var ignoreScrolling: Bool = false + private var tabSwitchFraction: CGFloat = 0.0 + private var isDraggingTabs: Bool = false + private var temporaryLiftTimer: Foundation.Timer? + + private var tapRecognizer: UITapGestureRecognizer? + + private var reorderingGesture: ReorderingGestureRecognizer? + private var reorderingItem: AnyHashable? + private var reorderingItemPosition: (initial: CGFloat, offset: CGFloat)? + private var reorderingAutoScrollAnimator: ConstantDisplayLinkAnimator? + private var initialReorderedItemIds: [AnyHashable]? + public private(set) var reorderedItemIds: [AnyHashable]? + + private var layoutData: LayoutData? + + private var component: HorizontalTabsComponent? + private weak var state: EmptyComponentState? + private var isUpdating: Bool = false + + override init(frame: CGRect) { + self.lensView = LiquidLensView(kind: .noContainer) + self.scrollView = ScrollView() + + self.selectedScrollView = UIView() + self.selectedScrollView.clipsToBounds = true + + super.init(frame: frame) + + self.scrollView.delaysContentTouches = false + self.scrollView.canCancelContentTouches = true + self.scrollView.contentInsetAdjustmentBehavior = .never + self.scrollView.automaticallyAdjustsScrollIndicatorInsets = false + self.scrollView.showsVerticalScrollIndicator = false + self.scrollView.showsHorizontalScrollIndicator = false + self.scrollView.alwaysBounceHorizontal = false + self.scrollView.alwaysBounceVertical = false + self.scrollView.scrollsToTop = false + self.scrollView.clipsToBounds = true + self.scrollView.delegate = self + + self.scrollView.disablesInteractiveTransitionGestureRecognizerNow = { [weak self] in + guard let self else { + return false + } + return self.scrollView.contentOffset.x > .ulpOfOne + } + + self.addSubview(self.lensView) + + self.lensView.contentView.addSubview(self.scrollView) + self.lensView.selectedContentView.addSubview(self.selectedScrollView) + /*self.lensView.onUpdatedIsAnimating = { [weak self] _ in + guard let self else { + return + } + self.alpha = self.lensView.isAnimating ? 1.0 : 0.7 + }*/ + /*self.lensView.isLiftedAnimationCompleted = { [weak self] in + guard let self else { + return + } + if let temporaryLiftTimer = self.temporaryLiftTimer { + let _ = temporaryLiftTimer + /*self.temporaryLiftTimer = nil + temporaryLiftTimer.invalidate() + if !self.isUpdating { + self.state?.updated(transition: .spring(duration: 0.5)) + }*/ + } + }*/ + + let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.onTapGesture(_:))) + self.tapRecognizer = tapRecognizer + self.addGestureRecognizer(tapRecognizer) + + let reorderingGesture = ReorderingGestureRecognizer(shouldBegin: { [weak self] point in + guard let self else { + return false + } + for (_, itemView) in self.itemViews { + guard let itemView = itemView.regularView.view else { + continue + } + if itemView.convert(itemView.bounds, to: self).contains(point) { + return true + } + } + return false + }, began: { [weak self] point in + guard let self else { + return + } + self.initialReorderedItemIds = self.reorderedItemIds + for (id, itemView) in self.itemViews { + guard let regularItemView = itemView.regularView.view, let selectedItemView = itemView.selectedView.view else { + continue + } + let itemFrame = regularItemView.convert(regularItemView.bounds, to: self) + if itemFrame.contains(point) { + HapticFeedback().impact() + + self.reorderingItem = id + regularItemView.frame = itemFrame + selectedItemView.frame = itemFrame + + self.reorderingAutoScrollAnimator = ConstantDisplayLinkAnimator(update: { [weak self] in + guard let self, let currentLocation = self.reorderingGesture?.currentLocation else { + return + } + let edgeWidth: CGFloat = 20.0 + if currentLocation.x <= edgeWidth { + var contentOffset = self.scrollView.contentOffset + contentOffset.x = max(0.0, contentOffset.x - 3.0) + self.scrollView.setContentOffset(contentOffset, animated: false) + } else if currentLocation.x >= self.bounds.width - edgeWidth { + var contentOffset = self.scrollView.contentOffset + contentOffset.x = max(0.0, min(self.scrollView.contentSize.width - self.scrollView.bounds.width, contentOffset.x + 3.0)) + self.scrollView.setContentOffset(contentOffset, animated: false) + } + }) + self.reorderingAutoScrollAnimator?.isPaused = false + self.addSubview(regularItemView) + self.addSubview(selectedItemView) + + self.reorderingItemPosition = (regularItemView.frame.minX, 0.0) + self.state?.updated(transition: .easeInOut(duration: 0.25)) + + return + } + } + }, ended: { [weak self] in + guard let self, let reorderingItem = self.reorderingItem else { + return + } + + if let itemView = self.itemViews[reorderingItem], let regularItemView = itemView.regularView.view, let selectedItemView = itemView.selectedView.view { + let projectedItemFrame = regularItemView.convert(regularItemView.bounds, to: self.scrollView) + regularItemView.frame = projectedItemFrame + selectedItemView.frame = projectedItemFrame + self.scrollView.addSubview(regularItemView) + self.selectedScrollView.addSubview(selectedItemView) + } + + /*if strongSelf.currentParams?.canReorderAllChats == false, let firstItem = strongSelf.reorderedItemIds?.first, case .filter = firstItem { + strongSelf.reorderedItemIds = strongSelf.initialReorderedItemIds + strongSelf.presentPremiumTip?() + }*/ + + self.reorderingItem = nil + self.reorderingItemPosition = nil + self.reorderingAutoScrollAnimator?.invalidate() + self.reorderingAutoScrollAnimator = nil + + self.state?.updated(transition: .easeInOut(duration: 0.25)) + }, moved: { [weak self] offset in + guard let self, let reorderingItem = self.reorderingItem else { + return + } + + let minIndex = 0 + if let reorderingItemView = self.itemViews[reorderingItem], let regularItemView = reorderingItemView.regularView.view, let _ = reorderingItemView.selectedView.view, let (initial, _) = self.reorderingItemPosition, let reorderedItemIds = self.reorderedItemIds, let currentItemIndex = reorderedItemIds.firstIndex(of: reorderingItem) { + + for (id, otherItemView) in self.itemViews { + guard let itemIndex = reorderedItemIds.firstIndex(of: id) else { + continue + } + guard let otherRegularItemView = otherItemView.regularView.view else { + continue + } + if id != reorderingItem { + let itemFrame = otherRegularItemView.convert(otherRegularItemView.bounds, to: self) + if regularItemView.frame.intersects(itemFrame) { + let targetIndex: Int + if regularItemView.frame.midX < itemFrame.midX { + targetIndex = max(minIndex, itemIndex - 1) + } else { + targetIndex = max(minIndex, min(reorderedItemIds.count - 1, itemIndex)) + } + if targetIndex != currentItemIndex { + HapticFeedback().tap() + + var updatedReorderedItemIds = reorderedItemIds + if targetIndex > currentItemIndex { + updatedReorderedItemIds.insert(reorderingItem, at: targetIndex + 1) + updatedReorderedItemIds.remove(at: currentItemIndex) + } else { + updatedReorderedItemIds.remove(at: currentItemIndex) + updatedReorderedItemIds.insert(reorderingItem, at: targetIndex) + } + self.reorderedItemIds = updatedReorderedItemIds + + self.state?.updated(transition: .easeInOut(duration: 0.25)) + } + break + } + } + } + + self.reorderingItemPosition = (initial, offset) + } + + self.state?.updated(transition: .immediate) + }) + self.reorderingGesture = reorderingGesture + self.addGestureRecognizer(reorderingGesture) + reorderingGesture.isEnabled = false + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public func setOverlayContainerView(overlayContainerView: UIView) { + self.lensView.setLiftedContainer(view: overlayContainerView) + } + + override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + return self.scrollView.hitTest(self.convert(point, to: self.scrollView), with: event) + } + + @objc private func onTapGesture(_ recognizer: UITapGestureRecognizer) { + guard let component = self.component else { + return + } + if case .ended = recognizer.state { + let point = recognizer.location(in: self) + for (id, itemView) in self.itemViews { + if self.scrollView.convert(itemView.selectionFrame, to: self).contains(point) { + if let tab = component.tabs.first(where: { $0.id == id }) { + tab.action() + } + } + } + } + } + + public func updateTabSwitchFraction(fraction: CGFloat, isDragging: Bool, transition: ComponentTransition) { + self.tabSwitchFraction = -fraction + self.isDraggingTabs = isDragging + self.state?.updated(transition: transition, isLocal: true) + + /*if self.isDraggingTabs != isDragging { + self.isDraggingTabs = isDragging + + if !isDragging { + self.temporaryLiftTimer = Foundation.Timer.scheduledTimer(withTimeInterval: 0.25, repeats: false, block: { [weak self] timer in + guard let self else { + return + } + if self.temporaryLiftTimer === timer { + self.temporaryLiftTimer = nil + self.state?.updated(transition: .spring(duration: 0.5)) + } + }) + } else { + self.state?.updated(transition: .spring(duration: 0.4), isLocal: true) + } + }*/ + } + + public func scrollViewDidScroll(_ scrollView: UIScrollView) { + if self.ignoreScrolling { + return + } + self.updateScrolling(transition: .immediate) + } + + private func updateScrolling(transition: ComponentTransition) { + guard let component = self.component, let layoutData = self.layoutData else { + return + } + var isLifted = self.temporaryLiftTimer != nil + if !component.liftWhileSwitching { + isLifted = false + } + if #available(iOS 26.0, *) { + } else { + isLifted = false + } + self.lensView.update(size: CGSize(width: layoutData.size.width - 3.0 * 2.0, height: layoutData.size.height - 3.0 * 2.0), selectionOrigin: CGPoint(x: -self.scrollView.contentOffset.x + layoutData.selectedItemFrame.minX, y: 0.0), selectionSize: CGSize(width: layoutData.selectedItemFrame.width, height: layoutData.size.height - 3.0 * 2.0), inset: 0.0, liftedInset: 6.0, isDark: component.theme.overallDarkAppearance, isLifted: isLifted, transition: transition) + + transition.setPosition(view: self.selectedScrollView, position: CGRect(origin: CGPoint(x: 3.0, y: 0.0), size: CGSize(width: layoutData.size.width - 3.0 * 2.0, height: layoutData.size.height - 3.0 * 2.0)).center) + transition.setBounds(view: self.selectedScrollView, bounds: CGRect(origin: CGPoint(x: self.scrollView.contentOffset.x, y: 0.0), size: CGSize(width: layoutData.size.width - 3.0 * 2.0, height: layoutData.size.height - 3.0 * 2.0))) + } + + func update(component: HorizontalTabsComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.isUpdating = true + defer { + self.isUpdating = false + } + + var shouldFocusOnSelectedTab = self.isDraggingTabs + + if component.isEditing { + if self.reorderedItemIds == nil { + self.reorderedItemIds = component.tabs.map(\.id) + } + } else { + self.reorderedItemIds = nil + } + + if self.component?.selectedTab != component.selectedTab { + self.tabSwitchFraction = 0.0 + if !self.isDraggingTabs { + self.temporaryLiftTimer?.invalidate() + self.temporaryLiftTimer = nil + + if !transition.animation.isImmediate { + self.temporaryLiftTimer = Foundation.Timer.scheduledTimer(withTimeInterval: 0.3, repeats: false, block: { [weak self] _ in + guard let self else { + return + } + self.temporaryLiftTimer = nil + if !self.isUpdating { + self.state?.updated(transition: .easeInOut(duration: 0.2), isLocal: true) + } + }) + } + } + shouldFocusOnSelectedTab = true + } + + self.component = component + self.state = state + + self.reorderingGesture?.isEnabled = component.isEditing + + let sizeHeight: CGFloat = availableSize.height + + let sideInset: CGFloat = 0.0 + + var validIds: [Tab.Id] = [] + + var orderedTabs = component.tabs + if let reorderedItemIds = self.reorderedItemIds { + orderedTabs.removeAll() + for id in reorderedItemIds { + if let item = component.tabs.first(where: { $0.id == id }) { + orderedTabs.append(item) + } + } + for tab in component.tabs { + if !orderedTabs.contains(where: { $0.id == tab.id }) { + orderedTabs.append(tab) + } + } + } + + var items: [(tabId: AnyHashable, itemView: ItemView, size: CGSize, itemTransition: ComponentTransition)] = [] + + for tab in orderedTabs { + let tabId = tab.id + validIds.append(tabId) + + var itemTransition = transition + let itemView: ItemView + if let current = self.itemViews[tabId] { + itemView = current + } else { + itemTransition = itemTransition.withAnimation(.none) + itemView = ItemView() + self.itemViews[tabId] = itemView + } + + var itemEditing: ItemComponent.Editing? + if component.isEditing { + itemEditing = ItemComponent.Editing(isEditable: true) + } + + let itemSize = itemView.regularView.update( + transition: itemTransition, + component: AnyComponent(ItemComponent( + context: component.context, + theme: component.theme, + tab: tab, + isSelected: false, + editing: itemEditing + )), + environment: {}, + containerSize: CGSize(width: 1000.0, height: sizeHeight - 3.0 * 2.0) + ) + let _ = itemView.selectedView.update( + transition: itemTransition, + component: AnyComponent(ItemComponent( + context: component.context, + theme: component.theme, + tab: tab, + isSelected: true, + editing: itemEditing + )), + environment: {}, + containerSize: CGSize(width: 1000.0, height: sizeHeight - 3.0 * 2.0) + ) + + items.append((tabId, itemView, itemSize, itemTransition)) + } + + var totalContentWidth: CGFloat = sideInset + for item in items { + totalContentWidth += item.size.width + } + totalContentWidth += sideInset + + let scrollContentWidth: CGFloat + if case .fill = component.layout, totalContentWidth < availableSize.width { + let regularItemWidth = floor((availableSize.width - 3.0 * 2.0 - sideInset * 2.0) / CGFloat(items.count)) + let lastItemWidth = (availableSize.width - 3.0 * 2.0 - sideInset * 2.0) - regularItemWidth * CGFloat(items.count - 1) + for i in 0 ..< items.count { + let item = items[i] + let itemWidth = (i == items.count - 1) ? lastItemWidth : regularItemWidth + var itemFrame = CGRect(origin: CGPoint(x: sideInset + regularItemWidth * CGFloat(i) + floor((itemWidth - item.size.width) * 0.5), y: 0.0), size: item.size) + if item.tabId == self.reorderingItem, let (initial, offset) = self.reorderingItemPosition { + itemFrame.origin = CGPoint(x: initial + offset, y: 3.0 + itemFrame.minY) + } + item.itemView.frame = itemFrame + item.itemView.selectionFrame = CGRect(origin: CGPoint(x: sideInset + regularItemWidth * CGFloat(i), y: 0.0), size: CGSize(width: itemWidth, height: item.size.height)) + } + + scrollContentWidth = availableSize.width - 3.0 * 2.0 + } else { + var contentWidth: CGFloat = sideInset + for item in items { + var itemFrame = CGRect(origin: CGPoint(x: contentWidth - 3.0, y: 0.0), size: item.size) + if item.tabId == self.reorderingItem, let (initial, offset) = self.reorderingItemPosition { + itemFrame.origin = CGPoint(x: initial + offset, y: 3.0 + itemFrame.minY) + } + item.itemView.frame = itemFrame + item.itemView.selectionFrame = itemFrame + item.itemView.selectionFrame.size.width += 3.0 + contentWidth += item.size.width + } + contentWidth += sideInset + scrollContentWidth = contentWidth + } + + for (tabId, itemView, _, itemTransition) in items { + let itemFrame = itemView.frame + + if let itemRegularView = itemView.regularView.view, let itemSelectedView = itemView.selectedView.view { + if itemRegularView.superview == nil { + self.scrollView.addSubview(itemRegularView) + self.selectedScrollView.addSubview(itemSelectedView) + + transition.animateAlpha(view: itemRegularView, from: 0.0, to: 1.0) + transition.animateScale(view: itemRegularView, from: 0.001, to: 1.0) + + transition.animateAlpha(view: itemSelectedView, from: 0.0, to: 1.0) + transition.animateScale(view: itemSelectedView, from: 0.001, to: 1.0) + } + itemTransition.setFrame(view: itemRegularView, frame: itemFrame) + itemTransition.setFrame(view: itemSelectedView, frame: itemFrame) + + if tabId == self.reorderingItem { + itemTransition.setSublayerTransform(view: itemRegularView, transform: CATransform3DMakeScale(1.2, 1.2, 1.0)) + itemTransition.setSublayerTransform(view: itemSelectedView, transform: CATransform3DMakeScale(1.2, 1.2, 1.0)) + itemTransition.setAlpha(view: itemRegularView, alpha: 0.9) + itemTransition.setAlpha(view: itemSelectedView, alpha: 0.0) + } else { + itemTransition.setSublayerTransform(view: itemRegularView, transform: CATransform3DIdentity) + itemTransition.setSublayerTransform(view: itemSelectedView, transform: CATransform3DIdentity) + itemTransition.setAlpha(view: itemRegularView, alpha: 1.0) + itemTransition.setAlpha(view: itemSelectedView, alpha: 1.0) + } + } + } + + var removedIds: [Tab.Id] = [] + for (id, itemView) in self.itemViews { + if !validIds.contains(id) { + removedIds.append(id) + if let itemRegularView = itemView.regularView.view, let itemSelectedView = itemView.selectedView.view { + transition.setScale(view: itemRegularView, scale: 0.001) + transition.setAlpha(view: itemRegularView, alpha: 0.0, completion: { [weak itemRegularView] _ in + itemRegularView?.removeFromSuperview() + }) + transition.setScale(view: itemSelectedView, scale: 0.001) + transition.setAlpha(view: itemSelectedView, alpha: 0.0, completion: { [weak itemSelectedView] _ in + itemSelectedView?.removeFromSuperview() + }) + } + } + } + for id in removedIds { + self.itemViews.removeValue(forKey: id) + } + + var selectedItemFrame: CGRect? + if let selectedTab = component.selectedTab { + for i in 0 ..< component.tabs.count { + if component.tabs[i].id == selectedTab { + if let itemView = self.itemViews[component.tabs[i].id] { + var selectedItemFrameValue = itemView.selectionFrame + if selectedTab == self.reorderingItem, let itemSuperview = itemView.regularView.view?.superview { + selectedItemFrameValue = itemSuperview.convert(itemView.selectionFrame, to: self.scrollView) + } + + var pendingItemFrame: CGRect? + if self.tabSwitchFraction != 0.0 { + if self.tabSwitchFraction > 0.0 && i != component.tabs.count - 1 { + if let nextItemView = self.itemViews[component.tabs[i + 1].id] { + pendingItemFrame = nextItemView.selectionFrame + } + } else if self.tabSwitchFraction < 0.0 && i != 0 { + if let previousItemView = self.itemViews[component.tabs[i - 1].id] { + pendingItemFrame = previousItemView.selectionFrame + } + } + } + if let pendingItemFrame { + let fraction = abs(self.tabSwitchFraction) + selectedItemFrameValue.origin.x = selectedItemFrameValue.minX * (1.0 - fraction) + pendingItemFrame.minX * fraction + selectedItemFrameValue.size.width = selectedItemFrameValue.width * (1.0 - fraction) + pendingItemFrame.width * fraction + } + + selectedItemFrame = selectedItemFrameValue + } + break + } + } + } + + let contentSize = CGSize(width: scrollContentWidth, height: sizeHeight - 3.0 * 2.0) + + let sizeWidth: CGFloat + switch component.layout { + case .fill: + sizeWidth = availableSize.width + case .fit: + sizeWidth = min(availableSize.width, scrollContentWidth + 3.0 * 2.0) + } + + let size = CGSize(width: sizeWidth, height: sizeHeight) + + self.layoutData = LayoutData( + size: size, + selectedItemFrame: selectedItemFrame ?? CGRect() + ) + + self.ignoreScrolling = true + let scrollViewFrame = CGRect(origin: CGPoint(x: 3.0, y: 0.0), size: CGSize(width: size.width - 3.0 * 2.0, height: size.height - 3.0 * 2.0)) + transition.setPosition(view: self.scrollView, position: scrollViewFrame.center) + if self.scrollView.contentSize != contentSize { + self.scrollView.contentSize = contentSize + } + + var scrollViewBounds = CGRect(origin: self.scrollView.bounds.origin, size: scrollViewFrame.size) + if shouldFocusOnSelectedTab || self.scrollView.bounds.size != scrollViewBounds.size { + if shouldFocusOnSelectedTab, let selectedItemFrame { + let scrollLookahead: CGFloat = 100.0 + + if scrollViewBounds.minX + scrollViewBounds.width - scrollLookahead < selectedItemFrame.maxX { + scrollViewBounds.origin.x = selectedItemFrame.maxX - scrollViewBounds.width + scrollLookahead + } + if scrollViewBounds.minX > selectedItemFrame.minX - scrollLookahead { + scrollViewBounds.origin.x = selectedItemFrame.minX - scrollLookahead + } + if scrollViewBounds.origin.x + scrollViewBounds.width > contentSize.width { + scrollViewBounds.origin.x = contentSize.width - scrollViewBounds.width + } + if scrollViewBounds.origin.x < 0.0 { + scrollViewBounds.origin.x = 0.0 + } + } + transition.setBounds(view: self.scrollView, bounds: scrollViewBounds) + } + + self.scrollView.layer.cornerRadius = (size.height - 3.0 * 2.0) * 0.5 + self.selectedScrollView.layer.cornerRadius = (size.height - 3.0 * 2.0) * 0.5 + + transition.setFrame(view: self.lensView, frame: CGRect(origin: CGPoint(x: 3.0, y: 3.0), size: CGSize(width: size.width - 3.0 * 2.0, height: size.height - 3.0 * 2.0))) + self.lensView.clipsToBounds = true + self.lensView.layer.cornerRadius = (size.height - 3.0 * 2.0) * 0.5 + self.ignoreScrolling = false + + self.updateScrolling(transition: transition) + + return size + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + +private final class ItemComponent: Component { + struct Editing: Equatable { + var isEditable: Bool + + init(isEditable: Bool) { + self.isEditable = isEditable + } + } + + let context: AccountContext? + let theme: PresentationTheme + let tab: HorizontalTabsComponent.Tab + let isSelected: Bool + let editing: Editing? + + init(context: AccountContext?, theme: PresentationTheme, tab: HorizontalTabsComponent.Tab, isSelected: Bool, editing: Editing?) { + self.context = context + self.theme = theme + self.tab = tab + self.isSelected = isSelected + self.editing = editing + } + + static func ==(lhs: ItemComponent, rhs: ItemComponent) -> Bool { + if lhs.theme !== rhs.theme { + return false + } + if lhs.tab != rhs.tab { + return false + } + if lhs.isSelected != rhs.isSelected { + return false + } + if lhs.editing != rhs.editing { + return false + } + return true + } + + final class View: UIView { + let extractedContainerView: ContextExtractedContentContainingView + let containerView: ContextControllerSourceView + + var titleContent: ComponentView? + var customContent: ComponentView? + var badge: ComponentView? + var deleteIcon: (button: HighlightTrackingButton, icon: UIImageView)? + + var tapRecognizer: UITapGestureRecognizer? + + var component: ItemComponent? + + override init(frame: CGRect) { + self.extractedContainerView = ContextExtractedContentContainingView() + self.containerView = ContextControllerSourceView() + + super.init(frame: frame) + + //self.extractedContainerView.contentView.addSubview(self.extractedBackgroundNode) + + self.containerView.addSubview(self.extractedContainerView) + self.containerView.targetViewForActivationProgress = self.extractedContainerView.contentView + self.addSubview(self.containerView) + + self.containerView.activated = { [weak self] gesture, _ in + guard let self, let component = self.component else { + return + } + component.tab.contextAction?(self.extractedContainerView, gesture) + } + + self.extractedContainerView.willUpdateIsExtractedToContextPreview = { [weak self] isExtracted, transition in + guard let self, let component else { + return + } + let _ = component + + /*if isExtracted, let theme = strongSelf.theme { + strongSelf.extractedBackgroundNode.image = generateStretchableFilledCircleImage(diameter: 28.0, color: theme.contextMenu.backgroundColor) + } + transition.updateAlpha(node: strongSelf.extractedBackgroundNode, alpha: isExtracted ? 1.0 : 0.0, completion: { _ in + if !isExtracted { + self?.extractedBackgroundNode.image = nil + } + })*/ + } + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func updateIsShaking(animated: Bool) { + guard let component = self.component else { + return + } + + if component.editing != nil { + if self.layer.animation(forKey: "shaking_position") == nil { + let degreesToRadians: (_ x: CGFloat) -> CGFloat = { x in + return .pi * x / 180.0 + } + + let duration: Double = 0.4 + let displacement: CGFloat = 1.0 + let degreesRotation: CGFloat = 2.0 + + let negativeDisplacement = -1.0 * displacement + let position = CAKeyframeAnimation.init(keyPath: "position") + position.beginTime = 0.8 + position.duration = duration + position.values = [ + NSValue(cgPoint: CGPoint(x: negativeDisplacement, y: negativeDisplacement)), + NSValue(cgPoint: CGPoint(x: 0, y: 0)), + NSValue(cgPoint: CGPoint(x: negativeDisplacement, y: 0)), + NSValue(cgPoint: CGPoint(x: 0, y: negativeDisplacement)), + NSValue(cgPoint: CGPoint(x: negativeDisplacement, y: negativeDisplacement)) + ] + position.calculationMode = .linear + position.isRemovedOnCompletion = false + position.repeatCount = Float.greatestFiniteMagnitude + position.beginTime = CFTimeInterval(Float(arc4random()).truncatingRemainder(dividingBy: Float(25)) / Float(100)) + position.isAdditive = true + + let transform = CAKeyframeAnimation.init(keyPath: "transform") + transform.beginTime = 2.6 + transform.duration = 0.3 + transform.valueFunction = CAValueFunction(name: CAValueFunctionName.rotateZ) + transform.values = [ + degreesToRadians(-1.0 * degreesRotation), + degreesToRadians(degreesRotation), + degreesToRadians(-1.0 * degreesRotation) + ] + transform.calculationMode = .linear + transform.isRemovedOnCompletion = false + transform.repeatCount = Float.greatestFiniteMagnitude + transform.isAdditive = true + transform.beginTime = CFTimeInterval(Float(arc4random()).truncatingRemainder(dividingBy: Float(25)) / Float(100)) + + self.layer.add(position, forKey: "shaking_position") + self.layer.add(transform, forKey: "shaking_rotation") + } + } else if self.layer.animation(forKey: "shaking_position") != nil { + if let presentationLayer = self.layer.presentation() { + let transition: ComponentTransition = .easeInOut(duration: 0.1) + if presentationLayer.position != self.layer.position { + transition.animatePosition(layer: self.layer, from: CGPoint(x: presentationLayer.position.x - self.layer.position.x, y: presentationLayer.position.y - self.layer.position.y), to: CGPoint(), additive: true) + } + if !CATransform3DIsIdentity(presentationLayer.transform) { + transition.setTransform(layer: self.layer, transform: CATransform3DIdentity) + } + } + + self.layer.removeAnimation(forKey: "shaking_position") + self.layer.removeAnimation(forKey: "shaking_rotation") + } + } + + @objc private func deleteButtonPressed() { + self.component?.tab.deleteAction?() + } + + func update(component: ItemComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + + self.containerView.isGestureEnabled = component.editing == nil + self.tapRecognizer?.isEnabled = component.editing == nil + + let sideInset: CGFloat = 16.0 + let badgeSpacing: CGFloat = 5.0 + + var size = CGSize(width: sideInset, height: availableSize.height) + + var titleContentSize: CGSize? + if case let .title(title) = component.tab.content { + let titleContent: ComponentView + if let current = self.titleContent { + titleContent = current + } else { + titleContent = ComponentView() + self.titleContent = titleContent + } + + let font = Font.medium(15.0) + + let rawAttributedString = ChatTextInputStateText(text: title.text, attributes: title.entities.compactMap { entity -> ChatTextInputStateTextAttribute? in + if case let .CustomEmoji(_, fileId) = entity.type { + return ChatTextInputStateTextAttribute(type: .customEmoji(stickerPack: nil, fileId: fileId, enableAnimation: title.enableAnimations), range: entity.range) + } + return nil + }).attributedText() + + let titleString = NSMutableAttributedString(attributedString: rawAttributedString) + titleString.addAttributes([ + .font: font, + .foregroundColor: component.theme.chat.inputPanel.panelControlColor + ], range: NSRange(location: 0, length: titleString.length)) + + titleContentSize = titleContent.update( + transition: .immediate, + component: AnyComponent(MultilineTextWithEntitiesComponent( + context: component.context, + animationCache: component.context?.animationCache, + animationRenderer: component.context?.animationRenderer, + placeholderColor: component.theme.chat.inputPanel.panelControlColor.withMultipliedAlpha(0.1), + text: .plain(titleString), + displaysAsynchronously: false + )), + environment: {}, + containerSize: CGSize(width: 300.0, height: 100.0) + ) + } else if let titleContent = self.titleContent { + self.titleContent = nil + titleContent.view?.removeFromSuperview() + } + + var customContentSize: CGSize? + if case let .custom(custom) = component.tab.content { + let customContent: ComponentView + if let current = self.customContent { + customContent = current + } else { + customContent = ComponentView() + self.customContent = customContent + } + + customContentSize = customContent.update( + transition: transition, + component: custom, + environment: {}, + containerSize: CGSize(width: 300.0, height: 100.0) + ) + } else if let customContent = self.customContent { + self.customContent = nil + customContent.view?.removeFromSuperview() + } + + if let titleContentSize { + size.width += titleContentSize.width + } + if let customContentSize { + size.width += customContentSize.width + } + + if let badgeData = component.tab.badge, component.tab.deleteAction == nil { + let badge: ComponentView + var badgeTransition = transition + if let current = self.badge { + badge = current + } else { + badgeTransition = badgeTransition.withAnimation(.none) + badge = ComponentView() + self.badge = badge + } + let badgeSize = badge.update( + transition: badgeTransition, + component: AnyComponent(TextBadgeComponent( + text: badgeData.title, + font: Font.medium(12.0), + background: badgeData.isAccent ? component.theme.list.itemCheckColors.fillColor : component.theme.chatList.unreadBadgeInactiveBackgroundColor, + foreground: component.theme.list.itemCheckColors.foregroundColor, + insets: UIEdgeInsets(top: 1.0, left: 5.0, bottom: 2.0, right: 5.0) + )), + environment: {}, + containerSize: CGSize(width: 100.0, height: 100.0) + ) + size.width += badgeSpacing + let badgeFrame = CGRect(origin: CGPoint(x: size.width, y: floorToScreenPixels((size.height - badgeSize.height) * 0.5)), size: badgeSize) + if let badgeView = badge.view { + if badgeView.superview == nil { + self.extractedContainerView.contentView.addSubview(badgeView) + transition.animateAlpha(view: badgeView, from: 0.0, to: 1.0) + transition.animateScale(view: badgeView, from: 0.001, to: 1.0) + } + badgeTransition.setFrame(view: badgeView, frame: badgeFrame) + } + size.width += badgeSize.width - 2.0 + } else if let badge = self.badge { + self.badge = nil + if let badgeView = badge.view { + transition.setFrame(view: badgeView, frame: badgeView.bounds.size.centered(around: CGPoint(x: size.width + sideInset - badgeView.bounds.width * 0.5, y: size.height * 0.5))) + transition.setScale(view: badgeView, scale: 0.001) + transition.setAlpha(view: badgeView, alpha: 0.0, completion: { [weak badgeView] _ in + badgeView?.removeFromSuperview() + }) + } + } + + if component.tab.deleteAction != nil { + let deleteIcon: (button: HighlightTrackingButton, icon: UIImageView) + if let current = self.deleteIcon { + deleteIcon = current + } else { + deleteIcon = (HighlightTrackingButton(), UIImageView()) + self.deleteIcon = deleteIcon + deleteIcon.button.addSubview(deleteIcon.icon) + deleteIcon.icon.image = generateImage(CGSize(width: 12.0, height: 12.0), contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setStrokeColor(UIColor.white.cgColor) + context.setLineWidth(1.33) + context.setLineCap(.round) + context.move(to: CGPoint(x: 1.0, y: 1.0)) + context.addLine(to: CGPoint(x: size.width - 1.0, y: size.height - 1.0)) + context.strokePath() + context.move(to: CGPoint(x: size.width - 1.0, y: 1.0)) + context.addLine(to: CGPoint(x: 1.0, y: size.height - 1.0)) + context.strokePath() + })?.withRenderingMode(.alwaysTemplate) + deleteIcon.button.addTarget(self, action: #selector(self.deleteButtonPressed), for: .touchUpInside) + } + deleteIcon.icon.tintColor = component.theme.chat.inputPanel.panelControlColor + if let image = deleteIcon.icon.image { + let deleteButtonFrame = CGRect(origin: CGPoint(x: size.width + 2.0, y: 0.0), size: CGSize(width: image.size.width + 6.0 * 2.0, height: size.height)) + let deleteIconFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((deleteButtonFrame.width - image.size.width) * 0.5), y: floorToScreenPixels((deleteButtonFrame.height - image.size.height) * 0.5)), size: image.size) + if deleteIcon.button.superview == nil { + self.addSubview(deleteIcon.button) + deleteIcon.button.frame = deleteButtonFrame + deleteIcon.icon.frame = deleteIconFrame + transition.animateAlpha(view: deleteIcon.button, from: 0.0, to: 1.0) + transition.animateScale(view: deleteIcon.button, from: 0.001, to: 1.0) + } + transition.setFrame(view: deleteIcon.button, frame: deleteButtonFrame) + transition.setFrame(view: deleteIcon.icon, frame: deleteIconFrame) + size.width += deleteButtonFrame.width - 3.0 + } + } else if let deleteIcon = self.deleteIcon { + self.deleteIcon = nil + let (button, _) = deleteIcon + transition.setScale(view: button, scale: 0.001) + transition.setAlpha(view: button, alpha: 0.0, completion: { [weak button] _ in + button?.removeFromSuperview() + }) + } + + size.width += sideInset + + if let titleView = self.titleContent?.view, let titleContentSize { + let titleFrame = CGRect(origin: CGPoint(x: sideInset, y: floorToScreenPixels((size.height - titleContentSize.height) * 0.5)), size: titleContentSize) + if titleView.superview == nil { + titleView.layer.anchorPoint = CGPoint() + self.extractedContainerView.contentView.addSubview(titleView) + } + transition.setPosition(view: titleView, position: titleFrame.origin) + titleView.bounds = CGRect(origin: CGPoint(), size: titleFrame.size) + } + + if let customView = self.customContent?.view, let customContentSize { + let customFrame = CGRect(origin: CGPoint(x: sideInset, y: floorToScreenPixels((size.height - customContentSize.height) * 0.5)), size: customContentSize) + if customView.superview == nil { + customView.layer.anchorPoint = CGPoint() + self.extractedContainerView.contentView.addSubview(customView) + } + transition.setFrame(view: customView, frame: customFrame) + } + + transition.setFrame(view: self.extractedContainerView, frame: CGRect(origin: CGPoint(), size: size)) + transition.setFrame(view: self.extractedContainerView.contentView, frame: CGRect(origin: CGPoint(), size: size)) + + let extractedBackgroundFrame = CGRect(origin: CGPoint(), size: size) + + self.extractedContainerView.contentRect = CGRect(origin: CGPoint(x: extractedBackgroundFrame.minX, y: 0.0), size: CGSize(width: extractedBackgroundFrame.width, height: size.height)) + transition.setFrame(view: self.containerView, frame: CGRect(origin: CGPoint(), size: size)) + + self.updateIsShaking(animated: !transition.animation.isImmediate) + + return size + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/ItemListDatePickerItem/Sources/ItemListDatePickerItem.swift b/submodules/TelegramUI/Components/ItemListDatePickerItem/Sources/ItemListDatePickerItem.swift index b5e0920f..db1bb623 100644 --- a/submodules/TelegramUI/Components/ItemListDatePickerItem/Sources/ItemListDatePickerItem.swift +++ b/submodules/TelegramUI/Components/ItemListDatePickerItem/Sources/ItemListDatePickerItem.swift @@ -131,7 +131,7 @@ public class ItemListDatePickerItemNode: ListViewItemNode, ItemListItemNode { self.containerNode = ASDisplayNode() self.containerNode.clipsToBounds = true - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.containerNode) diff --git a/submodules/TelegramUI/Components/LegacyChatHeaderPanelComponent/BUILD b/submodules/TelegramUI/Components/LegacyChatHeaderPanelComponent/BUILD new file mode 100644 index 00000000..abdc05b2 --- /dev/null +++ b/submodules/TelegramUI/Components/LegacyChatHeaderPanelComponent/BUILD @@ -0,0 +1,24 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "LegacyChatHeaderPanelComponent", + module_name = "LegacyChatHeaderPanelComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Display", + "//submodules/AsyncDisplayKit", + "//submodules/TelegramPresentationData", + "//submodules/ComponentFlow", + "//submodules/Components/ComponentDisplayAdapters", + "//submodules/ChatPresentationInterfaceState", + "//submodules/AccountContext", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/LegacyChatHeaderPanelComponent/Sources/LegacyChatHeaderPanelComponent.swift b/submodules/TelegramUI/Components/LegacyChatHeaderPanelComponent/Sources/LegacyChatHeaderPanelComponent.swift new file mode 100644 index 00000000..941ae252 --- /dev/null +++ b/submodules/TelegramUI/Components/LegacyChatHeaderPanelComponent/Sources/LegacyChatHeaderPanelComponent.swift @@ -0,0 +1,90 @@ +import Foundation +import UIKit +import Display +import TelegramPresentationData +import ComponentFlow +import ComponentDisplayAdapters +import ChatPresentationInterfaceState +import AsyncDisplayKit +import AccountContext + +open class ChatTitleAccessoryPanelNode: ASDisplayNode { + public typealias LayoutResult = ChatControllerCustomNavigationPanelNodeLayoutResult + + open var interfaceInteraction: ChatPanelInterfaceInteraction? + + open func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) -> LayoutResult { + preconditionFailure() + } +} + +public final class LegacyChatHeaderPanelComponent: Component { + public let panelNode: ChatTitleAccessoryPanelNode + public let interfaceState: ChatPresentationInterfaceState + + public init( + panelNode: ChatTitleAccessoryPanelNode, + interfaceState: ChatPresentationInterfaceState + ) { + self.panelNode = panelNode + self.interfaceState = interfaceState + } + + public static func ==(lhs: LegacyChatHeaderPanelComponent, rhs: LegacyChatHeaderPanelComponent) -> Bool { + if lhs.panelNode !== rhs.panelNode { + return false + } + if lhs.interfaceState != rhs.interfaceState { + return false + } + return true + } + + public final class View: UIView { + private var component: LegacyChatHeaderPanelComponent? + private weak var state: EmptyComponentState? + + public override init(frame: CGRect) { + super.init(frame: frame) + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + } + + func update(component: LegacyChatHeaderPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + let previousComponent = self.component + self.component = component + self.state = state + + if previousComponent?.panelNode !== component.panelNode { + previousComponent?.panelNode.view.removeFromSuperview() + self.addSubview(component.panelNode.view) + } + + let result = component.panelNode.updateLayout( + width: availableSize.width, + leftInset: 0.0, + rightInset: 0.0, + transition: transition.containedViewLayoutTransition, + interfaceState: component.interfaceState + ) + let size = CGSize(width: availableSize.width, height: result.backgroundHeight) + let panelFrame = CGRect(origin: CGPoint(), size: size) + transition.setFrame(view: component.panelNode.view, frame: panelFrame) + + return size + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/LiquidLens/Sources/LiquidLensView.swift b/submodules/TelegramUI/Components/LiquidLens/Sources/LiquidLensView.swift index e5d1e849..6931fbd1 100644 --- a/submodules/TelegramUI/Components/LiquidLens/Sources/LiquidLensView.swift +++ b/submodules/TelegramUI/Components/LiquidLens/Sources/LiquidLensView.swift @@ -58,36 +58,54 @@ private final class RestingBackgroundView: UIVisualEffectView { } public final class LiquidLensView: UIView { + public enum Kind { + case externalContainer + case builtinContainer + case noContainer + } + private struct Params: Equatable { var size: CGSize - var selectionX: CGFloat - var selectionWidth: CGFloat + var cornerRadius: CGFloat? + var selectionOrigin: CGPoint + var selectionSize: CGSize + var inset: CGFloat + var liftedInset: CGFloat var isDark: Bool var isLifted: Bool + var isCollapsed: Bool - init(size: CGSize, selectionX: CGFloat, selectionWidth: CGFloat, isDark: Bool, isLifted: Bool) { + init(size: CGSize, cornerRadius: CGFloat?, selectionOrigin: CGPoint, selectionSize: CGSize, inset: CGFloat, liftedInset: CGFloat, isDark: Bool, isLifted: Bool, isCollapsed: Bool) { self.size = size - self.selectionX = selectionX - self.selectionWidth = selectionWidth + self.cornerRadius = cornerRadius + self.selectionOrigin = selectionOrigin + self.selectionSize = selectionSize + self.inset = inset + self.liftedInset = liftedInset self.isLifted = isLifted self.isDark = isDark + self.isCollapsed = isCollapsed } } private struct LensParams: Equatable { var baseFrame: CGRect + var inset: CGFloat + var liftedInset: CGFloat var isLifted: Bool - init(baseFrame: CGRect, isLifted: Bool) { + init(baseFrame: CGRect, inset: CGFloat, liftedInset: CGFloat, isLifted: Bool) { self.baseFrame = baseFrame + self.inset = inset + self.liftedInset = liftedInset self.isLifted = isLifted } } private let containerView: UIView - private let backgroundContainerContainer: UIView - private let backgroundContainer: GlassBackgroundContainerView - private let backgroundView: GlassBackgroundView + private let backgroundContainer: GlassBackgroundContainerView? + private let genericBackgroundContainer: UIView? + private let backgroundView: GlassBackgroundView? private var lensView: UIView? private let liftedContainerView: UIView public let contentView: UIView @@ -109,34 +127,64 @@ public final class LiquidLensView: UIView { private var liftedDisplayLink: SharedDisplayLinkDriver.Link? - public var selectionX: CGFloat? { - return self.params?.selectionX + public var selectionOrigin: CGPoint? { + return self.params?.selectionOrigin } - public var selectionWidth: CGFloat? { - return self.params?.selectionWidth + public var selectionSize: CGSize? { + return self.params?.selectionSize } + + public private(set) var isAnimating: Bool = false { + didSet { + if self.isAnimating != oldValue { + self.onUpdatedIsAnimating?(self.isAnimating) + } + } + } + public var onUpdatedIsAnimating: ((Bool) -> Void)? + public var isLiftedAnimationCompleted: (() -> Void)? - override public init(frame: CGRect) { + public init(kind: Kind) { self.containerView = UIView() - self.backgroundContainerContainer = UIView() - self.backgroundContainer = GlassBackgroundContainerView() + switch kind { + case .builtinContainer: + self.backgroundContainer = GlassBackgroundContainerView() + self.genericBackgroundContainer = nil + case .externalContainer, .noContainer: + self.backgroundContainer = nil + self.genericBackgroundContainer = UIView() + } - self.backgroundView = GlassBackgroundView() + if case .noContainer = kind { + self.backgroundView = nil + } else { + self.backgroundView = GlassBackgroundView() + } self.contentView = UIView() self.liftedContainerView = UIView() self.restingBackgroundView = RestingBackgroundView() - super.init(frame: frame) + super.init(frame: CGRect()) - self.backgroundContainerContainer.addSubview(self.backgroundContainer) - self.addSubview(self.backgroundContainerContainer) - - self.backgroundContainer.contentView.addSubview(self.backgroundView) - self.backgroundView.contentView.addSubview(self.containerView) + if let backgroundContainer = self.backgroundContainer { + self.addSubview(backgroundContainer) + if let backgroundView = self.backgroundView { + backgroundContainer.contentView.addSubview(backgroundView) + backgroundView.contentView.addSubview(self.containerView) + } + } else if let genericBackgroundContainer = self.genericBackgroundContainer { + self.addSubview(genericBackgroundContainer) + if let backgroundView = self.backgroundView { + genericBackgroundContainer.addSubview(backgroundView) + backgroundView.contentView.addSubview(self.containerView) + } else { + genericBackgroundContainer.addSubview(self.containerView) + } + } self.containerView.isUserInteractionEnabled = false if #available(iOS 26.0, *) { @@ -150,7 +198,11 @@ public final class LiquidLensView: UIView { } if let lensView = self.lensView { - self.backgroundContainer.layer.zPosition = 1 + if let backgroundContainer = self.backgroundContainer { + backgroundContainer.layer.zPosition = 1 + } else if let genericBackgroundContainer = self.genericBackgroundContainer{ + genericBackgroundContainer.layer.zPosition = 1 + } lensView.layer.zPosition = 10.0 self.liftedContainerView.addSubview(self.restingBackgroundView) @@ -159,7 +211,11 @@ public final class LiquidLensView: UIView { self.containerView.addSubview(lensView) self.containerView.addSubview(self.contentView) - lensView.perform(NSSelectorFromString("setLiftedContainerView:"), with: self.backgroundContainer.contentView) + if let backgroundContainer = self.backgroundContainer { + lensView.perform(NSSelectorFromString("setLiftedContainerView:"), with: backgroundContainer.contentView) + } else if let genericBackgroundContainer = self.genericBackgroundContainer { + lensView.perform(NSSelectorFromString("setLiftedContainerView:"), with: genericBackgroundContainer) + } lensView.perform(NSSelectorFromString("setLiftedContentView:"), with: self.liftedContainerView) lensView.perform(NSSelectorFromString("setOverridePunchoutView:"), with: self.contentView) @@ -194,7 +250,11 @@ public final class LiquidLensView: UIView { } else { let legacySelectionView = GlassBackgroundView.ContentImageView() self.legacySelectionView = legacySelectionView - self.backgroundView.contentView.insertSubview(legacySelectionView, at: 0) + if let backgroundView = self.backgroundView { + backgroundView.contentView.insertSubview(legacySelectionView, at: 0) + } else { + self.containerView.insertSubview(legacySelectionView, at: 0) + } let legacyContentMaskView = UIView() legacyContentMaskView.backgroundColor = .white @@ -222,9 +282,16 @@ public final class LiquidLensView: UIView { required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + public func setLiftedContainer(view: UIView) { + guard let lensView = self.lensView else { + return + } + lensView.perform(NSSelectorFromString("setLiftedContainerView:"), with: view) + } - public func update(size: CGSize, selectionX: CGFloat, selectionWidth: CGFloat, isDark: Bool, isLifted: Bool, transition: ComponentTransition) { - let params = Params(size: size, selectionX: selectionX, selectionWidth: selectionWidth, isDark: isDark, isLifted: isLifted) + public func update(size: CGSize, cornerRadius: CGFloat? = nil, selectionOrigin: CGPoint, selectionSize: CGSize, inset: CGFloat, liftedInset: CGFloat = 4.0, isDark: Bool, isLifted: Bool, isCollapsed: Bool = false, transition: ComponentTransition) { + let params = Params(size: size, cornerRadius: cornerRadius, selectionOrigin: selectionOrigin, selectionSize: selectionSize, inset: inset, liftedInset: liftedInset, isDark: isDark, isLifted: isLifted, isCollapsed: isCollapsed) if self.params == params { return } @@ -238,7 +305,7 @@ public final class LiquidLensView: UIView { self.update(params: params, transition: transition) } - private func updateLens(params: LensParams, animated: Bool) { + private func updateLens(params: LensParams, transition: ComponentTransition) { guard let lensView = self.lensView else { return } @@ -249,22 +316,23 @@ public final class LiquidLensView: UIView { } self.isApplyingLensParams = true let previousParams = self.appliedLensParams - - let transition: ComponentTransition = animated ? .easeInOut(duration: 0.3) : .immediate + self.appliedLensParams = params if previousParams?.isLifted != params.isLifted { + self.isAnimating = true + let selector = NSSelectorFromString("setLifted:animated:alongsideAnimations:completion:") var shouldScheduleUpdate = false var didProcessUpdate = false self.pendingLensParams = params if let lensView = self.lensView, let method = lensView.method(for: selector) { - typealias ObjCMethod = @convention(c) (AnyObject, Selector, Bool, Bool, @escaping () -> Void, AnyObject?) -> Void + typealias ObjCMethod = @convention(c) (AnyObject, Selector, Bool, Bool, @escaping () -> Void, (() -> Void)?) -> Void let function = unsafeBitCast(method, to: ObjCMethod.self) function(lensView, selector, params.isLifted, !transition.animation.isImmediate, { [weak self] in guard let self else { return } - let liftedInset: CGFloat = params.isLifted ? 4.0 : -4.0 + let liftedInset: CGFloat = params.isLifted ? params.liftedInset : (-params.inset) lensView.bounds = CGRect(origin: CGPoint(), size: CGSize(width: params.baseFrame.width + liftedInset * 2.0, height: params.baseFrame.height + liftedInset * 2.0)) didProcessUpdate = true if shouldScheduleUpdate { @@ -274,10 +342,18 @@ public final class LiquidLensView: UIView { } self.isApplyingLensParams = false self.pendingLensParams = nil - self.updateLens(params: pendingLensParams, animated: !transition.animation.isImmediate) + self.updateLens(params: pendingLensParams, transition: transition) } } - }, nil) + }, { [weak self] in + guard let self else { + return + } + if !self.isApplyingLensParams { + self.isAnimating = false + } + self.isLiftedAnimationCompleted?() + }) } if didProcessUpdate { transition.animateView { @@ -289,11 +365,32 @@ public final class LiquidLensView: UIView { shouldScheduleUpdate = true } } else { + let liftedInset: CGFloat = params.isLifted ? params.liftedInset : (-params.inset) + let lensBounds = CGRect(origin: CGPoint(), size: CGSize(width: params.baseFrame.width + liftedInset * 2.0, height: params.baseFrame.height + liftedInset * 2.0)) + let lensCenter = CGPoint(x: params.baseFrame.midX, y: params.baseFrame.midY) + + let previousBounds: CGRect = lensView.bounds transition.animateView { - let liftedInset: CGFloat = params.isLifted ? 4.0 : -4.0 - lensView.bounds = CGRect(origin: CGPoint(), size: CGSize(width: params.baseFrame.width + liftedInset * 2.0, height: params.baseFrame.height + liftedInset * 2.0)) - lensView.center = CGPoint(x: params.baseFrame.midX, y: params.baseFrame.midY) + lensView.bounds = lensBounds } + + lensView.layer.removeAllAnimations() + lensView.bounds = lensBounds + + if !transition.animation.isImmediate { + self.isAnimating = true + } + transition.setPosition(view: lensView, position: lensCenter, completion: { [weak self] flag in + guard let self, flag else { + return + } + if !self.isApplyingLensParams { + self.isAnimating = false + } + }) + // No idea why + transition.animatePosition(layer: lensView.layer, from: CGPoint(x: (lensBounds.width - previousBounds.width) * 0.5, y: 0.0), to: CGPoint(), additive: true) + self.isApplyingLensParams = false } } @@ -319,25 +416,48 @@ public final class LiquidLensView: UIView { self.params = params transition.setFrame(view: self.containerView, frame: CGRect(origin: CGPoint(), size: params.size)) - transition.setFrame(view: self.backgroundContainerContainer, frame: CGRect(origin: CGPoint(), size: params.size)) - transition.setFrame(view: self.backgroundContainer, frame: CGRect(origin: CGPoint(), size: params.size)) - self.backgroundContainer.update(size: params.size, isDark: params.isDark, transition: transition) + if let backgroundContainer = self.backgroundContainer { + transition.setFrame(view: backgroundContainer, frame: CGRect(origin: CGPoint(), size: params.size)) + backgroundContainer.update(size: params.size, isDark: params.isDark, transition: transition) + } else if let genericBackgroundContainer = self.genericBackgroundContainer { + transition.setFrame(view: genericBackgroundContainer, frame: CGRect(origin: CGPoint(), size: params.size)) + } - transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: params.size)) - self.backgroundView.update(size: params.size, cornerRadius: params.size.height * 0.5, isDark: params.isDark, tintColor: GlassBackgroundView.TintColor.init(kind: .panel, color: UIColor(white: params.isDark ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition) + if let backgroundView = self.backgroundView { + transition.setFrame(view: backgroundView, frame: CGRect(origin: CGPoint(), size: params.size)) + backgroundView.update(size: params.size, cornerRadius: params.cornerRadius ?? (params.size.height * 0.5), isDark: params.isDark, tintColor: GlassBackgroundView.TintColor.init(kind: .panel, color: UIColor(white: params.isDark ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition) + } - transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: params.size)) - transition.setFrame(view: self.liftedContainerView, frame: CGRect(origin: CGPoint(), size: params.size)) + if self.contentView.bounds.size != params.size { + self.contentView.clipsToBounds = true + transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: params.size), completion: { [weak self] completed in + guard let self, completed else { + return + } + self.contentView.clipsToBounds = false + }) + transition.setCornerRadius(layer: self.contentView.layer, cornerRadius: params.cornerRadius ?? (params.size.height * 0.5)) - let baseLensFrame = CGRect(origin: CGPoint(x: max(0.0, min(params.selectionX, params.size.width - params.selectionWidth)), y: 0.0), size: CGSize(width: params.selectionWidth, height: params.size.height)) - self.updateLens(params: LensParams(baseFrame: baseLensFrame, isLifted: params.isLifted), animated: !transition.animation.isImmediate) + self.liftedContainerView.clipsToBounds = true + transition.setFrame(view: self.liftedContainerView, frame: CGRect(origin: CGPoint(), size: params.size), completion: { [weak self] completed in + guard let self, completed else { + return + } + self.liftedContainerView.clipsToBounds = false + }) + transition.setCornerRadius(layer: self.liftedContainerView.layer, cornerRadius: params.cornerRadius ?? (params.size.height * 0.5)) + } + + + let baseLensFrame = CGRect(origin: params.selectionOrigin, size: params.selectionSize) + self.updateLens(params: LensParams(baseFrame: baseLensFrame, inset: params.inset, liftedInset: params.liftedInset, isLifted: params.isLifted), transition: transition) if let legacyContentMaskView = self.legacyContentMaskView { transition.setFrame(view: legacyContentMaskView, frame: CGRect(origin: CGPoint(), size: params.size)) } if let legacyContentMaskBlobView = self.legacyContentMaskBlobView, let legacyLiftedContentBlobMaskView = self.legacyLiftedContentBlobMaskView, let legacySelectionView = self.legacySelectionView { - let lensFrame = baseLensFrame.insetBy(dx: 4.0, dy: 4.0) + let lensFrame = baseLensFrame.insetBy(dx: params.inset, dy: params.inset) let effectiveLensFrame = lensFrame.insetBy(dx: params.isLifted ? -2.0 : 0.0, dy: params.isLifted ? -2.0 : 0.0) if legacyContentMaskBlobView.image?.size.height != lensFrame.height { @@ -354,7 +474,7 @@ public final class LiquidLensView: UIView { transition.setFrame(view: self.restingBackgroundView, frame: CGRect(origin: CGPoint(), size: params.size)) self.restingBackgroundView.update(isDark: params.isDark) - transition.setAlpha(view: self.restingBackgroundView, alpha: params.isLifted ? 0.0 : 1.0) + transition.setAlpha(view: self.restingBackgroundView, alpha: (params.isLifted || params.isCollapsed) ? 0.0 : 1.0) if params.isLifted { if self.liftedDisplayLink == nil { diff --git a/submodules/TelegramUI/Components/LiveLocationHeaderPanelComponent/BUILD b/submodules/TelegramUI/Components/LiveLocationHeaderPanelComponent/BUILD new file mode 100644 index 00000000..fa7ab63c --- /dev/null +++ b/submodules/TelegramUI/Components/LiveLocationHeaderPanelComponent/BUILD @@ -0,0 +1,34 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "LiveLocationHeaderPanelComponent", + module_name = "LiveLocationHeaderPanelComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Display", + "//submodules/AsyncDisplayKit", + "//submodules/TelegramPresentationData", + "//submodules/AccountContext", + "//submodules/TelegramCore", + "//submodules/Postbox", + "//submodules/Components/ComponentDisplayAdapters", + "//submodules/TelegramUIPreferences", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/ComponentFlow", + "//submodules/TelegramUI/Components/GlobalControlPanelsContext", + "//submodules/PresentationDataUtils", + "//submodules/TextFormat", + "//submodules/Markdown", + "//submodules/LocalizedPeerData", + "//submodules/LiveLocationTimerNode", + "//submodules/AvatarNode", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/LiveLocationHeaderPanelComponent/Sources/LiveLocationHeaderPanelComponent.swift b/submodules/TelegramUI/Components/LiveLocationHeaderPanelComponent/Sources/LiveLocationHeaderPanelComponent.swift new file mode 100644 index 00000000..9ec0dfbf --- /dev/null +++ b/submodules/TelegramUI/Components/LiveLocationHeaderPanelComponent/Sources/LiveLocationHeaderPanelComponent.swift @@ -0,0 +1,286 @@ +import Foundation +import UIKit +import Display +import TelegramPresentationData +import ComponentFlow +import ComponentDisplayAdapters +import AccountContext +import TelegramCore +import GlobalControlPanelsContext +import SwiftSignalKit +import Postbox +import PresentationDataUtils + +private func presentLiveLocationController(context: AccountContext, peerId: PeerId, controller: ViewController) { + let presentImpl: (EngineMessage?) -> Void = { [weak controller] message in + if let message = message, let strongController = controller { + let _ = context.sharedContext.openChatMessage(OpenChatMessageParams(context: context, chatLocation: nil, chatFilterTag: nil, chatLocationContextHolder: nil, message: message._asMessage(), standalone: false, reverseMessageGalleryOrder: false, navigationController: strongController.navigationController as? NavigationController, modal: true, dismissInput: { + controller?.view.endEditing(true) + }, present: { c, a, _ in + controller?.present(c, in: .window(.root), with: a, blockInteraction: true) + }, transitionNode: { _, _, _ in + return nil + }, addToTransitionSurface: { _ in + }, openUrl: { _ in + }, openPeer: { peer, navigation in + }, callPeer: { _, _ in + }, openConferenceCall: { _ in + }, enqueueMessage: { message in + let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [message]).start() + }, sendSticker: nil, sendEmoji: nil, setupTemporaryHiddenMedia: { _, _, _ in + }, chatAvatarHiddenMedia: { _, _ in + })) + } + } + if let id = context.liveLocationManager?.internalMessageForPeerId(peerId) { + let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: id)) + |> deliverOnMainQueue).start(next: presentImpl) + } else if let liveLocationManager = context.liveLocationManager { + let _ = (liveLocationManager.summaryManager.peersBroadcastingTo(peerId: peerId) + |> take(1) + |> map { peersAndMessages -> EngineMessage? in + return peersAndMessages?.first?.1 + } |> deliverOnMainQueue).start(next: presentImpl) + } +} + +public final class LiveLocationHeaderPanelComponent: Component { + public let context: AccountContext + public let theme: PresentationTheme + public let strings: PresentationStrings + public let data: GlobalControlPanelsContext.LiveLocation + public let controller: () -> ViewController? + + public init( + context: AccountContext, + theme: PresentationTheme, + strings: PresentationStrings, + data: GlobalControlPanelsContext.LiveLocation, + controller: @escaping () -> ViewController? + ) { + self.context = context + self.theme = theme + self.strings = strings + self.data = data + self.controller = controller + } + + public static func ==(lhs: LiveLocationHeaderPanelComponent, rhs: LiveLocationHeaderPanelComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.theme !== rhs.theme { + return false + } + if lhs.strings !== rhs.strings { + return false + } + if lhs.data != rhs.data { + return false + } + return true + } + + public final class View: UIView { + private var panel: LocationBroadcastNavigationAccessoryPanel? + + private var component: LiveLocationHeaderPanelComponent? + private weak var state: EmptyComponentState? + + public override init(frame: CGRect) { + super.init(frame: frame) + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + } + + func update(component: LiveLocationHeaderPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + let themeUpdated = self.component?.theme !== component.theme + + self.component = component + self.state = state + + let panel: LocationBroadcastNavigationAccessoryPanel + if let current = self.panel { + panel = current + } else { + panel = LocationBroadcastNavigationAccessoryPanel( + accountPeerId: component.context.account.peerId, + theme: component.theme, + strings: component.strings, + nameDisplayOrder: component.context.sharedContext.currentPresentationData.with({ $0 }).nameDisplayOrder, + tapAction: { [weak self] in + guard let self, let component = self.component, let controller = component.controller() else { + return + } + switch component.data.mode { + case .all: + let messages = component.data.messages.values.sorted(by: { $0.index > $1.index }) + + if messages.count == 1 { + presentLiveLocationController(context: component.context, peerId: messages[0].id.peerId, controller: controller) + } else { + let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } + let actionSheet = ActionSheetController(presentationData: presentationData) + let dismissAction: () -> Void = { [weak actionSheet] in + actionSheet?.dismissAnimated() + } + var items: [ActionSheetItem] = [] + if !messages.isEmpty { + items.append(ActionSheetTextItem(title: presentationData.strings.LiveLocation_MenuChatsCount(Int32(messages.count)))) + for message in messages { + if let peer = message.peers[message.id.peerId] { + var beginTimeAndTimeout: (Double, Double)? + for media in message.media { + if let media = media as? TelegramMediaMap, let timeout = media.liveBroadcastingTimeout { + beginTimeAndTimeout = (Double(message.timestamp), Double(timeout)) + } + } + + if let beginTimeAndTimeout { + items.append(LocationBroadcastActionSheetItem(context: component.context, peer: peer, title: EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), beginTimestamp: beginTimeAndTimeout.0, timeout: beginTimeAndTimeout.1, strings: presentationData.strings, action: { [weak self] in + dismissAction() + + guard let self, let component = self.component, let controller = component.controller() else { + return + } + presentLiveLocationController(context: component.context, peerId: peer.id, controller: controller) + })) + } + } + } + items.append(ActionSheetButtonItem(title: presentationData.strings.LiveLocation_MenuStopAll, color: .destructive, action: { [weak self] in + dismissAction() + + guard let self, let component = self.component else { + return + } + for peer in component.data.peers { + component.context.liveLocationManager?.cancelLiveLocation(peerId: peer.id) + } + })) + } + actionSheet.setItemGroups([ + ActionSheetItemGroup(items: items), + ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) + ]) + self.window?.endEditing(true) + controller.present(actionSheet, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + } + case let .peer(peerId): + presentLiveLocationController(context: component.context, peerId: peerId, controller: controller) + } + }, + close: { [weak self] in + guard let self, let component = self.component, let controller = component.controller() else { + return + } + var closePeers: [EnginePeer]? + var closePeerId: EnginePeer.Id? + switch component.data.mode { + case .all: + if component.data.peers.count > 1 { + closePeers = component.data.peers + } else { + closePeerId = component.data.peers.first?.id + } + case let .peer(peerId): + closePeerId = peerId + } + let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } + let actionSheet = ActionSheetController(presentationData: presentationData) + let dismissAction: () -> Void = { [weak actionSheet] in + actionSheet?.dismissAnimated() + } + var items: [ActionSheetItem] = [] + if let closePeers = closePeers, !closePeers.isEmpty { + items.append(ActionSheetTextItem(title: presentationData.strings.LiveLocation_MenuChatsCount(Int32(closePeers.count)))) + for peer in closePeers { + items.append(ActionSheetButtonItem(title: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), action: { [weak self] in + dismissAction() + + guard let self, let component = self.component, let controller = component.controller() else { + return + } + presentLiveLocationController(context: component.context, peerId: peer.id, controller: controller) + })) + } + items.append(ActionSheetButtonItem(title: presentationData.strings.LiveLocation_MenuStopAll, color: .destructive, action: { [weak self] in + dismissAction() + + guard let self, let component = self.component else { + return + } + for peer in closePeers { + component.context.liveLocationManager?.cancelLiveLocation(peerId: peer.id) + } + })) + } else if let closePeerId { + items.append(ActionSheetButtonItem(title: presentationData.strings.Map_StopLiveLocation, color: .destructive, action: { [weak self] in + dismissAction() + + guard let self, let component = self.component else { + return + } + component.context.liveLocationManager?.cancelLiveLocation(peerId: closePeerId) + })) + } + actionSheet.setItemGroups([ + ActionSheetItemGroup(items: items), + ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) + ]) + self.window?.endEditing(true) + controller.present(actionSheet, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + } + ) + self.panel = panel + self.addSubview(panel.view) + } + + let size = CGSize(width: availableSize.width, height: 40.0) + let panelFrame = CGRect(origin: CGPoint(), size: size) + transition.setFrame(view: panel.view, frame: panelFrame) + panel.updateLayout(size: panelFrame.size, leftInset: 0.0, rightInset: 0.0, transition: transition.containedViewLayoutTransition) + + let mappedMode: LocationBroadcastNavigationAccessoryPanelMode + switch component.data.mode { + case .all: + mappedMode = .summary + case .peer: + mappedMode = .peer + } + panel.update(peers: component.data.peers, mode: mappedMode, canClose: component.data.canClose) + + if themeUpdated { + panel.updatePresentationData(PresentationData( + strings: component.strings, + theme: component.theme, + autoNightModeTriggered: false, + chatWallpaper: .builtin(WallpaperSettings()), + chatFontSize: .regular, + chatBubbleCorners: PresentationChatBubbleCorners(mainRadius: 0.0, auxiliaryRadius: 0.0, mergeBubbleCorners: true), + listsFontSize: .regular, + dateTimeFormat: PresentationDateTimeFormat(), + nameDisplayOrder: .firstLast, + nameSortOrder: .firstLast, + reduceMotion: false, + largeEmoji: false + )) + } + + return size + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/LiveLocationHeaderPanelComponent/Sources/LocationBroadcastActionSheetItem.swift b/submodules/TelegramUI/Components/LiveLocationHeaderPanelComponent/Sources/LocationBroadcastActionSheetItem.swift new file mode 100644 index 00000000..30bdac85 --- /dev/null +++ b/submodules/TelegramUI/Components/LiveLocationHeaderPanelComponent/Sources/LocationBroadcastActionSheetItem.swift @@ -0,0 +1,139 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import TelegramCore +import Postbox +import TelegramPresentationData +import AccountContext +import LiveLocationTimerNode +import AvatarNode + +public class LocationBroadcastActionSheetItem: ActionSheetItem { + public let context: AccountContext + public let peer: Peer + public let title: String + public let beginTimestamp: Double + public let timeout: Double + public let strings: PresentationStrings + public let action: () -> Void + + public init(context: AccountContext, peer: Peer, title: String, beginTimestamp: Double, timeout: Double, strings: PresentationStrings, action: @escaping () -> Void) { + self.context = context + self.peer = peer + self.title = title + self.beginTimestamp = beginTimestamp + self.timeout = timeout + self.strings = strings + self.action = action + } + + public func node(theme: ActionSheetControllerTheme) -> ActionSheetItemNode { + let node = LocationBroadcastActionSheetItemNode(theme: theme) + node.setItem(self) + return node + } + + public func updateNode(_ node: ActionSheetItemNode) { + guard let node = node as? LocationBroadcastActionSheetItemNode else { + assertionFailure() + return + } + + node.setItem(self) + node.requestLayoutUpdate() + } +} + +private let avatarFont = avatarPlaceholderFont(size: 15.0) + +public class LocationBroadcastActionSheetItemNode: ActionSheetItemNode { + private let theme: ActionSheetControllerTheme + + private let defaultFont: UIFont + + private var item: LocationBroadcastActionSheetItem? + + private let button: HighlightTrackingButton + private let avatarNode: AvatarNode + private let label: ImmediateTextNode + private let timerNode: ChatMessageLiveLocationTimerNode + + override public init(theme: ActionSheetControllerTheme) { + self.theme = theme + self.defaultFont = Font.regular(floor(theme.baseFontSize * 20.0 / 17.0)) + + self.button = HighlightTrackingButton() + + self.avatarNode = AvatarNode(font: avatarFont) + self.avatarNode.isLayerBacked = !smartInvertColorsEnabled() + + self.label = ImmediateTextNode() + self.label.isUserInteractionEnabled = false + self.label.displaysAsynchronously = false + self.label.maximumNumberOfLines = 1 + + self.timerNode = ChatMessageLiveLocationTimerNode() + + super.init(theme: theme) + + self.view.addSubview(self.button) + self.addSubnode(self.avatarNode) + self.addSubnode(self.label) + self.addSubnode(self.timerNode) + + self.button.highligthedChanged = { [weak self] highlighted in + if let strongSelf = self { + if highlighted { + strongSelf.backgroundNode.backgroundColor = strongSelf.theme.itemHighlightedBackgroundColor + } else { + UIView.animate(withDuration: 0.3, animations: { + strongSelf.backgroundNode.backgroundColor = strongSelf.theme.itemBackgroundColor + }) + } + } + } + + self.button.addTarget(self, action: #selector(self.buttonPressed), for: .touchUpInside) + } + + func setItem(_ item: LocationBroadcastActionSheetItem) { + self.item = item + + let defaultFont = Font.regular(floor(theme.baseFontSize * 20.0 / 17.0)) + + let textColor: UIColor = self.theme.primaryTextColor + self.label.attributedText = NSAttributedString(string: item.title, font: defaultFont, textColor: textColor) + + self.avatarNode.setPeer(context: item.context, theme: (item.context.sharedContext.currentPresentationData.with { $0 }).theme, peer: EnginePeer(item.peer)) + + self.timerNode.update(backgroundColor: self.theme.controlAccentColor.withAlphaComponent(0.4), foregroundColor: self.theme.controlAccentColor, textColor: self.theme.controlAccentColor, beginTimestamp: item.beginTimestamp, timeout: Int32(item.timeout) == liveLocationIndefinitePeriod ? -1.0 : item.timeout, strings: item.strings) + } + + public override func updateLayout(constrainedSize: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { + let size = CGSize(width: constrainedSize.width, height: 57.0) + + self.button.frame = CGRect(origin: CGPoint(), size: size) + + let avatarInset: CGFloat = 42.0 + let avatarSize: CGFloat = 32.0 + + self.avatarNode.frame = CGRect(origin: CGPoint(x: 16.0, y: floor((size.height - avatarSize) / 2.0)), size: CGSize(width: avatarSize, height: avatarSize)) + + let labelSize = self.label.updateLayout(CGSize(width: max(1.0, size.width - avatarInset - 16.0 - 16.0 - 30.0), height: size.height)) + self.label.frame = CGRect(origin: CGPoint(x: 16.0 + avatarInset, y: floorToScreenPixels((size.height - labelSize.height) / 2.0)), size: labelSize) + + let timerSize = CGSize(width: 28.0, height: 28.0) + self.timerNode.frame = CGRect(origin: CGPoint(x: size.width - 16.0 - timerSize.width, y: floorToScreenPixels((size.height - timerSize.height) / 2.0)), size: timerSize) + + self.updateInternalLayout(size, constrainedSize: constrainedSize) + return size + } + + @objc func buttonPressed() { + if let item = self.item { + item.action() + } + } +} + diff --git a/submodules/TelegramUI/Components/LiveLocationHeaderPanelComponent/Sources/LocationBroadcastNavigationAccessoryPanel.swift b/submodules/TelegramUI/Components/LiveLocationHeaderPanelComponent/Sources/LocationBroadcastNavigationAccessoryPanel.swift new file mode 100644 index 00000000..c0f86a67 --- /dev/null +++ b/submodules/TelegramUI/Components/LiveLocationHeaderPanelComponent/Sources/LocationBroadcastNavigationAccessoryPanel.swift @@ -0,0 +1,212 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import TelegramCore +import TelegramPresentationData +import TelegramUIPreferences +import TextFormat +import Markdown +import LocalizedPeerData +import LiveLocationTimerNode + +private let titleFont = Font.regular(12.0) +private let subtitleFont = Font.regular(10.0) + +enum LocationBroadcastNavigationAccessoryPanelMode { + case summary + case peer +} + +final class LocationBroadcastNavigationAccessoryPanel: ASDisplayNode { + private let accountPeerId: EnginePeer.Id + private var theme: PresentationTheme + private var strings: PresentationStrings + private var nameDisplayOrder: PresentationPersonNameOrder + + private let tapAction: () -> Void + private let close: () -> Void + + private let contentNode: ASDisplayNode + + private let iconNode: ASImageNode + private let wavesNode: LiveLocationWavesNode + private let titleNode: TextNode + private let subtitleNode: TextNode + private let closeButton: HighlightableButtonNode + + private var validLayout: (CGSize, CGFloat, CGFloat)? + private var peersAndMode: ([EnginePeer], LocationBroadcastNavigationAccessoryPanelMode, Bool)? + + init(accountPeerId: EnginePeer.Id, theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, tapAction: @escaping () -> Void, close: @escaping () -> Void) { + self.accountPeerId = accountPeerId + self.theme = theme + self.strings = strings + self.nameDisplayOrder = nameDisplayOrder + + self.tapAction = tapAction + self.close = close + + self.contentNode = ASDisplayNode() + + self.iconNode = ASImageNode() + self.iconNode.isLayerBacked = true + self.iconNode.displayWithoutProcessing = true + self.iconNode.displaysAsynchronously = false + self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/LiveLocationPanelIcon"), color: theme.chat.inputPanel.panelControlColor) + + self.wavesNode = LiveLocationWavesNode(color: self.theme.chat.inputPanel.panelControlColor) + + self.titleNode = TextNode() + self.titleNode.isUserInteractionEnabled = false + + self.subtitleNode = TextNode() + self.subtitleNode.isUserInteractionEnabled = false + + self.closeButton = HighlightableButtonNode() + self.closeButton.setImage(generateImage(CGSize(width: 12.0, height: 12.0), contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setStrokeColor(theme.chat.inputPanel.panelControlColor.cgColor) + context.setLineWidth(1.33) + context.setLineCap(.round) + context.move(to: CGPoint(x: 1.0, y: 1.0)) + context.addLine(to: CGPoint(x: size.width - 1.0, y: size.height - 1.0)) + context.strokePath() + context.move(to: CGPoint(x: size.width - 1.0, y: 1.0)) + context.addLine(to: CGPoint(x: 1.0, y: size.height - 1.0)) + context.strokePath() + }), for: []) + self.closeButton.hitTestSlop = UIEdgeInsets(top: -8.0, left: -8.0, bottom: -8.0, right: -8.0) + self.closeButton.displaysAsynchronously = false + + super.init() + + self.clipsToBounds = true + + self.addSubnode(self.contentNode) + + self.contentNode.addSubnode(self.iconNode) + self.contentNode.addSubnode(self.wavesNode) + self.contentNode.addSubnode(self.titleNode) + self.contentNode.addSubnode(self.subtitleNode) + self.contentNode.addSubnode(self.closeButton) + + self.closeButton.addTarget(self, action: #selector(self.closePressed), forControlEvents: .touchUpInside) + } + + override func didLoad() { + super.didLoad() + + let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))) + self.view.addGestureRecognizer(tapRecognizer) + } + + func updatePresentationData(_ presentationData: PresentationData) { + self.theme = presentationData.theme + self.strings = presentationData.strings + + self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/LiveLocationPanelIcon"), color: theme.chat.inputPanel.panelControlColor) + + self.wavesNode.color = self.theme.chat.inputPanel.panelControlColor + self.closeButton.setImage(generateImage(CGSize(width: 12.0, height: 12.0), contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setStrokeColor(theme.chat.inputPanel.panelControlColor.cgColor) + context.setLineWidth(1.33) + context.setLineCap(.round) + context.move(to: CGPoint(x: 1.0, y: 1.0)) + context.addLine(to: CGPoint(x: size.width - 1.0, y: size.height - 1.0)) + context.strokePath() + context.move(to: CGPoint(x: size.width - 1.0, y: 1.0)) + context.addLine(to: CGPoint(x: 1.0, y: size.height - 1.0)) + context.strokePath() + }), for: []) + } + + func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { + self.validLayout = (size, leftInset, rightInset) + + transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: size)) + transition.updateAlpha(node: self.contentNode, alpha: isHidden ? 0.0 : 1.0) + + let titleString = NSAttributedString(string: self.strings.Conversation_LiveLocation, font: titleFont, textColor: self.theme.rootController.navigationBar.primaryTextColor) + var subtitleString: NSAttributedString? + if let (peers, mode, canClose) = self.peersAndMode { + switch mode { + case .summary: + let text: String + if peers.count == 1 { + text = self.strings.DialogList_LiveLocationSharingTo(peers[0].displayTitle(strings: self.strings, displayOrder: self.nameDisplayOrder)).string + } else { + text = self.strings.DialogList_LiveLocationChatsCount(Int32(peers.count)) + } + subtitleString = NSAttributedString(string: text, font: subtitleFont, textColor: self.theme.rootController.navigationBar.secondaryTextColor) + case .peer: + self.closeButton.isHidden = !canClose + let filteredPeers = peers.filter { + $0.id != self.accountPeerId + } + if filteredPeers.count == 0 { + subtitleString = NSAttributedString(string: self.strings.Conversation_LiveLocationYou, font: subtitleFont, textColor: self.theme.chat.inputPanel.panelControlColor) + } else { + let otherString: String + if filteredPeers.count == 1 { + otherString = peers[0].compactDisplayTitle.replacingOccurrences(of: "*", with: "") + } else { + otherString = self.strings.Conversation_LiveLocationMembersCount(Int32(peers.count)) + } + let rawText: String + if filteredPeers.count != peers.count { + rawText = self.strings.Conversation_LiveLocationYouAndOther(otherString).string + } else { + rawText = otherString + } + let body = MarkdownAttributeSet(font: subtitleFont, textColor: self.theme.rootController.navigationBar.secondaryTextColor) + let accent = MarkdownAttributeSet(font: subtitleFont, textColor: self.theme.chat.inputPanel.panelControlColor) + subtitleString = parseMarkdownIntoAttributedString(rawText, attributes: MarkdownAttributes(body: body, bold: accent, link: body, linkAttribute: { _ in nil })) + } + + } + } + let makeTitleLayout = TextNode.asyncLayout(self.titleNode) + let makeSubtitleLayout = TextNode.asyncLayout(self.subtitleNode) + + let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .middle, constrainedSize: CGSize(width: size.width - 80.0, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + let (subtitleLayout, subtitleApply) = makeSubtitleLayout(TextNodeLayoutArguments(attributedString: subtitleString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .middle, constrainedSize: CGSize(width: size.width - 80.0, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + + let _ = titleApply() + let _ = subtitleApply() + + let minimizedTitleOffset: CGFloat = subtitleString == nil ? 6.0 : 0.0 + + let minimizedTitleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleLayout.size.width) / 2.0), y: 4.0 + minimizedTitleOffset), size: titleLayout.size) + let minimizedSubtitleFrame = CGRect(origin: CGPoint(x: floor((size.width - subtitleLayout.size.width) / 2.0), y: 20.0), size: subtitleLayout.size) + + if let image = self.iconNode.image { + transition.updateFrame(node: self.iconNode, frame: CGRect(origin: CGPoint(x: 7.0 + leftInset, y: 9.0), size: image.size)) + transition.updateFrame(node: self.wavesNode, frame: CGRect(origin: CGPoint(x: -2.0 + leftInset, y: -3.0), size: CGSize(width: 48.0, height: 48.0))) + } + + transition.updateFrame(node: self.titleNode, frame: minimizedTitleFrame) + transition.updateFrame(node: self.subtitleNode, frame: minimizedSubtitleFrame) + + let closeButtonSize = self.closeButton.measure(CGSize(width: 100.0, height: 100.0)) + transition.updateFrame(node: self.closeButton, frame: CGRect(origin: CGPoint(x: bounds.size.width - 18.0 - closeButtonSize.width - rightInset, y: minimizedTitleFrame.minY + 10.0), size: closeButtonSize)) + } + + func update(peers: [EnginePeer], mode: LocationBroadcastNavigationAccessoryPanelMode, canClose: Bool) { + self.peersAndMode = (peers, mode, canClose) + if let (size, leftInset, rightInset) = self.validLayout { + self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .immediate) + } + } + + @objc func closePressed() { + self.close() + } + + @objc func tapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + self.tapAction() + } + } +} diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/CreateLinkScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/CreateLinkScreen.swift index d3d015ac..eb682e3a 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/CreateLinkScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/CreateLinkScreen.swift @@ -109,7 +109,7 @@ private final class SheetContent: CombinedComponent { component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: theme.chat.inputPanel.panelControlColor ) )), action: { _ in diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 1b649ed2..3291c6ca 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -1259,7 +1259,6 @@ final class MediaEditorScreenComponent: Component { pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, - importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, diff --git a/submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent/BUILD b/submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent/BUILD new file mode 100644 index 00000000..4fc1322a --- /dev/null +++ b/submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent/BUILD @@ -0,0 +1,37 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "MediaPlaybackHeaderPanelComponent", + module_name = "MediaPlaybackHeaderPanelComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Display", + "//submodules/AsyncDisplayKit", + "//submodules/TelegramPresentationData", + "//submodules/AccountContext", + "//submodules/TelegramCore", + "//submodules/Components/ComponentDisplayAdapters", + "//submodules/TelegramUIPreferences", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/MediaPlayer:UniversalMediaPlayer", + "//submodules/TelegramStringFormatting", + "//submodules/ManagedAnimationNode", + "//submodules/ContextUI", + "//submodules/TelegramNotices", + "//submodules/TooltipUI", + "//submodules/TelegramUI/Components/SliderContextItem", + "//submodules/TelegramUI/Components/GlobalControlPanelsContext", + "//submodules/UndoUI", + "//submodules/OverlayStatusController", + "//submodules/Postbox", + "//submodules/PresentationDataUtils", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent/Sources/MediaNavigationAccessoryContainerNode.swift b/submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent/Sources/MediaNavigationAccessoryContainerNode.swift new file mode 100644 index 00000000..087ff60a --- /dev/null +++ b/submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent/Sources/MediaNavigationAccessoryContainerNode.swift @@ -0,0 +1,39 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import TelegramCore +import TelegramPresentationData +import AccountContext + +public final class MediaNavigationAccessoryContainerNode: ASDisplayNode, ASGestureRecognizerDelegate { + public let headerNode: MediaNavigationAccessoryHeaderNode + private var presentationData: PresentationData + + init(context: AccountContext, presentationData: PresentationData) { + self.presentationData = presentationData + + self.headerNode = MediaNavigationAccessoryHeaderNode(context: context, presentationData: presentationData) + + super.init() + + self.addSubnode(self.headerNode) + } + + func updatePresentationData(_ presentationData: PresentationData) { + self.presentationData = presentationData + self.headerNode.updatePresentationData(presentationData) + } + + func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { + transition.updateFrame(node: self.headerNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height))) + self.headerNode.updateLayout(size: CGSize(width: size.width, height: size.height), leftInset: leftInset, rightInset: rightInset, transition: transition) + } + + override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if !self.headerNode.frame.contains(point) { + return nil + } + return super.hitTest(point, with: event) + } +} diff --git a/submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent/Sources/MediaNavigationAccessoryHeaderNode.swift b/submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent/Sources/MediaNavigationAccessoryHeaderNode.swift new file mode 100644 index 00000000..387df50b --- /dev/null +++ b/submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent/Sources/MediaNavigationAccessoryHeaderNode.swift @@ -0,0 +1,817 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import SwiftSignalKit +import TelegramCore +import TelegramPresentationData +import TelegramUIPreferences +import UniversalMediaPlayer +import AccountContext +import TelegramStringFormatting +import ManagedAnimationNode +import ContextUI +import TelegramNotices +import TooltipUI +import SliderContextItem + +private let titleFont = Font.regular(12.0) +private let subtitleFont = Font.regular(10.0) + +private func normalizeValue(_ value: CGFloat) -> CGFloat { + return round(value * 10.0) / 10.0 +} + +private class MediaHeaderItemNode: ASDisplayNode { + private let titleNode: TextNode + private let subtitleNode: TextNode + + override init() { + self.titleNode = TextNode() + self.titleNode.isUserInteractionEnabled = false + self.titleNode.displaysAsynchronously = false + self.subtitleNode = TextNode() + self.subtitleNode.isUserInteractionEnabled = false + self.subtitleNode.displaysAsynchronously = false + + super.init() + + self.isUserInteractionEnabled = false + + self.addSubnode(self.titleNode) + self.addSubnode(self.subtitleNode) + } + + func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, playbackItem: SharedMediaPlaylistItem?, transition: ContainedViewLayoutTransition) -> (NSAttributedString?, NSAttributedString?, Bool) { + var rateButtonHidden = false + var titleString: NSAttributedString? + var subtitleString: NSAttributedString? + if let playbackItem = playbackItem, let displayData = playbackItem.displayData { + switch displayData { + case let .music(title, performer, _, long, _): + rateButtonHidden = !long + let titleText: String = title ?? strings.MediaPlayer_UnknownTrack + let subtitleText: String = performer ?? strings.MediaPlayer_UnknownArtist + + titleString = NSAttributedString(string: titleText, font: titleFont, textColor: theme.rootController.navigationBar.primaryTextColor) + subtitleString = NSAttributedString(string: subtitleText, font: subtitleFont, textColor: theme.rootController.navigationBar.secondaryTextColor) + case let .voice(author, peer): + rateButtonHidden = false + let titleText: String = author?.displayTitle(strings: strings, displayOrder: nameDisplayOrder) ?? "" + let subtitleText: String + if let peer = peer { + if case let .channel(peer) = peer, case .broadcast = peer.info { + subtitleText = strings.MusicPlayer_VoiceNote + } else if case .legacyGroup = peer { + subtitleText = peer.displayTitle(strings: strings, displayOrder: nameDisplayOrder) + } else if case .channel = peer { + subtitleText = peer.displayTitle(strings: strings, displayOrder: nameDisplayOrder) + } else { + subtitleText = strings.MusicPlayer_VoiceNote + } + } else { + subtitleText = strings.MusicPlayer_VoiceNote + } + + titleString = NSAttributedString(string: titleText, font: titleFont, textColor: theme.rootController.navigationBar.primaryTextColor) + subtitleString = NSAttributedString(string: subtitleText, font: subtitleFont, textColor: theme.rootController.navigationBar.secondaryTextColor) + case let .instantVideo(author, peer, timestamp): + rateButtonHidden = false + let titleText: String = author?.displayTitle(strings: strings, displayOrder: nameDisplayOrder) ?? "" + var subtitleText: String + + if let peer = peer { + switch peer { + case .legacyGroup, .channel: + subtitleText = peer.displayTitle(strings: strings, displayOrder: nameDisplayOrder) + default: + subtitleText = strings.Message_VideoMessage + } + } else { + subtitleText = strings.Message_VideoMessage + } + + if titleText == subtitleText { + subtitleText = humanReadableStringForTimestamp(strings: strings, dateTimeFormat: dateTimeFormat, timestamp: timestamp).string + } + + titleString = NSAttributedString(string: titleText, font: titleFont, textColor: theme.rootController.navigationBar.primaryTextColor) + subtitleString = NSAttributedString(string: subtitleText, font: subtitleFont, textColor: theme.rootController.navigationBar.secondaryTextColor) + } + } + let makeTitleLayout = TextNode.asyncLayout(self.titleNode) + let makeSubtitleLayout = TextNode.asyncLayout(self.subtitleNode) + + var titleSideInset: CGFloat = 12.0 + if !rateButtonHidden { + titleSideInset += 52.0 + } + + let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .middle, constrainedSize: CGSize(width: size.width - titleSideInset, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + let (subtitleLayout, subtitleApply) = makeSubtitleLayout(TextNodeLayoutArguments(attributedString: subtitleString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .middle, constrainedSize: CGSize(width: size.width - titleSideInset, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + + let _ = titleApply() + let _ = subtitleApply() + + let minimizedTitleOffset: CGFloat = subtitleString == nil ? 6.0 : 0.0 + + let minimizedTitleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleLayout.size.width) / 2.0), y: 4.0 + minimizedTitleOffset), size: titleLayout.size) + let minimizedSubtitleFrame = CGRect(origin: CGPoint(x: floor((size.width - subtitleLayout.size.width) / 2.0), y: 20.0), size: subtitleLayout.size) + + transition.updateFrame(node: self.titleNode, frame: minimizedTitleFrame) + transition.updateFrame(node: self.subtitleNode, frame: minimizedSubtitleFrame) + + return (titleString, subtitleString, rateButtonHidden) + } +} + +private func generateMaskImage(color: UIColor) -> UIImage? { + return generateImage(CGSize(width: 12.0, height: 2.0), opaque: false, rotatedContext: { size, context in + let bounds = CGRect(origin: CGPoint(), size: size) + context.clear(bounds) + + let gradientColors = [color.cgColor, color.withAlphaComponent(0.0).cgColor] as CFArray + + var locations: [CGFloat] = [0.0, 1.0] + let colorSpace = CGColorSpaceCreateDeviceRGB() + let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)! + + context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 12.0, y: 0.0), options: CGGradientDrawingOptions()) + }) +} + +public final class MediaNavigationAccessoryHeaderNode: ASDisplayNode, ASScrollViewDelegate { + private let context: AccountContext + private var theme: PresentationTheme + private var strings: PresentationStrings + private var dateTimeFormat: PresentationDateTimeFormat + private var nameDisplayOrder: PresentationPersonNameOrder + + private let scrollNode: ASScrollNode + private var initialContentOffset: CGFloat? + + private let leftMaskNode: ASImageNode + private let rightMaskNode: ASImageNode + + private let currentItemNode: MediaHeaderItemNode + private let previousItemNode: MediaHeaderItemNode + private let nextItemNode: MediaHeaderItemNode + + private let closeButton: HighlightableButtonNode + private let actionButton: HighlightTrackingButtonNode + private let playPauseIconNode: PlayPauseIconNode + private let rateButton: AudioRateButton + private let accessibilityAreaNode: AccessibilityAreaNode + + private let scrubbingNode: MediaPlayerScrubbingNode + + private var validLayout: (CGSize, CGFloat, CGFloat)? + + public var displayScrubber: Bool = true { + didSet { + self.scrubbingNode.isHidden = !self.displayScrubber + } + } + + private var tapRecognizer: UITapGestureRecognizer? + + public var tapAction: (() -> Void)? + public var close: (() -> Void)? + public var setRate: ((AudioPlaybackRate, MediaNavigationAccessoryPanel.ChangeType) -> Void)? + public var togglePlayPause: (() -> Void)? + public var playPrevious: (() -> Void)? + public var playNext: (() -> Void)? + + public var getController: (() -> ViewController?)? + public var presentInGlobalOverlay: ((ViewController) -> Void)? + + public var playbackBaseRate: AudioPlaybackRate? = nil { + didSet { + guard self.playbackBaseRate != oldValue, let playbackBaseRate = self.playbackBaseRate else { + return + } + self.rateButton.accessibilityLabel = self.strings.VoiceOver_Media_PlaybackRate + self.rateButton.accessibilityHint = self.strings.VoiceOver_Media_PlaybackRateChange + self.rateButton.accessibilityValue = playbackBaseRate.stringValue + + self.rateButton.setContent(.image(optionsRateImage(rate: playbackBaseRate.stringValue.uppercased(), color: self.theme.rootController.navigationBar.controlColor))) + } + } + + public var playbackStatus: Signal? { + didSet { + self.scrubbingNode.status = self.playbackStatus + } + } + + public var playbackItems: (SharedMediaPlaylistItem?, SharedMediaPlaylistItem?, SharedMediaPlaylistItem?)? { + didSet { + if !arePlaylistItemsEqual(self.playbackItems?.0, oldValue?.0) || !arePlaylistItemsEqual(self.playbackItems?.1, oldValue?.1) || !arePlaylistItemsEqual(self.playbackItems?.2, oldValue?.2), let layout = validLayout { + self.updateLayout(size: layout.0, leftInset: layout.1, rightInset: layout.2, transition: .immediate) + } + } + } + + private weak var tooltipController: TooltipScreen? + private let dismissedPromise = ValuePromise(false) + + public init(context: AccountContext, presentationData: PresentationData) { + self.context = context + + self.theme = presentationData.theme + self.strings = presentationData.strings + self.dateTimeFormat = presentationData.dateTimeFormat + self.nameDisplayOrder = presentationData.nameDisplayOrder + + self.scrollNode = ASScrollNode() + + self.currentItemNode = MediaHeaderItemNode() + self.previousItemNode = MediaHeaderItemNode() + self.nextItemNode = MediaHeaderItemNode() + + self.leftMaskNode = ASImageNode() + self.leftMaskNode.contentMode = .scaleToFill + self.rightMaskNode = ASImageNode() + self.rightMaskNode.contentMode = .scaleToFill + + let maskImage = generateMaskImage(color: self.theme.rootController.navigationBar.opaqueBackgroundColor) + self.leftMaskNode.image = maskImage + self.rightMaskNode.image = maskImage + + self.closeButton = HighlightableButtonNode() + self.closeButton.accessibilityLabel = presentationData.strings.VoiceOver_Media_PlaybackStop + self.closeButton.setImage(generateImage(CGSize(width: 12.0, height: 12.0), contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setStrokeColor(presentationData.theme.chat.inputPanel.panelControlColor.cgColor) + context.setLineWidth(1.33) + context.setLineCap(.round) + context.move(to: CGPoint(x: 1.0, y: 1.0)) + context.addLine(to: CGPoint(x: size.width - 1.0, y: size.height - 1.0)) + context.strokePath() + context.move(to: CGPoint(x: size.width - 1.0, y: 1.0)) + context.addLine(to: CGPoint(x: 1.0, y: size.height - 1.0)) + context.strokePath() + }), for: []) + self.closeButton.hitTestSlop = UIEdgeInsets(top: -8.0, left: -8.0, bottom: -8.0, right: -8.0) + self.closeButton.contentEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 2.0) + self.closeButton.displaysAsynchronously = false + + self.rateButton = AudioRateButton() + self.rateButton.hitTestSlop = UIEdgeInsets(top: -8.0, left: -4.0, bottom: -8.0, right: -4.0) + self.rateButton.displaysAsynchronously = false + + self.accessibilityAreaNode = AccessibilityAreaNode() + + self.actionButton = HighlightTrackingButtonNode() + self.actionButton.hitTestSlop = UIEdgeInsets(top: -8.0, left: -8.0, bottom: -8.0, right: -8.0) + self.actionButton.displaysAsynchronously = false + + self.playPauseIconNode = PlayPauseIconNode() + self.playPauseIconNode.customColor = self.theme.chat.inputPanel.panelControlColor + + self.scrubbingNode = MediaPlayerScrubbingNode(content: .standard(lineHeight: 2.0, lineCap: .square, scrubberHandle: .none, backgroundColor: .clear, foregroundColor: self.theme.chat.inputPanel.panelControlColor, bufferingColor: self.theme.chat.inputPanel.panelControlColor.withAlphaComponent(0.5), chapters: [])) + + super.init() + + self.clipsToBounds = true + + self.addSubnode(self.scrollNode) + self.scrollNode.addSubnode(self.currentItemNode) + self.scrollNode.addSubnode(self.previousItemNode) + self.scrollNode.addSubnode(self.nextItemNode) + + self.addSubnode(self.closeButton) + self.addSubnode(self.rateButton) + self.addSubnode(self.accessibilityAreaNode) + + self.actionButton.addSubnode(self.playPauseIconNode) + self.addSubnode(self.actionButton) + + self.closeButton.addTarget(self, action: #selector(self.closeButtonPressed), forControlEvents: .touchUpInside) + self.actionButton.addTarget(self, action: #selector(self.actionButtonPressed), forControlEvents: .touchUpInside) + + self.rateButton.addTarget(self, action: #selector(self.rateButtonPressed), forControlEvents: .touchUpInside) + self.rateButton.contextAction = { [weak self] sourceNode, gesture in + self?.openRateMenu(sourceNode: sourceNode, gesture: gesture) + } + + self.addSubnode(self.scrubbingNode) + + self.actionButton.highligthedChanged = { [weak self] highlighted in + if let strongSelf = self { + if highlighted { + strongSelf.actionButton.layer.removeAnimation(forKey: "opacity") + strongSelf.actionButton.alpha = 0.4 + } else { + strongSelf.actionButton.alpha = 1.0 + strongSelf.actionButton.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + } + } + } + + self.scrubbingNode.playerStatusUpdated = { [weak self] status in + guard let strongSelf = self else { + return + } + if let status = status { + strongSelf.playbackBaseRate = AudioPlaybackRate(status.baseRate) + } else { + strongSelf.playbackBaseRate = .x1 + } + } + + self.scrubbingNode.playbackStatusUpdated = { [weak self] status in + if let strongSelf = self { + let paused: Bool + if let status = status { + switch status { + case .paused: + paused = true + case let .buffering(_, whilePlaying, _, _): + paused = !whilePlaying + case .playing: + paused = false + } + } else { + paused = true + } + strongSelf.playPauseIconNode.enqueueState(paused ? .play : .pause, animated: true) + strongSelf.actionButton.accessibilityLabel = paused ? strongSelf.strings.VoiceOver_Media_PlaybackPlay : strongSelf.strings.VoiceOver_Media_PlaybackPause + } + } + } + + override public func didLoad() { + super.didLoad() + + self.view.disablesInteractiveTransitionGestureRecognizer = true + self.scrollNode.view.alwaysBounceHorizontal = true + self.scrollNode.view.delegate = self.wrappedScrollViewDelegate + self.scrollNode.view.isPagingEnabled = true + self.scrollNode.view.showsHorizontalScrollIndicator = false + self.scrollNode.view.showsVerticalScrollIndicator = false + + let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))) + self.tapRecognizer = tapRecognizer + self.view.addGestureRecognizer(tapRecognizer) + } + + public func updatePresentationData(_ presentationData: PresentationData) { + self.theme = presentationData.theme + self.strings = presentationData.strings + self.nameDisplayOrder = presentationData.nameDisplayOrder + self.dateTimeFormat = presentationData.dateTimeFormat + + let maskImage = generateMaskImage(color: self.theme.rootController.navigationBar.opaqueBackgroundColor) + self.leftMaskNode.image = maskImage + self.rightMaskNode.image = maskImage + + self.closeButton.setImage(generateImage(CGSize(width: 12.0, height: 12.0), contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setStrokeColor(presentationData.theme.chat.inputPanel.panelControlColor.cgColor) + context.setLineWidth(1.33) + context.setLineCap(.round) + context.move(to: CGPoint(x: 1.0, y: 1.0)) + context.addLine(to: CGPoint(x: size.width - 1.0, y: size.height - 1.0)) + context.strokePath() + context.move(to: CGPoint(x: size.width - 1.0, y: 1.0)) + context.addLine(to: CGPoint(x: 1.0, y: size.height - 1.0)) + context.strokePath() + }), for: []) + self.playPauseIconNode.customColor = self.theme.chat.inputPanel.panelControlColor + self.scrubbingNode.updateContent(.standard(lineHeight: 2.0, lineCap: .square, scrubberHandle: .none, backgroundColor: .clear, foregroundColor: self.theme.chat.inputPanel.panelControlColor, bufferingColor: self.theme.chat.inputPanel.panelControlColor.withAlphaComponent(0.5), chapters: [])) + + if let playbackBaseRate = self.playbackBaseRate { + self.rateButton.setContent(.image(optionsRateImage(rate: playbackBaseRate.stringValue.uppercased(), color: self.theme.rootController.navigationBar.controlColor))) + } + if let (size, leftInset, rightInset) = self.validLayout { + self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .immediate) + } + } + + public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + if scrollView.isDecelerating { + self.changeTrack() + } + + self.rateButton.alpha = 0.0 + self.rateButton.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) + } + + public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { + self.changeTrack() + + self.rateButton.alpha = 1.0 + self.rateButton.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + + public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + guard !decelerate else { + return + } + self.changeTrack() + + self.rateButton.alpha = 1.0 + self.rateButton.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + + private func changeTrack() { + guard let initialContentOffset = self.initialContentOffset, abs(initialContentOffset - self.scrollNode.view.contentOffset.x) > self.bounds.width / 2.0 else { + return + } + if self.scrollNode.view.contentOffset.x < initialContentOffset { + self.playPrevious?() + } else if self.scrollNode.view.contentOffset.x > initialContentOffset { + self.playNext?() + } + } + + public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { + self.validLayout = (size, leftInset, rightInset) + + let minHeight = 40.0 + + let inset: CGFloat = 45.0 + leftInset + let constrainedSize = CGSize(width: size.width - inset * 2.0, height: size.height) + let (titleString, subtitleString, rateButtonHidden) = self.currentItemNode.updateLayout(size: constrainedSize, leftInset: leftInset, rightInset: rightInset, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, playbackItem: self.playbackItems?.0, transition: transition) + self.accessibilityAreaNode.accessibilityLabel = "\(titleString?.string ?? ""). \(subtitleString?.string ?? "")" + self.rateButton.isHidden = rateButtonHidden + + let _ = self.previousItemNode.updateLayout(size: constrainedSize, leftInset: 0.0, rightInset: 0.0, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, playbackItem: self.playbackItems?.1, transition: transition) + let _ = self.nextItemNode.updateLayout(size: constrainedSize, leftInset: 0.0, rightInset: 0.0, theme: self.theme, strings: self.strings, dateTimeFormat: self.dateTimeFormat, nameDisplayOrder: self.nameDisplayOrder, playbackItem: self.playbackItems?.2, transition: transition) + + let constrainedBounds = CGRect(origin: CGPoint(), size: constrainedSize) + transition.updateFrame(node: self.scrollNode, frame: constrainedBounds.offsetBy(dx: inset, dy: 0.0)) + + var contentSize = constrainedSize + var contentOffset: CGFloat = 0.0 + if self.playbackItems?.1 != nil { + contentSize.width += constrainedSize.width + contentOffset = constrainedSize.width + } + if self.playbackItems?.2 != nil { + contentSize.width += constrainedSize.width + } + + self.previousItemNode.frame = constrainedBounds.offsetBy(dx: contentOffset - constrainedSize.width, dy: 0.0) + self.currentItemNode.frame = constrainedBounds.offsetBy(dx: contentOffset, dy: 0.0) + self.nextItemNode.frame = constrainedBounds.offsetBy(dx: contentOffset + constrainedSize.width, dy: 0.0) + + self.leftMaskNode.frame = CGRect(x: inset, y: 0.0, width: 12.0, height: minHeight) + self.rightMaskNode.transform = CATransform3DMakeScale(-1.0, 1.0, 1.0) + self.rightMaskNode.frame = CGRect(x: size.width - inset - 12.0, y: 0.0, width: 12.0, height: minHeight) + + if !self.scrollNode.view.isTracking && !self.scrollNode.view.isTracking { + self.scrollNode.view.contentSize = contentSize + self.scrollNode.view.contentOffset = CGPoint(x: contentOffset, y: 0.0) + self.initialContentOffset = contentOffset + } + + let bounds = CGRect(origin: CGPoint(), size: size) + let closeButtonSize = self.closeButton.measure(CGSize(width: 100.0, height: 100.0)) + transition.updateFrame(node: self.closeButton, frame: CGRect(origin: CGPoint(x: bounds.size.width - 44.0 - rightInset, y: 0.0), size: CGSize(width: 44.0, height: minHeight))) + let rateButtonSize = CGSize(width: 30.0, height: minHeight) + transition.updateFrame(node: self.rateButton, frame: CGRect(origin: CGPoint(x: bounds.size.width - 27.0 - closeButtonSize.width - rateButtonSize.width - rightInset, y: -2.0), size: rateButtonSize)) + + transition.updateFrame(node: self.playPauseIconNode, frame: CGRect(origin: CGPoint(x: 6.0, y: 6.0 + UIScreenPixel), size: CGSize(width: 28.0, height: 28.0))) + transition.updateFrame(node: self.actionButton, frame: CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: 40.0, height: 40.0))) + transition.updateFrame(node: self.scrubbingNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 40.0 - 2.0), size: CGSize(width: size.width, height: 2.0))) + + self.accessibilityAreaNode.frame = CGRect(origin: CGPoint(x: self.actionButton.frame.maxX, y: 0.0), size: CGSize(width: self.rateButton.frame.minX - self.actionButton.frame.maxX, height: minHeight)) + } + + @objc public func closeButtonPressed() { + self.close?() + } + + @objc public func rateButtonPressed() { + var changeType: MediaNavigationAccessoryPanel.ChangeType = .preset + let nextRate: AudioPlaybackRate + if let rate = self.playbackBaseRate { + switch rate { + case .x0_5, .x2: + nextRate = .x1 + case .x1: + nextRate = .x1_5 + case .x1_5: + nextRate = .x2 + default: + if rate.doubleValue < 0.5 { + nextRate = .x0_5 + } else if rate.doubleValue < 1.0 { + nextRate = .x1 + } else if rate.doubleValue < 1.5 { + nextRate = .x1_5 + } else if rate.doubleValue < 2.0 { + nextRate = .x2 + } else { + nextRate = .x1 + } + changeType = .sliderCommit(rate.doubleValue, nextRate.doubleValue) + } + } else { + nextRate = .x1_5 + } + self.setRate?(nextRate, changeType) + + let frame = self.rateButton.view.convert(self.rateButton.bounds, to: nil) + + let _ = (ApplicationSpecificNotice.incrementAudioRateOptionsTip(accountManager: self.context.sharedContext.accountManager) + |> deliverOnMainQueue).start(next: { [weak self] value in + if let strongSelf = self, let controller = strongSelf.getController?(), value == 2 { + let tooltipController = TooltipScreen(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, text: .plain(text: strongSelf.strings.Conversation_AudioRateOptionsTooltip), style: .default, icon: nil, location: .point(frame.offsetBy(dx: 0.0, dy: 4.0), .bottom), displayDuration: .custom(3.0), inset: 3.0, shouldDismissOnTouch: { _, _ in + return .dismiss(consume: false) + }) + controller.present(tooltipController, in: .window(.root)) + strongSelf.tooltipController = tooltipController + } + }) + } + + private func speedList(strings: PresentationStrings) -> [(String, String, AudioPlaybackRate)] { + let speedList: [(String, String, AudioPlaybackRate)] = [ + ("0.5x", "0.5x", .x0_5), + (strings.PlaybackSpeed_Normal, "1x", .x1), + ("1.5x", "1.5x", .x1_5), + ("2x", "2x", .x2) + ] + return speedList + } + + private func contextMenuSpeedItems(scheduleTooltip: @escaping (MediaNavigationAccessoryPanel.ChangeType?) -> Void) -> Signal { + var presetItems: [ContextMenuItem] = [] + + let previousRate = self.playbackBaseRate + let previousValue = self.playbackBaseRate?.doubleValue ?? 1.0 + let sliderValuePromise = ValuePromise(nil) + let sliderItem: ContextMenuItem = .custom(SliderContextItem(minValue: 0.2, maxValue: 2.5, value: previousValue, valueChanged: { [weak self] newValue, finished in + let newValue = normalizeValue(newValue) + self?.setRate?(AudioPlaybackRate(newValue), .sliderChange) + sliderValuePromise.set(newValue) + if finished { + scheduleTooltip(.sliderCommit(previousValue, newValue)) + } + }), true) + + let theme = self.context.sharedContext.currentPresentationData.with { $0 }.theme + for (text, _, rate) in self.speedList(strings: self.strings) { + let isSelected = self.playbackBaseRate == rate + presetItems.append(.action(ContextMenuActionItem(text: text, icon: { _ in return nil }, iconSource: ContextMenuActionItemIconSource(size: CGSize(width: 24.0, height: 24.0), signal: sliderValuePromise.get() + |> map { value in + if isSelected && value == nil { + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) + } else { + return nil + } + }), action: { [weak self] _, f in + scheduleTooltip(nil) + f(.default) + + if let previousRate, previousRate.isPreset { + self?.setRate?(rate, .preset) + } else { + self?.setRate?(rate, .sliderCommit(previousValue, rate.doubleValue)) + } + }))) + } + + return .single(ContextController.Items(content: .twoLists(presetItems, [sliderItem]))) + } + + private func openRateMenu(sourceNode: ASDisplayNode, gesture: ContextGesture?) { + guard let controller = self.getController?() else { + return + } + var scheduledTooltip: MediaNavigationAccessoryPanel.ChangeType? + let items = self.contextMenuSpeedItems(scheduleTooltip: { change in + scheduledTooltip = change + }) + let contextController = ContextController(presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceNode: self.rateButton.referenceNode, shouldBeDismissed: self.dismissedPromise.get())), items: items, gesture: gesture) + contextController.dismissed = { [weak self] in + if let scheduledTooltip, let self, let rate = self.playbackBaseRate { + self.setRate?(rate, scheduledTooltip) + } + } + self.presentInGlobalOverlay?(contextController) + } + + @objc public func actionButtonPressed() { + self.togglePlayPause?() + } + + @objc public func tapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + self.tapAction?() + } + } +} + +private enum PlayPauseIconNodeState: Equatable { + case play + case pause +} + +private final class PlayPauseIconNode: ManagedAnimationNode { + private let duration: Double = 0.35 + private var iconState: PlayPauseIconNodeState = .pause + + init() { + super.init(size: CGSize(width: 28.0, height: 28.0)) + + self.trackTo(item: ManagedAnimationItem(source: .local("anim_playpause"), frames: .range(startFrame: 41, endFrame: 41), duration: 0.01)) + } + + func enqueueState(_ state: PlayPauseIconNodeState, animated: Bool) { + guard self.iconState != state else { + return + } + + let previousState = self.iconState + self.iconState = state + + switch previousState { + case .pause: + switch state { + case .play: + if animated { + self.trackTo(item: ManagedAnimationItem(source: .local("anim_playpause"), frames: .range(startFrame: 41, endFrame: 83), duration: self.duration)) + } else { + self.trackTo(item: ManagedAnimationItem(source: .local("anim_playpause"), frames: .range(startFrame: 0, endFrame: 0), duration: 0.01)) + } + case .pause: + break + } + case .play: + switch state { + case .pause: + if animated { + self.trackTo(item: ManagedAnimationItem(source: .local("anim_playpause"), frames: .range(startFrame: 0, endFrame: 41), duration: self.duration)) + } else { + self.trackTo(item: ManagedAnimationItem(source: .local("anim_playpause"), frames: .range(startFrame: 41, endFrame: 41), duration: 0.01)) + } + case .play: + break + } + } + } +} + +private func optionsRateImage(rate: String, color: UIColor = .white) -> UIImage? { + let isLarge = "".isEmpty + return generateImage(isLarge ? CGSize(width: 30.0, height: 30.0) : CGSize(width: 24.0, height: 24.0), rotatedContext: { size, context in + UIGraphicsPushContext(context) + + context.clear(CGRect(origin: CGPoint(), size: size)) + + if let image = generateTintedImage(image: UIImage(bundleImageName: isLarge ? "Chat/Context Menu/Playspeed30" : "Chat/Context Menu/Playspeed24"), color: color) { + image.draw(at: CGPoint(x: 0.0, y: 0.0)) + } + + let string = NSMutableAttributedString(string: rate, font: Font.with(size: isLarge ? 11.0 : 10.0, design: .round, weight: .semibold), textColor: color) + + var offset = CGPoint(x: 1.0, y: 0.0) + if rate.count >= 3 { + if rate == "0.5x" { + string.addAttribute(.kern, value: -0.8 as NSNumber, range: NSRange(string.string.startIndex ..< string.string.endIndex, in: string.string)) + offset.x += -0.5 + } else { + string.addAttribute(.kern, value: -0.5 as NSNumber, range: NSRange(string.string.startIndex ..< string.string.endIndex, in: string.string)) + offset.x += -0.3 + } + } else { + offset.x += -0.3 + } + + if !isLarge { + offset.x *= 0.5 + offset.y *= 0.5 + } + + let boundingRect = string.boundingRect(with: size, options: [], context: nil) + string.draw(at: CGPoint(x: offset.x + floor((size.width - boundingRect.width) / 2.0), y: offset.y + floor((size.height - boundingRect.height) / 2.0))) + + UIGraphicsPopContext() + }) +} + +public final class AudioRateButton: HighlightableButtonNode { + public enum Content { + case image(UIImage?) + } + + public let referenceNode: ContextReferenceContentNode + let containerNode: ContextControllerSourceNode + private let iconNode: ASImageNode + + public var contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? + + private let wide: Bool + + public init(wide: Bool = false) { + self.wide = wide + + self.referenceNode = ContextReferenceContentNode() + self.containerNode = ContextControllerSourceNode() + self.containerNode.animateScale = false + self.iconNode = ASImageNode() + self.iconNode.displaysAsynchronously = false + self.iconNode.displayWithoutProcessing = true + self.iconNode.contentMode = .scaleToFill + + super.init() + + self.containerNode.addSubnode(self.referenceNode) + self.referenceNode.addSubnode(self.iconNode) + self.addSubnode(self.containerNode) + + self.containerNode.shouldBegin = { [weak self] location in + guard let strongSelf = self, let _ = strongSelf.contextAction else { + return false + } + return true + } + self.containerNode.activated = { [weak self] gesture, _ in + guard let strongSelf = self else { + return + } + strongSelf.contextAction?(strongSelf.containerNode, gesture) + } + + self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 26.0, height: 44.0)) + self.referenceNode.frame = self.containerNode.bounds + + if let image = self.iconNode.image { + self.iconNode.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - image.size.width) / 2.0), y: floor((self.containerNode.bounds.height - image.size.height) / 2.0)), size: image.size) + } + + self.hitTestSlop = UIEdgeInsets(top: 0.0, left: -4.0, bottom: 0.0, right: -4.0) + } + + private var content: Content? + public func setContent(_ content: Content, animated: Bool = false) { + if animated { + if let snapshotView = self.referenceNode.view.snapshotContentTree() { + snapshotView.frame = self.referenceNode.frame + self.view.addSubview(snapshotView) + + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + snapshotView.layer.animateScale(from: 1.0, to: 0.1, duration: 0.3, removeOnCompletion: false) + + self.iconNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + self.iconNode.layer.animateScale(from: 0.1, to: 1.0, duration: 0.3) + } + + switch content { + case let .image(image): + if let image = image { + self.iconNode.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - image.size.width) / 2.0), y: floor((self.containerNode.bounds.height - image.size.height) / 2.0)), size: image.size) + } + + self.iconNode.image = image + self.iconNode.isHidden = false + } + } else { + self.content = content + switch content { + case let .image(image): + if let image = image { + self.iconNode.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - image.size.width) / 2.0), y: floor((self.containerNode.bounds.height - image.size.height) / 2.0)), size: image.size) + } + + self.iconNode.image = image + self.iconNode.isHidden = false + } + } + } + + public override func didLoad() { + super.didLoad() + self.view.isOpaque = false + } + + public override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { + return CGSize(width: wide ? 32.0 : 22.0, height: 44.0) + } + + func onLayout() { + } +} + +private final class HeaderContextReferenceContentSource: ContextReferenceContentSource { + private let controller: ViewController + private let sourceNode: ContextReferenceContentNode + + var shouldBeDismissed: Signal + + init(controller: ViewController, sourceNode: ContextReferenceContentNode, shouldBeDismissed: Signal) { + self.controller = controller + self.sourceNode = sourceNode + self.shouldBeDismissed = shouldBeDismissed + } + + func transitionInfo() -> ContextControllerReferenceViewInfo? { + return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds) + } +} diff --git a/submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent/Sources/MediaNavigationAccessoryPanel.swift b/submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent/Sources/MediaNavigationAccessoryPanel.swift new file mode 100644 index 00000000..bf1939fd --- /dev/null +++ b/submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent/Sources/MediaNavigationAccessoryPanel.swift @@ -0,0 +1,91 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import TelegramCore +import AccountContext +import TelegramUIPreferences +import TelegramPresentationData + +public final class MediaNavigationAccessoryPanel: ASDisplayNode { + public let containerNode: MediaNavigationAccessoryContainerNode + + public enum ChangeType { + case preset + case sliderChange + case sliderCommit(CGFloat, CGFloat) + } + + public var close: (() -> Void)? + public var setRate: ((AudioPlaybackRate, ChangeType) -> Void)? + public var togglePlayPause: (() -> Void)? + public var tapAction: (() -> Void)? + public var playPrevious: (() -> Void)? + public var playNext: (() -> Void)? + + public var getController: (() -> ViewController?)? + public var presentInGlobalOverlay: ((ViewController) -> Void)? + + public init(context: AccountContext, presentationData: PresentationData) { + self.containerNode = MediaNavigationAccessoryContainerNode(context: context, presentationData: presentationData) + + super.init() + + self.clipsToBounds = true + + self.addSubnode(self.containerNode) + + self.containerNode.headerNode.close = { [weak self] in + if let strongSelf = self, let close = strongSelf.close { + close() + } + } + self.containerNode.headerNode.setRate = { [weak self] rate, type in + self?.setRate?(rate, type) + } + self.containerNode.headerNode.togglePlayPause = { [weak self] in + if let strongSelf = self, let togglePlayPause = strongSelf.togglePlayPause { + togglePlayPause() + } + } + self.containerNode.headerNode.tapAction = { [weak self] in + if let strongSelf = self, let tapAction = strongSelf.tapAction { + tapAction() + } + } + self.containerNode.headerNode.playPrevious = { [weak self] in + if let strongSelf = self, let playPrevious = strongSelf.playPrevious { + playPrevious() + } + } + self.containerNode.headerNode.playNext = { [weak self] in + if let strongSelf = self, let playNext = strongSelf.playNext { + playNext() + } + } + + self.containerNode.headerNode.getController = { [weak self] in + if let strongSelf = self, let getController = strongSelf.getController { + return getController() + } else { + return nil + } + } + + self.containerNode.headerNode.presentInGlobalOverlay = { [weak self] c in + if let strongSelf = self, let presentInGlobalOverlay = strongSelf.presentInGlobalOverlay { + presentInGlobalOverlay(c) + } + } + } + + public func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { + transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: size)) + transition.updateAlpha(node: self.containerNode, alpha: isHidden ? 0.0 : 1.0) + self.containerNode.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: transition) + } + + override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + return self.containerNode.hitTest(point, with: event) + } +} diff --git a/submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent/Sources/MediaPlaybackHeaderPanelComponent.swift b/submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent/Sources/MediaPlaybackHeaderPanelComponent.swift new file mode 100644 index 00000000..c8275e69 --- /dev/null +++ b/submodules/TelegramUI/Components/MediaPlaybackHeaderPanelComponent/Sources/MediaPlaybackHeaderPanelComponent.swift @@ -0,0 +1,362 @@ +import Foundation +import UIKit +import Display +import TelegramPresentationData +import ComponentFlow +import ComponentDisplayAdapters +import AccountContext +import TelegramCore +import GlobalControlPanelsContext +import SwiftSignalKit +import UniversalMediaPlayer +import UndoUI +import OverlayStatusController +import TelegramUIPreferences +import Postbox +import PresentationDataUtils + +public final class MediaPlaybackHeaderPanelComponent: Component { + public let context: AccountContext + public let theme: PresentationTheme + public let strings: PresentationStrings + public let data: GlobalControlPanelsContext.MediaPlayback + public let controller: () -> ViewController? + + public init( + context: AccountContext, + theme: PresentationTheme, + strings: PresentationStrings, + data: GlobalControlPanelsContext.MediaPlayback, + controller: @escaping () -> ViewController? + ) { + self.context = context + self.theme = theme + self.strings = strings + self.data = data + self.controller = controller + } + + public static func ==(lhs: MediaPlaybackHeaderPanelComponent, rhs: MediaPlaybackHeaderPanelComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.theme !== rhs.theme { + return false + } + if lhs.strings !== rhs.strings { + return false + } + if lhs.data != rhs.data { + return false + } + return true + } + + public final class View: UIView { + private var panel: MediaNavigationAccessoryPanel? + + private var component: MediaPlaybackHeaderPanelComponent? + private weak var state: EmptyComponentState? + + private var playlistPreloadDisposable: Disposable? + + public override init(frame: CGRect) { + super.init(frame: frame) + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + self.playlistPreloadDisposable?.dispose() + } + + func update(component: MediaPlaybackHeaderPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + let themeUpdated = self.component?.theme !== component.theme + + self.component = component + self.state = state + + let panel: MediaNavigationAccessoryPanel + if let current = self.panel { + panel = current + } else { + panel = MediaNavigationAccessoryPanel(context: component.context, presentationData: PresentationData( + strings: component.strings, + theme: component.theme, + autoNightModeTriggered: false, + chatWallpaper: .builtin(WallpaperSettings()), + chatFontSize: .regular, + chatBubbleCorners: PresentationChatBubbleCorners(mainRadius: 0.0, auxiliaryRadius: 0.0, mergeBubbleCorners: true), + listsFontSize: .regular, + dateTimeFormat: PresentationDateTimeFormat(), + nameDisplayOrder: .firstLast, + nameSortOrder: .firstLast, + reduceMotion: false, + largeEmoji: false + )) + self.panel = panel + self.addSubview(panel.view) + + let delayedStatus = component.context.sharedContext.mediaManager.globalMediaPlayerState + |> mapToSignal { value -> Signal<(Account, SharedMediaPlayerItemPlaybackStateOrLoading, MediaManagerPlayerType)?, NoError> in + guard let value = value else { + return .single(nil) + } + switch value.1 { + case .state: + return .single(value) + case .loading: + return .single(value) |> delay(0.1, queue: .mainQueue()) + } + } + + panel.containerNode.headerNode.playbackStatus = delayedStatus + |> map { state -> MediaPlayerStatus in + if let stateOrLoading = state?.1, case let .state(state) = stateOrLoading { + return state.status + } else { + return MediaPlayerStatus(generationTimestamp: 0.0, duration: 0.0, dimensions: CGSize(), timestamp: 0.0, baseRate: 1.0, seekId: 0, status: .paused, soundEnabled: true) + } + } + + panel.containerNode.headerNode.displayScrubber = component.data.item.playbackData?.type != .instantVideo + panel.getController = { [weak self] in + guard let self, let component = self.component else { + return nil + } + return component.controller() + } + panel.presentInGlobalOverlay = { [weak self] c in + guard let self, let component = self.component else { + return + } + component.controller()?.presentInGlobalOverlay(c) + } + panel.close = { [weak self] in + guard let self, let component = self.component else { + return + } + component.context.sharedContext.mediaManager.setPlaylist(nil, type: component.data.kind, control: SharedMediaPlayerControlAction.playback(.pause)) + } + panel.setRate = { [weak self] rate, changeType in + guard let self, let component = self.component else { + return + } + let _ = (component.context.sharedContext.accountManager.transaction { transaction -> AudioPlaybackRate in + let settings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings)?.get(MusicPlaybackSettings.self) ?? MusicPlaybackSettings.defaultSettings + + transaction.updateSharedData(ApplicationSpecificSharedDataKeys.musicPlaybackSettings, { _ in + return PreferencesEntry(settings.withUpdatedVoicePlaybackRate(rate)) + }) + return rate + } + |> deliverOnMainQueue).start(next: { [weak self] baseRate in + guard let self, let component = self.component else { + return + } + component.context.sharedContext.mediaManager.playlistControl(.setBaseRate(baseRate), type: component.data.kind) + + var hasTooltip = false + component.controller()?.forEachController({ controller in + if let controller = controller as? UndoOverlayController { + hasTooltip = true + controller.dismissWithCommitAction() + } + return true + }) + + let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } + let text: String? + let rate: CGFloat? + if case let .sliderCommit(previousValue, newValue) = changeType { + let value = String(format: "%0.1f", baseRate.doubleValue) + if baseRate == .x1 { + text = presentationData.strings.Conversation_AudioRateTooltipNormal + } else { + text = presentationData.strings.Conversation_AudioRateTooltipCustom(value).string + } + if newValue > previousValue { + rate = .infinity + } else if newValue < previousValue { + rate = -.infinity + } else { + rate = nil + } + } else if baseRate == .x1 { + text = presentationData.strings.Conversation_AudioRateTooltipNormal + rate = 1.0 + } else if baseRate == .x1_5 { + text = presentationData.strings.Conversation_AudioRateTooltip15X + rate = 1.5 + } else if baseRate == .x2 { + text = presentationData.strings.Conversation_AudioRateTooltipSpeedUp + rate = 2.0 + } else { + text = nil + rate = nil + } + var showTooltip = true + if case .sliderChange = changeType { + showTooltip = false + } + if let rate, let text, showTooltip { + let controller = UndoOverlayController( + presentationData: presentationData, + content: .audioRate( + rate: rate, + text: text + ), + elevatedLayout: false, + animateInAsReplacement: hasTooltip, + action: { action in + return true + } + ) + //strongSelf.audioRateTooltipController = controller + component.controller()?.present(controller, in: .current) + } + }) + } + panel.togglePlayPause = { [weak self] in + guard let self, let component = self.component else { + return + } + component.context.sharedContext.mediaManager.playlistControl(.playback(.togglePlayPause), type: component.data.kind) + } + panel.playPrevious = { [weak self] in + guard let self, let component = self.component else { + return + } + component.context.sharedContext.mediaManager.playlistControl(.next, type: component.data.kind) + } + panel.playNext = { [weak self] in + guard let self, let component = self.component else { + return + } + component.context.sharedContext.mediaManager.playlistControl(.previous, type: component.data.kind) + } + panel.tapAction = { [weak self] in + guard let self, let component = self.component, let controller = component.controller(), let navigationController = controller.navigationController as? NavigationController else { + return + } + + if let id = component.data.item.id as? PeerMessagesMediaPlaylistItemId, let playlistLocation = component.data.playlistLocation as? PeerMessagesPlaylistLocation { + if case .music = component.data.kind { + switch playlistLocation { + case .custom, .savedMusic: + let controllerContext: AccountContext + if component.data.account.id == component.context.account.id { + controllerContext = component.context + } else { + controllerContext = component.context.sharedContext.makeTempAccountContext(account: component.data.account) + } + let playerController = component.context.sharedContext.makeOverlayAudioPlayerController(context: controllerContext, chatLocation: .peer(id: id.messageId.peerId), type: component.data.kind, initialMessageId: id.messageId, initialOrder: component.data.playbackOrder, playlistLocation: playlistLocation, parentNavigationController: navigationController) + self.window?.endEditing(true) + controller.present(playerController, in: .window(.root)) + case let .messages(chatLocation, _, _): + let signal = component.context.sharedContext.messageFromPreloadedChatHistoryViewForLocation(id: id.messageId, location: ChatHistoryLocationInput(content: .InitialSearch(subject: MessageHistoryInitialSearchSubject(location: .id(id.messageId)), count: 60, highlight: true, setupReply: false), id: 0), context: component.context, chatLocation: chatLocation, subject: nil, chatLocationContextHolder: Atomic(value: nil), tag: .tag(MessageTags.music)) + + var cancelImpl: (() -> Void)? + let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } + let progressSignal = Signal { [weak self] subscriber in + let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: { + cancelImpl?() + })) + self?.component?.controller()?.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 = MetaDisposable() + var progressStarted = false + self.playlistPreloadDisposable?.dispose() + self.playlistPreloadDisposable = (signal + |> afterDisposed { + Queue.mainQueue().async { + progressDisposable.dispose() + } + } + |> deliverOnMainQueue).start(next: { [weak self] index in + guard let self, let component = self.component else { + return + } + if let _ = index.0 { + let controllerContext: AccountContext + if component.data.account.id == component.context.account.id { + controllerContext = component.context + } else { + controllerContext = component.context.sharedContext.makeTempAccountContext(account: component.data.account) + } + let playerController = component.context.sharedContext.makeOverlayAudioPlayerController(context: controllerContext, chatLocation: chatLocation, type: component.data.kind, initialMessageId: id.messageId, initialOrder: component.data.playbackOrder, playlistLocation: nil, parentNavigationController: navigationController) + self.window?.endEditing(true) + controller.present(playerController, in: .window(.root)) + } else if index.1 { + if !progressStarted { + progressStarted = true + progressDisposable.set(progressSignal.start()) + } + } + }, completed: { + }) + cancelImpl = { [weak self] in + self?.playlistPreloadDisposable?.dispose() + } + default: + break + } + } else { + component.context.sharedContext.navigateToChat(accountId: component.context.account.id, peerId: id.messageId.peerId, messageId: id.messageId) + } + } + } + } + + let size = CGSize(width: availableSize.width, height: 40.0) + let panelFrame = CGRect(origin: CGPoint(), size: size) + transition.setFrame(view: panel.view, frame: panelFrame) + panel.updateLayout(size: panelFrame.size, leftInset: 0.0, rightInset: 0.0, transition: transition.containedViewLayoutTransition) + + switch component.data.playbackOrder { + case .regular: + panel.containerNode.headerNode.playbackItems = (component.data.item, component.data.previousItem, component.data.nextItem) + case .reversed: + panel.containerNode.headerNode.playbackItems = (component.data.item, component.data.nextItem, component.data.previousItem) + case .random: + panel.containerNode.headerNode.playbackItems = (component.data.item, nil, nil) + } + + if themeUpdated { + panel.containerNode.updatePresentationData(PresentationData( + strings: component.strings, + theme: component.theme, + autoNightModeTriggered: false, + chatWallpaper: .builtin(WallpaperSettings()), + chatFontSize: .regular, + chatBubbleCorners: PresentationChatBubbleCorners(mainRadius: 0.0, auxiliaryRadius: 0.0, mergeBubbleCorners: true), + listsFontSize: .regular, + dateTimeFormat: PresentationDateTimeFormat(), + nameDisplayOrder: .firstLast, + nameSortOrder: .firstLast, + reduceMotion: false, + largeEmoji: false + )) + } + + return size + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/MeshTransform/BUILD b/submodules/TelegramUI/Components/MeshTransform/BUILD new file mode 100644 index 00000000..cde3e516 --- /dev/null +++ b/submodules/TelegramUI/Components/MeshTransform/BUILD @@ -0,0 +1,18 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "MeshTransform", + module_name = "MeshTransform", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/TelegramUI/Components/MeshTransform/MeshTransformApi" + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/MeshTransform/MeshTransformApi/BUILD b/submodules/TelegramUI/Components/MeshTransform/MeshTransformApi/BUILD new file mode 100644 index 00000000..f1f97638 --- /dev/null +++ b/submodules/TelegramUI/Components/MeshTransform/MeshTransformApi/BUILD @@ -0,0 +1,22 @@ + +objc_library( + name = "MeshTransformApi", + enable_modules = True, + module_name = "MeshTransformApi", + srcs = glob([ + "Sources/*.m", + ]), + hdrs = glob([ + "PublicHeaders/**/*.h", + ]), + includes = [ + "PublicHeaders", + ], + sdk_frameworks = [ + "Foundation", + "UIKit", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/MeshTransform/MeshTransformApi/PublicHeaders/MeshTransformApi/MeshTransformApi.h b/submodules/TelegramUI/Components/MeshTransform/MeshTransformApi/PublicHeaders/MeshTransformApi/MeshTransformApi.h new file mode 100644 index 00000000..3691bb61 --- /dev/null +++ b/submodules/TelegramUI/Components/MeshTransform/MeshTransformApi/PublicHeaders/MeshTransformApi/MeshTransformApi.h @@ -0,0 +1,33 @@ +#ifndef MeshTransformApi_h +#define MeshTransformApi_h + +#import +#import + +typedef struct MeshTransformMeshFace { + unsigned int indices[4]; + float w[4]; +} MeshTransformMeshFace; + +typedef struct MeshTransformPoint3D { + CGFloat x; + CGFloat y; + CGFloat z; +} MeshTransformPoint3D; + +typedef struct MeshTransformMeshVertex { + CGPoint from; + MeshTransformPoint3D to; +} MeshTransformMeshVertex; + +@protocol MeshTransformClass + +- (id)meshTransformWithVertexCount:(NSUInteger)vertexCount + vertices:(MeshTransformMeshVertex *)vertices + faceCount:(NSUInteger)faceCount + faces:(MeshTransformMeshFace *)faces + depthNormalization:(NSString *)depthNormalization; + +@end + +#endif diff --git a/submodules/TelegramUI/Components/MeshTransform/MeshTransformApi/Sources/MeshTransformApi.m b/submodules/TelegramUI/Components/MeshTransform/MeshTransformApi/Sources/MeshTransformApi.m new file mode 100644 index 00000000..a3272be0 --- /dev/null +++ b/submodules/TelegramUI/Components/MeshTransform/MeshTransformApi/Sources/MeshTransformApi.m @@ -0,0 +1,2 @@ +#import + diff --git a/submodules/TelegramUI/Components/MeshTransform/Sources/GenerateMesh.swift b/submodules/TelegramUI/Components/MeshTransform/Sources/GenerateMesh.swift new file mode 100644 index 00000000..e6aa5704 --- /dev/null +++ b/submodules/TelegramUI/Components/MeshTransform/Sources/GenerateMesh.swift @@ -0,0 +1,627 @@ +import Foundation +import UIKit + +private func a(_ a1: CGFloat, _ a2: CGFloat) -> CGFloat +{ + return 1.0 - 3.0 * a2 + 3.0 * a1 +} + +private func b(_ a1: CGFloat, _ a2: CGFloat) -> CGFloat +{ + return 3.0 * a2 - 6.0 * a1 +} + +private func c(_ a1: CGFloat) -> CGFloat +{ + return 3.0 * a1 +} + +private func calcBezier(_ t: CGFloat, _ a1: CGFloat, _ a2: CGFloat) -> CGFloat +{ + return ((a(a1, a2)*t + b(a1, a2))*t + c(a1)) * t +} + +private func calcSlope(_ t: CGFloat, _ a1: CGFloat, _ a2: CGFloat) -> CGFloat +{ + return 3.0 * a(a1, a2) * t * t + 2.0 * b(a1, a2) * t + c(a1) +} + +private func getTForX(_ x: CGFloat, _ x1: CGFloat, _ x2: CGFloat) -> CGFloat { + var t = x + var i = 0 + while i < 4 { + let currentSlope = calcSlope(t, x1, x2) + if currentSlope == 0.0 { + return t + } else { + let currentX = calcBezier(t, x1, x2) - x + t -= currentX / currentSlope + } + + i += 1 + } + + return t +} + +private func bezierPoint(_ x1: CGFloat, _ y1: CGFloat, _ x2: CGFloat, _ y2: CGFloat, _ x: CGFloat) -> CGFloat +{ + var value = calcBezier(getTForX(x, x1, x2), y1, y2) + if value >= 0.997 { + value = 1.0 + } + return value +} + +/// Bezier control points for displacement easing curve +public struct DisplacementBezier { + var x1: CGFloat + var y1: CGFloat + var x2: CGFloat + var y2: CGFloat + + public init(x1: CGFloat, y1: CGFloat, x2: CGFloat, y2: CGFloat) { + self.x1 = x1 + self.y1 = y1 + self.x2 = x2 + self.y2 = y2 + } +} + +/// Computes signed distance from a point to the edge of a rounded rectangle. +/// Returns negative inside, zero on edge, positive outside. +/// All values in points. +public func roundedRectSDF(x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat, cornerRadius: CGFloat) -> CGFloat { + // Center the point (SDF formula assumes center at origin) + let px = x - width / 2 + let py = y - height / 2 + + // Half extents of the box + let bx = width / 2 + let by = height / 2 + + // Standard rounded box SDF (Inigo Quilez formula) + let qx = abs(px) - bx + cornerRadius + let qy = abs(py) - by + cornerRadius + + let outsideDist = hypot(max(qx, 0), max(qy, 0)) + let insideDist = min(max(qx, qy), 0) + + return outsideDist + insideDist - cornerRadius +} + +/// Computes the gradient (outward normal) of the rounded rect SDF. +/// Returns normalized direction perpendicular to the nearest edge point. +public func roundedRectGradient(x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat, cornerRadius: CGFloat) -> (nx: CGFloat, ny: CGFloat) { + // Center the point + let px = x - width / 2 + let py = y - height / 2 + + // Half extents + let bx = width / 2 + let by = height / 2 + + // q values from SDF formula + let qx = abs(px) - bx + cornerRadius + let qy = abs(py) - by + cornerRadius + + var nx: CGFloat = 0 + var ny: CGFloat = 0 + + if qx > 0 && qy > 0 { + // Corner region - normal points radially from corner arc center + let d = hypot(qx, qy) + if d > 0 { + nx = qx / d + ny = qy / d + } + } else if qx > qy { + // Nearest point is on vertical edge (left or right) + nx = 1 + ny = 0 + } else { + // Nearest point is on horizontal edge (top or bottom) + nx = 0 + ny = 1 + } + + // Restore sign based on which side of center we're on + if px < 0 { nx = -nx } + if py < 0 { ny = -ny } + + return (nx, ny) +} + +/// Generates a displacement map image as a signed distance field from rounded rect edges. +/// - edgeDistance: The distance (in points) over which displacement is applied +/// - R channel: X displacement (127 = neutral, 0 = max left, 255 = max right) +/// - G channel: Y displacement (127 = neutral, 0 = max up, 255 = max down) +/// - B channel: Unused (always 0) +/// Displacement is maximum at the edge and fades linearly to zero at edgeDistance. +/// Actual displacement magnitude is applied when sampling the map. +public func generateDisplacementMap(size: CGSize, cornerRadius: CGFloat, edgeDistance: CGFloat, scale: CGFloat) -> CGImage? { + let width = Int(size.width * scale) + let height = Int(size.height * scale) + + // Clamp corner radius + let maxCornerRadius = min(size.width, size.height) / 2.0 + let clampedRadius = min(cornerRadius, maxCornerRadius) + + // Create bitmap context + var pixelData = [UInt8](repeating: 0, count: width * height * 4) + + for py in 0 ..< height { + for px in 0 ..< width { + // Convert pixel to point coordinates + let x = CGFloat(px) / scale + let y = CGFloat(py) / scale + + // Get signed distance (negative inside, positive outside) + let sdf = roundedRectSDF(x: x, y: y, width: size.width, height: size.height, cornerRadius: clampedRadius) + + // Get gradient (outward normal direction) + let (nx, ny) = roundedRectGradient(x: x, y: y, width: size.width, height: size.height, cornerRadius: clampedRadius) + + // Inward normal (content moves away from edge, toward center) + let inwardX = -nx + let inwardY = -ny + + // Distance from edge (positive inside the shape) + let distFromEdge = -sdf + + // Weight: 1 at edge, 0 at edgeDistance (linear falloff) + let weight = max(0, min(1, 1.0 - distFromEdge / edgeDistance)) + + // Displacement modulated by distance from edge + let displacementX = inwardX * weight + let displacementY = inwardY * weight + + // Encode in R/G: 127 = neutral, map -1..1 to 0..254 + let r = UInt8(max(0, min(255, Int(127 + displacementX * 127)))) + let g = UInt8(max(0, min(255, Int(127 + displacementY * 127)))) + + let idx = (py * width + px) * 4 + pixelData[idx + 0] = r // X displacement + pixelData[idx + 1] = g // Y displacement + pixelData[idx + 2] = 0 // Unused + pixelData[idx + 3] = 255 // A + } + } + + let colorSpace = CGColorSpaceCreateDeviceRGB() + guard let context = CGContext( + data: &pixelData, + width: width, + height: height, + bitsPerComponent: 8, + bytesPerRow: width * 4, + space: colorSpace, + bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue + ) else { + return nil + } + + return context.makeImage() +} + +/// Samples displacement from a displacement map with bilinear interpolation and bezier easing +/// - Parameters: +/// - x, y: Coordinates in the displacement map's pixel space +/// - pixels: Pointer to displacement map pixel data +/// - width, height: Displacement map dimensions +/// - bytesPerRow, bytesPerPixel: Displacement map layout +/// - bezier: Bezier control points for easing curve +/// - Returns: Displacement (dx, dy) in range -1..1 with bezier easing applied +public func sampleDisplacement( + x: CGFloat, + y: CGFloat, + pixels: UnsafePointer, + width: Int, + height: Int, + bytesPerRow: Int, + bytesPerPixel: Int, + bezier: DisplacementBezier +) -> (dx: CGFloat, dy: CGFloat) { + let clampedX = max(0, min(CGFloat(width - 1), x)) + let clampedY = max(0, min(CGFloat(height - 1), y)) + + let x0 = Int(clampedX) + let y0 = Int(clampedY) + let x1 = min(x0 + 1, width - 1) + let y1 = min(y0 + 1, height - 1) + + let fx = clampedX - CGFloat(x0) + let fy = clampedY - CGFloat(y0) + + func sample(_ sx: Int, _ sy: Int) -> (r: CGFloat, g: CGFloat) { + let offset = sy * bytesPerRow + sx * bytesPerPixel + return (CGFloat(pixels[offset + 0]), CGFloat(pixels[offset + 1])) + } + + let c00 = sample(x0, y0) + let c10 = sample(x1, y0) + let c01 = sample(x0, y1) + let c11 = sample(x1, y1) + + let r = (c00.r * (1 - fx) + c10.r * fx) * (1 - fy) + (c01.r * (1 - fx) + c11.r * fx) * fy + let g = (c00.g * (1 - fx) + c10.g * fx) * (1 - fy) + (c01.g * (1 - fx) + c11.g * fx) * fy + + // Decode: 127 = neutral, map 0..254 to -1..1 + var dx = (r - 127.0) / 127.0 + var dy = (g - 127.0) / 127.0 + + // Apply bezier easing to vector magnitude, preserving direction + let mag = hypot(dx, dy) + if mag > 0 { + let newMag = bezierPoint(bezier.x1, bezier.y1, bezier.x2, bezier.y2, mag) + let scale = newMag / mag + dx *= scale + dy *= scale + } + + return (dx, dy) +} + +/// Generates a glass mesh with corner-aware topology. +/// - 4 radial corner wedges sampled in polar space +/// - 4 edge strips aligned with the rectangle sides +/// - 1 center patch +/// Corner/edge seams share the same coordinates (but do not reuse vertices) so +/// the neighbouring faces fit perfectly without T-junctions. +public func generateGlassMeshFromDisplacementMap( + size: CGSize, + cornerRadius: CGFloat, + displacementMap: CGImage, + displacementMagnitudeU: CGFloat, + displacementMagnitudeV: CGFloat, + cornerResolution: Int, + outerEdgeDistance: CGFloat, + bezier: DisplacementBezier, + generateWireframe: Bool = false +) -> (mesh: MeshTransform, wireframe: CGPath?) { + guard let dispDataProvider = displacementMap.dataProvider, + let dispData = dispDataProvider.data, + let dispPixels = CFDataGetBytePtr(dispData) else { + return (mesh: MeshTransform(), wireframe: nil) + } + + let dispWidth = displacementMap.width + let dispHeight = displacementMap.height + let dispBytesPerRow = displacementMap.bytesPerRow + let dispBytesPerPixel = displacementMap.bitsPerPixel / 8 + + let clampedRadius = min(cornerRadius, min(size.width, size.height) / 2) + + let transform = MeshTransform() + var wireframe: CGMutablePath? + if generateWireframe { + wireframe = CGMutablePath() + } + + // Debug flags + let debugNoDisplacement = false + let debugLogCorner = false + + // Inset the mesh slightly (1 pixel) to clear the clip mask + let insetPoints = -1.0 + let usableWidth = max(1.0, size.width - insetPoints * 2) + let usableHeight = max(1.0, size.height - insetPoints * 2) + let insetUOffset = insetPoints / size.width + let insetVOffset = insetPoints / size.height + let usableUNorm = usableWidth / size.width + let usableVNorm = usableHeight / size.height + + // Helper to sample displacement and create vertex + func makeVertex(u: CGFloat, v: CGFloat, depth: CGFloat = 0) -> (vertex: MeshTransform.Vertex, point: CGPoint) { + let mappedU = insetUOffset + u * usableUNorm + let mappedV = insetVOffset + v * usableVNorm + let fromX: CGFloat + let fromY: CGFloat + + if debugNoDisplacement { + fromX = mappedU + fromY = mappedV + } else { + let (dispX, dispY) = sampleDisplacement( + x: mappedU * CGFloat(dispWidth - 1), + y: mappedV * CGFloat(dispHeight - 1), + pixels: dispPixels, + width: dispWidth, + height: dispHeight, + bytesPerRow: dispBytesPerRow, + bytesPerPixel: dispBytesPerPixel, + bezier: bezier + ) + + // Slight boost near the edge to emphasize the outer strip (rounded-corner aware) + let worldX = insetPoints + u * usableWidth + let worldY = insetPoints + v * usableHeight + let sdf = roundedRectSDF(x: worldX, y: worldY, width: size.width, height: size.height, cornerRadius: clampedRadius) + let distToEdge = max(0.0, -sdf) // distance inside the rounded rect to the edge + let edgeBand = max(0.0, outerEdgeDistance) + let edgeBoostGain: CGFloat = 0.5 // up to +50% displacement at the edge, fades inside + let edgeBoost: CGFloat + if edgeBand > 0 { + let t = max(0.0, min(1.0, (edgeBand - distToEdge) / edgeBand)) + let eased = t * t * (3 - 2 * t) // smoothstep + edgeBoost = 1.0 + eased * edgeBoostGain + } else { + edgeBoost = 1.0 + } + + fromX = max(0.0, min(1.0, mappedU + dispX * displacementMagnitudeU * edgeBoost)) + fromY = max(0.0, min(1.0, mappedV + dispY * displacementMagnitudeV * edgeBoost)) + } + + let vertex = MeshTransform.Vertex(from: CGPoint(x: fromX, y: fromY), to: MeshTransform.Point3D(x: mappedU, y: mappedV, z: depth)) + return (vertex, CGPoint(x: mappedU * size.width, y: mappedV * size.height)) + } + + var vertexIndex = 0 + var vertexPoints: [CGPoint] = [] + + func addVertex(u: CGFloat, v: CGFloat, depth: CGFloat = 0) -> Int { + let (vertex, point) = makeVertex(u: u, v: v, depth: depth) + transform.add(vertex) + vertexPoints.append(point) + let idx = vertexIndex + vertexIndex += 1 + return idx + } + + func addVertex(point: CGPoint, depth: CGFloat = 0) -> Int { + let u = point.x / size.width + let v = point.y / size.height + return addVertex(u: u, v: v, depth: depth) + } + + func addQuadFace(_ i0: Int, _ i1: Int, _ i2: Int, _ i3: Int) { + let p0 = vertexPoints[i0] + let p1 = vertexPoints[i1] + let p2 = vertexPoints[i2] + let p3 = vertexPoints[i3] + + let sdf0 = roundedRectSDF(x: p0.x, y: p0.y, width: size.width, height: size.height, cornerRadius: clampedRadius) + let sdf1 = roundedRectSDF(x: p1.x, y: p1.y, width: size.width, height: size.height, cornerRadius: clampedRadius) + let sdf2 = roundedRectSDF(x: p2.x, y: p2.y, width: size.width, height: size.height, cornerRadius: clampedRadius) + let sdf3 = roundedRectSDF(x: p3.x, y: p3.y, width: size.width, height: size.height, cornerRadius: clampedRadius) + + if sdf0 > 0 && sdf1 > 0 && sdf2 > 0 && sdf3 > 0 { + return + } + + transform.add(MeshTransform.Face(indices: (UInt32(i0), UInt32(i1), UInt32(i2), UInt32(i3)), w: (0.0, 0.0, 0.0, 0.0))) + + if let wireframe { + wireframe.move(to: p0) + wireframe.addLine(to: p1) + wireframe.addLine(to: p2) + wireframe.addLine(to: p3) + wireframe.closeSubpath() + } + } + + // Utility to build a grid of vertices from 2D points and emit quads + func buildGrid(points: [[CGPoint]]) { + guard !points.isEmpty else { return } + + var indexGrid: [[Int]] = [] + for row in points { + var rowIndices: [Int] = [] + for point in row { + rowIndices.append(addVertex(point: point)) + } + indexGrid.append(rowIndices) + } + + let numRows = indexGrid.count - 1 + let numCols = indexGrid.first!.count - 1 + for row in 0.. [CGFloat] { + guard count > 0, maxRadius > 0 else { return [0, 1] } + let bandNorm = max(0, min(1, band / maxRadius)) + + // Evenly distribute inner rings up to (1 - bandNorm), then insert the outer strip edge and 1.0. + let innerSegments = max(1, count - 1) + let innerMax = max(0, 1 - bandNorm) + + var factors: [CGFloat] = (0...innerSegments).map { i in + innerMax * CGFloat(i) / CGFloat(innerSegments) + } + + func appendUnique(_ value: CGFloat) { + if let last = factors.last, abs(last - value) < 1e-4 { return } + factors.append(value) + } + + appendUnique(innerMax) + appendUnique(1.0) + + return factors + } + + let depthFactors = depthFactorsWithOuterBand(count: radialSteps, band: outerEdgeDistance, maxRadius: clampedRadius) // 0...1 + let angularFactors = (0...angularSteps).map { CGFloat($0) / CGFloat(angularSteps) } // 0...1 + + // Edge segmentation along the long axes; even spacing + let horizontalSegments = max(2, cornerResolution / 2 + 1) + let verticalSegments = max(2, cornerResolution / 2 + 1) + + func linearPositions(count: Int, start: CGFloat, end: CGFloat) -> [CGFloat] { + return (0...count).map { i in + let t = CGFloat(i) / CGFloat(count) + return start + (end - start) * t + } + } + + // Shared tangential coordinates for strips/center + let topXPositions: [CGFloat] = linearPositions( + count: horizontalSegments, + start: clampedRadius, + end: width - clampedRadius + ) + let sideYPositions: [CGFloat] = linearPositions( + count: verticalSegments, + start: clampedRadius, + end: height - clampedRadius + ) + + // Shared depth coordinates (outer -> inner) so seams line up without T-junctions + let outerToInner = depthFactors.reversed() + let topYPositions: [CGFloat] = outerToInner.map { clampedRadius * (1 - $0) } // 0 ... radius + let bottomYPositions: [CGFloat] = depthFactors.map { height - clampedRadius + clampedRadius * $0 } // (h-r) ... h + let leftXPositions: [CGFloat] = outerToInner.map { clampedRadius * (1 - $0) } // 0 ... radius + let rightXPositions: [CGFloat] = depthFactors.map { width - clampedRadius + clampedRadius * $0 } // (w-r) ... w + + // Corner wedges in polar space with an explicit center fan to avoid zero-area quads + func buildCorner(center: CGPoint, startAngle: CGFloat, endAngle: CGFloat) { + let ringRadials = outerToInner.filter { $0 > 0 } + guard !ringRadials.isEmpty else { return } + + func formatVertex(_ idx: Int) -> String { + let p = vertexPoints[idx] + return "\(idx)=\(String(format: "(%.2f, %.2f)", p.x, p.y))" + } + + // Generate ring vertices from outer arc toward the center point + var ringIndices: [[Int]] = [] + for radial in ringRadials { + let r = clampedRadius * radial + var row: [Int] = [] + for t in angularFactors { + let angle = startAngle + (endAngle - startAngle) * t + let x = center.x + r * cos(angle) + let y = center.y + r * sin(angle) + row.append(addVertex(point: CGPoint(x: x, y: y))) + } + ringIndices.append(row) + } + + // Quad rings between concentric samples + for r in 0..<(ringIndices.count - 1) { + let outerRing = ringIndices[r] + let innerRing = ringIndices[r + 1] + for i in 0..<(outerRing.count - 1) { + addQuadFace( + outerRing[i], + outerRing[i + 1], + innerRing[i + 1], + innerRing[i] + ) + } + } + + // Final collapse: merge two wedge slices into one quad anchored at the center. + // Each quad spans a double-width wedge: center -> v0 -> v1 -> v2 (contiguous along the arc). + if let innermostRing = ringIndices.last { + let ringSegments = innermostRing.count - 1 // last point is the arc end (not wrapped) + guard ringSegments >= 2 else { return } + + if debugLogCorner { + let formatted = innermostRing.map { formatVertex($0) }.joined(separator: ", ") + print("Corner collapse ringSegments=\(ringSegments) stride=2 angularSteps=\(angularSteps)") + print("Innermost ring vertices: \(formatted)") + } + + let centerAnchor = addVertex(point: center, depth: -0.02) + let stride = 2 + + // Each quad covers two arc segments: (vi, vi+1) and (vi+1, vi+2) + var i = 0 + while i + 2 <= ringSegments { + let v0 = innermostRing[i] + let v1 = innermostRing[i + 1] + let v2 = innermostRing[i + 2] + + if debugLogCorner { + print("Quad indices: [\(centerAnchor), \(v0), \(v1), \(v2)]") + } + + addQuadFace( + centerAnchor, + v0, + v1, + v2 + ) + + i += stride + } + + // Safety: if an odd segment remains, cap it with a final quad + if i < ringSegments { + let v0 = innermostRing[ringSegments - 1] + let v1 = innermostRing[ringSegments] + let v2 = innermostRing[ringSegments] // duplicate to keep quad valid + if debugLogCorner { + print("Quad indices (odd tail): [\(centerAnchor), \(v0), \(v1), \(v2)]") + } + addQuadFace(centerAnchor, v0, v1, v2) + } + } + } + + // Edge strips + func buildStrip(xPositions: [CGFloat], yPositions: [CGFloat]) { + var points: [[CGPoint]] = [] + for y in yPositions { + let row = xPositions.map { CGPoint(x: $0, y: y) } + points.append(row) + } + buildGrid(points: points) + } + + // Top / bottom strips + buildStrip(xPositions: topXPositions, yPositions: topYPositions) + buildStrip(xPositions: topXPositions, yPositions: bottomYPositions) + + // Left / right strips + buildStrip(xPositions: leftXPositions, yPositions: sideYPositions) + buildStrip(xPositions: rightXPositions, yPositions: sideYPositions) + + // Center patch uses the same tangential sampling to meet edges cleanly + buildStrip(xPositions: topXPositions, yPositions: sideYPositions) + + // Corners (angles chosen to keep columns increasing along +x) + buildCorner( + center: CGPoint(x: clampedRadius, y: clampedRadius), + startAngle: .pi, + endAngle: 1.5 * .pi + ) + buildCorner( + center: CGPoint(x: width - clampedRadius, y: clampedRadius), + startAngle: 1.5 * .pi, + endAngle: 2 * .pi + ) + buildCorner( + center: CGPoint(x: width - clampedRadius, y: height - clampedRadius), + startAngle: .pi / 2, + endAngle: 0 + ) + buildCorner( + center: CGPoint(x: clampedRadius, y: height - clampedRadius), + startAngle: .pi, + endAngle: .pi / 2 + ) + + return (mesh: transform, wireframe: wireframe) +} diff --git a/submodules/TelegramUI/Components/MeshTransform/Sources/MeshTransform.swift b/submodules/TelegramUI/Components/MeshTransform/Sources/MeshTransform.swift new file mode 100644 index 00000000..b625a71c --- /dev/null +++ b/submodules/TelegramUI/Components/MeshTransform/Sources/MeshTransform.swift @@ -0,0 +1,150 @@ +import Foundation +import UIKit +import MeshTransformApi + +private let transformClass: NSObject? = { + let name = ("CAMutable" as NSString).appendingFormat("MeshTransform") + if let cls = NSClassFromString(name as String) as AnyObject as? NSObject { + return cls + } + return nil +}() + +private let immutableTransformClass: NSObject? = { + let name = ("CA" as NSString).appendingFormat("MeshTransform") + if let cls = NSClassFromString(name as String) as AnyObject as? NSObject { + return cls + } + return nil +}() + +@inline(__always) +private func getMethod(object: NSObject, selector: String) -> T? { + guard let method = object.method(for: NSSelectorFromString(selector)) else { + return nil + } + return unsafeBitCast(method, to: T.self) +} + +private var cachedTransformCreateMethod: (@convention(c) (AnyObject, Selector) -> NSObject?, Selector)? +private func invokeTransformCreateMethod() -> NSObject? { + guard let transformClass = transformClass else { + return nil + } + if let cachedTransformCreateMethod { + return cachedTransformCreateMethod.0(transformClass, cachedTransformCreateMethod.1) + } else { + let method: (@convention(c) (AnyObject, Selector) -> NSObject?)? = getMethod(object: transformClass, selector: "meshTransform") + if let method { + let selector = NSSelectorFromString("meshTransform") + cachedTransformCreateMethod = (method, selector) + return method(transformClass, selector) + } else { + return nil + } + } +} + +private var cachedTransformCreateCustomMethod: (@convention(c) (AnyObject, Selector, _ vertexCount: UInt, _ vertices: UnsafeMutablePointer, _ faceCount: UInt, _ faces: UnsafeMutablePointer, _ depthNormalization: NSString) -> NSObject?, Selector)? +private func invokeTransformCreateCustomMethod(vertexCount: UInt, vertices: UnsafeMutablePointer, faceCount: UInt, faces: UnsafeMutablePointer, depthNormalization: String) -> NSObject? { + guard let transformClass = transformClass else { + return nil + } + if let cachedTransformCreateCustomMethod { + return cachedTransformCreateCustomMethod.0(transformClass, cachedTransformCreateCustomMethod.1, vertexCount, vertices, faceCount, faces, depthNormalization as NSString) + } else { + let selectorName = ("meshTransf" as NSString).appending("ormWithVertexCount:vertices:faceCount:faces:depthNormalization:") + + let method: (@convention(c) (AnyObject, Selector, _ vertexCount: UInt, _ vertices: UnsafeMutablePointer, _ faceCount: UInt, _ faces: UnsafeMutablePointer, _ depthNormalization: NSString) -> NSObject?)? = getMethod(object: transformClass, selector: selectorName) + if let method { + let selector = NSSelectorFromString(selectorName) + cachedTransformCreateCustomMethod = (method, selector) + return method(transformClass, selector, vertexCount, vertices, faceCount, faces, depthNormalization as NSString) + } else { + return nil + } + } +} + +private var cachedTransformAddFaceMethod: (@convention(c) (AnyObject, Selector, MeshTransformMeshFace) -> Void, Selector)? +private func invokeTransformAddFaceMethod(object: NSObject, face: MeshTransformMeshFace) { + if let cachedTransformAddFaceMethod { + return cachedTransformAddFaceMethod.0(object, cachedTransformAddFaceMethod.1, face) + } else { + let method: (@convention(c) (AnyObject, Selector, MeshTransformMeshFace) -> Void)? = getMethod(object: object, selector: "addFace:") + if let method { + let selector = NSSelectorFromString("addFace:") + cachedTransformAddFaceMethod = (method, selector) + method(object, selector, face) + } + } +} + +private var cachedTransformAddVertexMethod: (@convention(c) (AnyObject, Selector, MeshTransformMeshVertex) -> Void, Selector)? +private func invokeTransformAddVertexMethod(object: NSObject, vertex: MeshTransformMeshVertex) { + if let cachedTransformAddVertexMethod { + return cachedTransformAddVertexMethod.0(object, cachedTransformAddVertexMethod.1, vertex) + } else { + let method: (@convention(c) (AnyObject, Selector, MeshTransformMeshVertex) -> Void)? = getMethod(object: object, selector: "addVertex:") + if let method { + let selector = NSSelectorFromString("addVertex:") + cachedTransformAddVertexMethod = (method, selector) + method(object, selector, vertex) + } + } +} + +private var cachedTransformSetSubdivisionStepsMethod: (@convention(c) (AnyObject, Selector, Int) -> Void, Selector)? +private func invokeTransformSetSubdivisionStepsMethod(object: NSObject, value: Int) { + if let cachedTransformSetSubdivisionStepsMethod { + return cachedTransformSetSubdivisionStepsMethod.0(object, cachedTransformSetSubdivisionStepsMethod.1, value) + } else { + let method: (@convention(c) (AnyObject, Selector, Int) -> Void)? = getMethod(object: object, selector: "setSubdivisionSteps:") + if let method { + let selector = NSSelectorFromString("setSubdivisionSteps:") + cachedTransformSetSubdivisionStepsMethod = (method, selector) + method(object, selector, value) + } + } +} + +public final class MeshTransform { + public typealias Value = NSObject + + public typealias Point3D = MeshTransformPoint3D + public typealias Vertex = MeshTransformMeshVertex + public typealias Face = MeshTransformMeshFace + + private var vertices: ContiguousArray = [] + private var faces: ContiguousArray = [] + + public init() { + } + + public func add(_ vertex: Vertex) { + self.vertices.append(vertex) + } + + public func add(_ face: Face) { + self.faces.append(face) + } + + public consuming func makeValue() -> Value? { + let result = self.vertices.withUnsafeMutableBufferPointer { vertices -> NSObject? in + return self.faces.withUnsafeMutableBufferPointer { faces -> NSObject? in + return invokeTransformCreateCustomMethod( + vertexCount: UInt(vertices.count), + vertices: vertices.baseAddress!, + faceCount: UInt(faces.count), + faces: faces.baseAddress!, + depthNormalization: "none" + ) + } + } + if let result { + invokeTransformSetSubdivisionStepsMethod(object: result, value: 0) + } + + return result + } +} diff --git a/submodules/TelegramUI/Components/MessageFeeHeaderPanelComponent/BUILD b/submodules/TelegramUI/Components/MessageFeeHeaderPanelComponent/BUILD new file mode 100644 index 00000000..17916486 --- /dev/null +++ b/submodules/TelegramUI/Components/MessageFeeHeaderPanelComponent/BUILD @@ -0,0 +1,31 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "MessageFeeHeaderPanelComponent", + module_name = "MessageFeeHeaderPanelComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Display", + "//submodules/AsyncDisplayKit", + "//submodules/TelegramPresentationData", + "//submodules/AccountContext", + "//submodules/ComponentFlow", + "//submodules/Components/ComponentDisplayAdapters", + "//submodules/PresentationDataUtils", + "//submodules/Postbox", + "//submodules/TelegramCore", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/TelegramUIPreferences", + "//submodules/TelegramStringFormatting", + "//submodules/TextFormat", + "//submodules/TelegramUI/Components/TextNodeWithEntities", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/MessageFeeHeaderPanelComponent/Sources/ChatFeePanelNode.swift b/submodules/TelegramUI/Components/MessageFeeHeaderPanelComponent/Sources/ChatFeePanelNode.swift new file mode 100644 index 00000000..a4a3262b --- /dev/null +++ b/submodules/TelegramUI/Components/MessageFeeHeaderPanelComponent/Sources/ChatFeePanelNode.swift @@ -0,0 +1,155 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import Postbox +import TelegramCore +import SwiftSignalKit +import TelegramPresentationData +import TelegramUIPreferences +import AccountContext +import TelegramStringFormatting +import TextFormat +import TextNodeWithEntities + +final class ChatFeePanelNode: ASDisplayNode { + private let context: AccountContext + private let removeFee: () -> Void + + private let contextContainer: ContextControllerSourceNode + private let clippingContainer: ASDisplayNode + private let contentContainer: ASDisplayNode + + private let textContainer: ASDisplayNode + private let textNode: ImmediateTextNodeWithEntities + + private let removeButtonNode: HighlightTrackingButtonNode + private let removeTextNode: ImmediateTextNode + + private var currentLayout: (CGFloat, CGFloat, CGFloat)? + + init(context: AccountContext, removeFee: @escaping () -> Void) { + self.context = context + self.removeFee = removeFee + + self.contextContainer = ContextControllerSourceNode() + + self.clippingContainer = ASDisplayNode() + self.clippingContainer.clipsToBounds = true + + self.contentContainer = ASDisplayNode() + self.contextContainer.isGestureEnabled = false + + self.textContainer = ASDisplayNode() + + self.textNode = ImmediateTextNodeWithEntities() + self.textNode.anchorPoint = CGPoint() + self.textNode.displaysAsynchronously = false + self.textNode.isUserInteractionEnabled = false + self.textNode.maximumNumberOfLines = 2 + self.textNode.textAlignment = .center + + self.removeButtonNode = HighlightTrackingButtonNode() + + self.removeTextNode = ImmediateTextNode() + self.removeTextNode.anchorPoint = CGPoint() + self.removeTextNode.displaysAsynchronously = false + self.removeTextNode.isUserInteractionEnabled = false + + super.init() + + self.addSubnode(self.contextContainer) + + self.contextContainer.addSubnode(self.clippingContainer) + self.clippingContainer.addSubnode(self.contentContainer) + + self.contextContainer.addSubnode(self.textContainer) + self.textContainer.addSubnode(self.textNode) + self.contextContainer.addSubnode(self.removeTextNode) + self.contextContainer.addSubnode(self.removeButtonNode) + + self.removeButtonNode.addTarget(self, action: #selector(self.removePressed), forControlEvents: [.touchUpInside]) + self.removeButtonNode.highligthedChanged = { [weak self] highlighted in + if let strongSelf = self { + if highlighted { + strongSelf.removeTextNode.layer.removeAnimation(forKey: "opacity") + strongSelf.removeTextNode.alpha = 0.4 + } else { + strongSelf.removeTextNode.alpha = 1.0 + strongSelf.removeTextNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + } + } + } + } + + private var theme: PresentationTheme? + + func updateLayout(width: CGFloat, theme: PresentationTheme, strings: PresentationStrings, info: MessageFeeHeaderPanelComponent.Info, transition: ContainedViewLayoutTransition) -> CGFloat { + let leftInset: CGFloat = 0.0 + let rightInset: CGFloat = 0.0 + + if self.theme !== theme { + self.theme = theme + self.removeTextNode.attributedText = NSAttributedString(string: strings.Chat_PaidMessageFee_RemoveFee, font: Font.regular(17.0), textColor: theme.chat.inputPanel.panelControlColor) + } + + let attributedText = NSMutableAttributedString(string: strings.Chat_PaidMessageFee_Text(info.peer.compactDisplayTitle, "⭐️\(info.value)").string, font: Font.regular(12.0), textColor: theme.rootController.navigationBar.secondaryTextColor) + let range = (attributedText.string as NSString).range(of: "⭐️") + if range.location != NSNotFound { + attributedText.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: true)), range: range) + attributedText.addAttribute(.baselineOffset, value: 0.0, range: range) + } + self.textNode.attributedText = attributedText + + self.textNode.visibility = true + self.textNode.arguments = TextNodeWithEntities.Arguments( + context: self.context, + cache: self.context.animationCache, + renderer: self.context.animationRenderer, + placeholderColor: UIColor(white: 1.0, alpha: 0.1), + attemptSynchronous: false + ) + + let sideInset = 12.0 + + let textSize = self.textNode.updateLayout(CGSize(width: width - leftInset - rightInset - sideInset * 2.0, height: .greatestFiniteMagnitude)) + let textFrame = CGRect(origin: CGPoint(x: leftInset + floorToScreenPixels((width - leftInset - rightInset - textSize.width) / 2.0), y: 9.0), size: textSize) + + transition.updateFrame(node: self.textContainer, frame: textFrame) + + if self.textNode.bounds.size.width != 0.0, transition.isAnimated { + if let snapshotLayer = self.textNode.layer.snapshotContentTree() { + self.textContainer.layer.addSublayer(snapshotLayer) + snapshotLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.18, removeOnCompletion: false, completion: { [weak snapshotLayer] _ in + snapshotLayer?.removeFromSuperlayer() + }) + + self.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) + } + } + + transition.updatePosition(node: self.textNode, position: CGPoint()) + self.textNode.bounds = CGRect(origin: CGPoint(), size: textFrame.size) + + let panelHeight: CGFloat = 48.0 + textSize.height + + let removeSize = self.removeTextNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: .greatestFiniteMagnitude)) + let removeFrame = CGRect(origin: CGPoint(x: leftInset + floorToScreenPixels((width - leftInset - rightInset - removeSize.width) / 2.0), y: panelHeight - removeSize.height - 9.0), size: removeSize) + transition.updatePosition(node: self.removeTextNode, position: removeFrame.origin) + self.removeTextNode.bounds = CGRect(origin: CGPoint(), size: removeFrame.size) + transition.updateFrame(node: self.removeButtonNode, frame: removeFrame.insetBy(dx: -8.0, dy: -4.0)) + + self.contextContainer.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: panelHeight)) + + self.clippingContainer.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: panelHeight)) + self.contentContainer.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: panelHeight)) + + self.currentLayout = (width, leftInset, rightInset) + + return panelHeight + } + + @objc func removePressed() { + self.removeFee() + } +} diff --git a/submodules/TelegramUI/Components/MessageFeeHeaderPanelComponent/Sources/MessageFeeHeaderPanelComponent.swift b/submodules/TelegramUI/Components/MessageFeeHeaderPanelComponent/Sources/MessageFeeHeaderPanelComponent.swift new file mode 100644 index 00000000..117e6456 --- /dev/null +++ b/submodules/TelegramUI/Components/MessageFeeHeaderPanelComponent/Sources/MessageFeeHeaderPanelComponent.swift @@ -0,0 +1,113 @@ +import Foundation +import UIKit +import Display +import TelegramPresentationData +import ComponentFlow +import ComponentDisplayAdapters +import AccountContext +import PresentationDataUtils +import TelegramCore + +public final class MessageFeeHeaderPanelComponent: Component { + public struct Info: Equatable { + public let value: Int64 + public let peer: EnginePeer + + public init(value: Int64, peer: EnginePeer) { + self.value = value + self.peer = peer + } + } + + public let context: AccountContext + public let theme: PresentationTheme + public let strings: PresentationStrings + public let info: Info + public let removeFee: () -> Void + + public init( + context: AccountContext, + theme: PresentationTheme, + strings: PresentationStrings, + info: Info, + removeFee: @escaping () -> Void + ) { + self.context = context + self.theme = theme + self.strings = strings + self.info = info + self.removeFee = removeFee + } + + public static func ==(lhs: MessageFeeHeaderPanelComponent, rhs: MessageFeeHeaderPanelComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.theme !== rhs.theme { + return false + } + if lhs.strings !== rhs.strings { + return false + } + if lhs.info != rhs.info { + return false + } + return true + } + + public final class View: UIView { + private var panel: ChatFeePanelNode? + + private var component: MessageFeeHeaderPanelComponent? + private weak var state: EmptyComponentState? + + public override init(frame: CGRect) { + super.init(frame: frame) + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + } + + func update(component: MessageFeeHeaderPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + self.state = state + + let panel: ChatFeePanelNode + if let current = self.panel { + panel = current + } else { + panel = ChatFeePanelNode( + context: component.context, + removeFee: component.removeFee + ) + self.panel = panel + self.addSubview(panel.view) + } + + let height = panel.updateLayout( + width: availableSize.width, + theme: component.theme, + strings: component.strings, + info: component.info, + transition: transition.containedViewLayoutTransition + ) + let size = CGSize(width: availableSize.width, height: height) + let panelFrame = CGRect(origin: CGPoint(), size: size) + transition.setFrame(view: panel.view, frame: panelFrame) + + return size + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/MiniAppListScreen/Sources/MiniAppListScreen.swift b/submodules/TelegramUI/Components/MiniAppListScreen/Sources/MiniAppListScreen.swift index 579bd7b8..811661b6 100644 --- a/submodules/TelegramUI/Components/MiniAppListScreen/Sources/MiniAppListScreen.swift +++ b/submodules/TelegramUI/Components/MiniAppListScreen/Sources/MiniAppListScreen.swift @@ -266,8 +266,7 @@ final class MiniAppListScreenComponent: Component { } ))) : nil, rightButtons: rightButtons, - backTitle: isModal ? nil : strings.Common_Back, - backPressed: { [weak self] in + backPressed: isModal ? nil : { [weak self] in guard let self else { return } @@ -286,14 +285,15 @@ final class MiniAppListScreenComponent: Component { strings: strings, statusBarHeight: statusBarHeight, sideInset: insets.left, - isSearchActive: self.isSearchDisplayControllerActive, - isSearchEnabled: true, + search: ChatListNavigationBar.Search(isEnabled: true), + activeSearch: self.isSearchDisplayControllerActive ? ChatListNavigationBar.ActiveSearch(isExternal: false) : nil, primaryContent: headerContent, secondaryContent: nil, secondaryTransition: 0.0, storySubscriptions: nil, storiesIncludeHidden: false, uploadProgress: [:], + headerPanels: nil, tabsNode: nil, tabsNodeIsSearch: false, accessoryPanelContainer: nil, @@ -460,6 +460,7 @@ final class MiniAppListScreenComponent: Component { let searchBarTheme = SearchBarNodeTheme(theme: environment.theme, hasSeparator: false) searchBarNode = SearchBarNode( theme: searchBarTheme, + presentationTheme: environment.theme, strings: environment.strings, fieldStyle: .modern, displayBackground: false diff --git a/submodules/TelegramUI/Components/MoreHeaderButton/Sources/MoreHeaderButton.swift b/submodules/TelegramUI/Components/MoreHeaderButton/Sources/MoreHeaderButton.swift index e559c373..6879454e 100644 --- a/submodules/TelegramUI/Components/MoreHeaderButton/Sources/MoreHeaderButton.swift +++ b/submodules/TelegramUI/Components/MoreHeaderButton/Sources/MoreHeaderButton.swift @@ -52,7 +52,7 @@ public final class MoreHeaderButton: HighlightableButtonNode { strongSelf.contextAction?(strongSelf.containerNode, gesture) } - self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 30.0, height: 44.0)) + self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 44.0, height: 44.0)) self.referenceNode.frame = self.containerNode.bounds //self.iconNode.image = MoreHeaderButton.optionsCircleImage(color: color) @@ -68,6 +68,15 @@ public final class MoreHeaderButton: HighlightableButtonNode { @objc private func pressed() { self.onPressed?() } + + public func updateColor(color: UIColor) { + if self.color != color { + self.color = color + if let content = self.content { + self.setContent(content, animated: false) + } + } + } private var content: Content? public func setContent(_ content: Content, animated: Bool = false) { @@ -165,7 +174,7 @@ public final class MoreHeaderButton: HighlightableButtonNode { } override public func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { - return CGSize(width: 22.0, height: 44.0) + return CGSize(width: 44.0, height: 44.0) } public func onLayout() { diff --git a/submodules/TelegramUI/Components/NavigationBarImpl/BUILD b/submodules/TelegramUI/Components/NavigationBarImpl/BUILD new file mode 100644 index 00000000..e7559917 --- /dev/null +++ b/submodules/TelegramUI/Components/NavigationBarImpl/BUILD @@ -0,0 +1,25 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "NavigationBarImpl", + module_name = "NavigationBarImpl", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/ComponentFlow", + "//submodules/TelegramUI/Components/GlassBackgroundComponent", + "//submodules/TelegramUI/Components/EdgeEffect", + "//submodules/Components/ComponentDisplayAdapters", + "//submodules/Components/MultilineTextComponent", + "//submodules/AppBundle", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationBarImpl.swift b/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationBarImpl.swift new file mode 100644 index 00000000..a0bee779 --- /dev/null +++ b/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationBarImpl.swift @@ -0,0 +1,1241 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import GlassBackgroundComponent +import AsyncDisplayKit +import EdgeEffect +import ComponentDisplayAdapters + +public final class NavigationBarImpl: ASDisplayNode, NavigationBar { + public static var defaultSecondaryContentHeight: CGFloat { + return 38.0 + } + + public static let thinBackArrowImage = generateTintedImage(image: UIImage(bundleImageName: "Navigation/BackArrow"), color: .white)?.withRenderingMode(.alwaysTemplate) + + public static let titleFont = Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers]) + + var presentationData: NavigationBarPresentationData + + private var validLayout: (size: CGSize, defaultHeight: CGFloat, additionalTopHeight: CGFloat, additionalContentHeight: CGFloat, additionalBackgroundHeight: CGFloat, additionalCutout: CGSize?, leftInset: CGFloat, rightInset: CGFloat, appearsHidden: Bool, isLandscape: Bool)? + private var requestedLayout: Bool = false + public var requestContainerLayout: ((ContainedViewLayoutTransition) -> Void)? + + public var backPressed: () -> () = { } + + public var userInfo: Any? + public var makeCustomTransitionNode: ((NavigationBar, Bool) -> CustomNavigationTransitionNode?)? + public var allowsCustomTransition: (() -> Bool)? + + public let stripeNode: ASDisplayNode + public let clippingNode: SparseNode + private let buttonsContainerNode: ASDisplayNode + + public private(set) var contentNode: NavigationBarContentNode? + public private(set) var secondaryContentNode: ASDisplayNode? + public var secondaryContentNodeDisplayFraction: CGFloat = 1.0 + + private var itemTitleListenerKey: Int? + private var itemTitleViewListenerKey: Int? + + private var itemLeftButtonListenerKey: Int? + private var itemLeftButtonSetEnabledListenerKey: Int? + + private var itemRightButtonListenerKey: Int? + private var itemRightButtonsListenerKey: Int? + + private var itemBadgeListenerKey: Int? + + private var hintAnimateTitleNodeOnNextLayout: Bool = false + + private var _item: UINavigationItem? + public var item: UINavigationItem? { + get { + return self._item + } set(value) { + if let previousValue = self._item { + if let itemTitleListenerKey = self.itemTitleListenerKey { + previousValue.removeSetTitleListener(itemTitleListenerKey) + self.itemTitleListenerKey = nil + } + + if let itemLeftButtonListenerKey = self.itemLeftButtonListenerKey { + previousValue.removeSetLeftBarButtonItemListener(itemLeftButtonListenerKey) + self.itemLeftButtonListenerKey = nil + } + + if let itemLeftButtonSetEnabledListenerKey = self.itemLeftButtonSetEnabledListenerKey { + previousValue.leftBarButtonItem?.removeSetEnabledListener(itemLeftButtonSetEnabledListenerKey) + self.itemLeftButtonSetEnabledListenerKey = nil + } + + if let itemRightButtonListenerKey = self.itemRightButtonListenerKey { + previousValue.removeSetRightBarButtonItemListener(itemRightButtonListenerKey) + self.itemRightButtonListenerKey = nil + } + + if let itemRightButtonsListenerKey = self.itemRightButtonsListenerKey { + previousValue.removeSetMultipleRightBarButtonItemsListener(itemRightButtonsListenerKey) + self.itemRightButtonsListenerKey = nil + } + + if let itemBadgeListenerKey = self.itemBadgeListenerKey { + previousValue.removeSetBadgeListener(itemBadgeListenerKey) + self.itemBadgeListenerKey = nil + } + } + self._item = value + + self.leftButtonNodeImpl.view.removeFromSuperview() + self.rightButtonNodeImpl.view.removeFromSuperview() + + if let item = value { + self.title = item.title + self.itemTitleListenerKey = item.addSetTitleListener { [weak self] text, animated in + if let strongSelf = self { + let animateIn = animated && (strongSelf.title?.isEmpty ?? true) + strongSelf.title = text + if animateIn { + strongSelf.titleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) + } + } + } + + let itemTitleView = item.titleView + if self.titleView !== itemTitleView { + if let oldTitleView = self.titleView as? NavigationBarTitleView { + oldTitleView.requestUpdate = nil + } + self.titleView = itemTitleView + if let titleView = self.titleView as? NavigationBarTitleView { + titleView.requestUpdate = { [weak self, weak titleView] transition in + guard let self, let titleView, self.titleView === titleView else { + return + } + if let requestContainerLayout = self.requestContainerLayout { + requestContainerLayout(transition) + } else { + self.requestLayout() + } + } + } + } + self.itemTitleViewListenerKey = item.addSetTitleViewListener { [weak self] itemTitleView in + guard let self else { + return + } + + if let oldTitleView = self.titleView as? NavigationBarTitleView { + oldTitleView.requestUpdate = nil + } + self.titleView = itemTitleView + if let titleView = self.titleView as? NavigationBarTitleView { + titleView.requestUpdate = { [weak self, weak titleView] transition in + guard let self, let titleView, self.titleView === titleView else { + return + } + if let requestContainerLayout = self.requestContainerLayout { + requestContainerLayout(transition) + } else { + self.requestLayout() + } + } + } + } + + self.itemLeftButtonListenerKey = item.addSetLeftBarButtonItemListener { [weak self] previousItem, _, animated in + if let strongSelf = self { + if let itemLeftButtonSetEnabledListenerKey = strongSelf.itemLeftButtonSetEnabledListenerKey { + previousItem?.removeSetEnabledListener(itemLeftButtonSetEnabledListenerKey) + strongSelf.itemLeftButtonSetEnabledListenerKey = nil + } + + strongSelf.updateLeftButton(animated: animated) + strongSelf.invalidateCalculatedLayout() + strongSelf.requestLayout() + } + } + + self.itemRightButtonListenerKey = item.addSetRightBarButtonItemListener { [weak self] previousItem, currentItem, animated in + if let strongSelf = self { + strongSelf.updateRightButton(animated: animated) + strongSelf.invalidateCalculatedLayout() + strongSelf.requestLayout() + } + } + + self.itemRightButtonsListenerKey = item.addSetMultipleRightBarButtonItemsListener { [weak self] items, animated in + if let strongSelf = self { + strongSelf.updateRightButton(animated: animated) + strongSelf.invalidateCalculatedLayout() + strongSelf.requestLayout() + } + } + + self.itemBadgeListenerKey = item.addSetBadgeListener { [weak self] text in + if let strongSelf = self { + strongSelf.updateBadgeText(text: text) + } + } + self.updateBadgeText(text: item.badge) + + self.updateLeftButton(animated: false) + self.updateRightButton(animated: false) + } else { + self.title = nil + self.updateLeftButton(animated: false) + self.updateRightButton(animated: false) + } + self.invalidateCalculatedLayout() + self.requestLayout() + } + } + + public var customBackButtonText: String? + + private var title: String? { + didSet { + if let title = self.title { + self.titleNode.attributedText = NSAttributedString(string: title, font: NavigationBarImpl.titleFont, textColor: self.presentationData.theme.primaryTextColor) + self.titleNode.accessibilityLabel = title + if self.titleNode.supernode == nil { + self.buttonsContainerNode.addSubnode(self.titleNode) + } + } else { + self.titleNode.removeFromSupernode() + } + + self.updateAccessibilityElements() + self.invalidateCalculatedLayout() + self.requestLayout() + } + } + + public private(set) var titleView: UIView? { + didSet { + if let oldValue = oldValue { + oldValue.removeFromSuperview() + } + + if let titleView = self.titleView { + if let backgroundContainer = self.backgroundContainer { + backgroundContainer.contentView.addSubview(titleView) + } else { + self.buttonsContainerNode.view.addSubview(titleView) + } + } + + self.invalidateCalculatedLayout() + self.requestLayout() + } + } + + public var layoutSuspended: Bool = false + + private let titleNode: ImmediateTextNode + + var previousItemListenerKey: Int? + var previousItemBackListenerKey: Int? + + private func updateAccessibilityElements() { + } + + override public var accessibilityElements: [Any]? { + get { + var accessibilityElements: [Any] = [] + if self.backButtonNodeImpl.view.superview != nil { + addAccessibilityChildren(of: self.backButtonNodeImpl, container: self, to: &accessibilityElements) + } + if self.leftButtonNodeImpl.view.superview != nil { + addAccessibilityChildren(of: self.leftButtonNodeImpl, container: self, to: &accessibilityElements) + } + if self.titleNode.view.superview != nil { + addAccessibilityChildren(of: self.titleNode, container: self, to: &accessibilityElements) + accessibilityElements.append(self.titleNode) + } + if let titleView = self.titleView, titleView.superview != nil { + titleView.accessibilityFrame = UIAccessibility.convertToScreenCoordinates(titleView.bounds, in: titleView) + accessibilityElements.append(titleView) + } + if self.rightButtonNodeImpl.supernode != nil { + addAccessibilityChildren(of: self.rightButtonNodeImpl, container: self, to: &accessibilityElements) + } + if let contentNode = self.contentNode { + addAccessibilityChildren(of: contentNode, container: self, to: &accessibilityElements) + } + if let secondaryContentNode = self.secondaryContentNode { + addAccessibilityChildren(of: secondaryContentNode, container: self, to: &accessibilityElements) + } + return accessibilityElements + } set(value) { + } + } + + override public func didLoad() { + super.didLoad() + + self.updateAccessibilityElements() + } + + public var enableAutomaticBackButton: Bool = true + + var _previousItem: NavigationPreviousAction? + public var previousItem: NavigationPreviousAction? { + get { + if !self.enableAutomaticBackButton { + return nil + } + return self._previousItem + } set(value) { + if !self.enableAutomaticBackButton { + self._previousItem = nil + return + } + if self._previousItem != value { + if let previousValue = self._previousItem, case let .item(itemValue) = previousValue { + if let previousItemListenerKey = self.previousItemListenerKey { + itemValue.removeSetTitleListener(previousItemListenerKey) + self.previousItemListenerKey = nil + } + if let previousItemBackListenerKey = self.previousItemBackListenerKey { + itemValue.removeSetBackBarButtonItemListener(previousItemBackListenerKey) + self.previousItemBackListenerKey = nil + } + } + self._previousItem = value + + if let previousItem = value { + switch previousItem { + case let .item(itemValue): + self.previousItemListenerKey = itemValue.addSetTitleListener { [weak self] _, _ in + if let strongSelf = self, let previousItem = strongSelf.previousItem, case let .item(itemValue) = previousItem { + if case .glass = strongSelf.presentationData.theme.style { + strongSelf.backButtonNodeImpl.updateManualText("", isBack: true) + } else if let customBackButtonText = strongSelf.customBackButtonText { + strongSelf.backButtonNodeImpl.updateManualText(customBackButtonText, isBack: true) + } else if let backBarButtonItem = itemValue.backBarButtonItem { + strongSelf.backButtonNodeImpl.updateManualText(backBarButtonItem.title ?? "", isBack: true) + } else { + strongSelf.backButtonNodeImpl.updateManualText(itemValue.title ?? "", isBack: true) + } + strongSelf.invalidateCalculatedLayout() + strongSelf.requestLayout() + } + } + + self.previousItemBackListenerKey = itemValue.addSetBackBarButtonItemListener { [weak self] _, _, _ in + if let strongSelf = self, let previousItem = strongSelf.previousItem, case let .item(itemValue) = previousItem { + if case .glass = strongSelf.presentationData.theme.style { + strongSelf.backButtonNodeImpl.updateManualText("", isBack: true) + } else if let customBackButtonText = strongSelf.customBackButtonText { + strongSelf.backButtonNodeImpl.updateManualText(customBackButtonText, isBack: true) + } else if let backBarButtonItem = itemValue.backBarButtonItem { + strongSelf.backButtonNodeImpl.updateManualText(backBarButtonItem.title ?? "", isBack: true) + } else { + strongSelf.backButtonNodeImpl.updateManualText(itemValue.title ?? "", isBack: true) + } + strongSelf.invalidateCalculatedLayout() + strongSelf.requestLayout() + } + } + case .close: + break + } + } + self.updateLeftButton(animated: false) + + self.invalidateCalculatedLayout() + self.requestLayout() + } + } + } + + private func updateBadgeText(text: String?) { + let actualText = text ?? "" + if self.badgeNode.text != actualText { + self.badgeNode.text = actualText + self.badgeNode.isHidden = actualText.isEmpty + self.backButtonNodeImpl.manualAlpha = self.badgeNode.isHidden ? 1.0 : 0.0 + + self.invalidateCalculatedLayout() + self.requestLayout() + } + } + + private func updateLeftButton(animated: Bool) { + if let item = self.item { + var needsLeftButton = false + if let leftBarButtonItem = item.leftBarButtonItem, !leftBarButtonItem.backButtonAppearance { + needsLeftButton = true + } else if let previousItem = self.previousItem, case .close = previousItem { + needsLeftButton = true + } + + if needsLeftButton { + if animated { + if self.backButtonNodeImpl.view.superview != nil { + if let snapshotView = self.backButtonNodeImpl.view.snapshotContentTree() { + snapshotView.frame = self.backButtonNodeImpl.frame + self.backButtonNodeImpl.view.superview?.insertSubview(snapshotView, aboveSubview: self.backButtonNodeImpl.view) + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + } + } + + if self.backButtonArrow.view.superview != nil { + if let snapshotView = self.backButtonArrow.view.snapshotContentTree() { + snapshotView.frame = self.backButtonArrow.frame + self.backButtonArrow.view.superview?.insertSubview(snapshotView, aboveSubview: self.backButtonArrow.view) + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + } + } + + if self.badgeNode.view.superview != nil { + if let snapshotView = self.badgeNode.view.snapshotContentTree() { + snapshotView.frame = self.badgeNode.frame + self.badgeNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.badgeNode.view) + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + } + } + } + + self.backButtonNodeImpl.view.removeFromSuperview() + self.backButtonArrow.view.removeFromSuperview() + self.badgeNode.view.removeFromSuperview() + + if let leftBarButtonItem = item.leftBarButtonItem { + self.leftButtonNodeImpl.updateItems([], animated: animated) + self.leftButtonNodeImpl.updateItems([leftBarButtonItem], animated: animated) + } else { + self.leftButtonNodeImpl.updateItems([], animated: animated) + self.leftButtonNodeImpl.updateItems([UIBarButtonItem(title: "___close", style: .plain, target: nil, action: nil)], animated: animated) + } + + if self.leftButtonNodeImpl.supernode == nil { + if let leftButtonsBackgroundView = self.leftButtonsBackgroundView { + leftButtonsBackgroundView.container.addSubview(self.leftButtonNodeImpl.view) + } else { + self.buttonsContainerNode.view.addSubview(self.leftButtonNodeImpl.view) + } + } + + if animated { + self.leftButtonNodeImpl.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) + } + } else { + if animated, self.leftButtonNodeImpl.view.superview != nil { + if let snapshotView = self.leftButtonNodeImpl.view.snapshotContentTree() { + snapshotView.frame = self.leftButtonNodeImpl.frame + self.leftButtonNodeImpl.view.superview?.insertSubview(snapshotView, aboveSubview: self.leftButtonNodeImpl.view) + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + } + } + self.leftButtonNodeImpl.view.removeFromSuperview() + + var backTitle: String? + if case .glass = self.presentationData.theme.style { + backTitle = "" + } else if let customBackButtonText = self.customBackButtonText { + backTitle = customBackButtonText + } else if let leftBarButtonItem = item.leftBarButtonItem, leftBarButtonItem.backButtonAppearance { + backTitle = leftBarButtonItem.title + } else if let previousItem = self.previousItem { + switch previousItem { + case let .item(itemValue): + if let backBarButtonItem = itemValue.backBarButtonItem { + backTitle = backBarButtonItem.title ?? self.presentationData.strings.back + } else { + backTitle = itemValue.title ?? self.presentationData.strings.back + } + case .close: + backTitle = nil + } + } + + if let backTitle { + self.backButtonNodeImpl.updateManualText(backTitle, isBack: true) + if self.backButtonNodeImpl.supernode == nil { + if let leftButtonsBackgroundView = self.leftButtonsBackgroundView { + leftButtonsBackgroundView.container.addSubview(self.backButtonNodeImpl.view) + leftButtonsBackgroundView.container.addSubview(self.backButtonArrow.view) + leftButtonsBackgroundView.container.addSubview(self.badgeNode.view) + } else { + self.buttonsContainerNode.view.addSubview(self.backButtonNodeImpl.view) + self.buttonsContainerNode.view.addSubview(self.backButtonArrow.view) + self.buttonsContainerNode.view.addSubview(self.badgeNode.view) + } + } + + if animated { + self.backButtonNodeImpl.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) + self.backButtonArrow.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) + self.badgeNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) + } + } else { + self.backButtonNodeImpl.view.removeFromSuperview() + } + } + } else { + self.leftButtonNodeImpl.view.removeFromSuperview() + self.backButtonNodeImpl.view.removeFromSuperview() + self.backButtonArrow.view.removeFromSuperview() + self.badgeNode.view.removeFromSuperview() + } + + self.updateAccessibilityElements() + } + + private func updateRightButton(animated: Bool) { + if let item = self.item { + var items: [UIBarButtonItem] = [] + if let rightBarButtonItems = item.rightBarButtonItems, !rightBarButtonItems.isEmpty { + items = rightBarButtonItems + } else if let rightBarButtonItem = item.rightBarButtonItem { + items = [rightBarButtonItem] + } + + self.rightButtonNodeUpdated = true + + if !items.isEmpty { + if self.rightButtonNodeImpl.isEmpty { + self.rightButtonNodeImpl.updateItems(items, animated: false) + } else { + self.rightButtonNodeImpl.updateItems([], animated: animated) + self.rightButtonNodeImpl.updateItems(items, animated: animated) + } + if self.rightButtonNodeImpl.view.superview == nil { + if let rightButtonsBackgroundView = self.rightButtonsBackgroundView { + rightButtonsBackgroundView.container.addSubview(self.rightButtonNodeImpl.view) + } else { + self.buttonsContainerNode.view.addSubview(self.rightButtonNodeImpl.view) + } + } + } else { + if animated, self.rightButtonNodeImpl.view.superview != nil { + if let snapshotView = self.rightButtonNodeImpl.view.snapshotContentTree() { + snapshotView.frame = self.rightButtonNodeImpl.frame + self.rightButtonNodeImpl.view.superview?.insertSubview(snapshotView, aboveSubview: self.rightButtonNodeImpl.view) + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + } + } + self.rightButtonNodeImpl.view.removeFromSuperview() + self.rightButtonNodeImpl.updateItems([], animated: false) + } + } else { + if animated, self.rightButtonNodeImpl.view.superview != nil { + if let snapshotView = self.rightButtonNodeImpl.view.snapshotContentTree() { + snapshotView.frame = self.rightButtonNodeImpl.frame + self.rightButtonNodeImpl.view.superview?.insertSubview(snapshotView, aboveSubview: self.rightButtonNodeImpl.view) + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + } + } + self.rightButtonNodeImpl.view.removeFromSuperview() + self.rightButtonNodeImpl.updateItems([], animated: false) + } + + self.updateAccessibilityElements() + } + + public let backgroundNode: NavigationBackgroundNode + + private var leftButtonsBackgroundView: (background: GlassBackgroundView, container: UIView)? + private var rightButtonsBackgroundView: (background: GlassBackgroundView, container: UIView)? + + private let backButtonNodeImpl: NavigationButtonNodeImpl + public var backButtonNode: NavigationButtonNode { + return self.backButtonNodeImpl + } + public let badgeNode: NavigationBarBadgeNode + public let backButtonArrow: ASImageNode + private let leftButtonNodeImpl: NavigationButtonNodeImpl + public var leftButtonNode: NavigationButtonNode { + return self.leftButtonNodeImpl + } + private let rightButtonNodeImpl: NavigationButtonNodeImpl + public var rightButtonNode: NavigationButtonNode { + return self.rightButtonNodeImpl + } + private var rightButtonNodeUpdated: Bool = false + public let additionalContentNode: SparseNode + + public func reattachAdditionalContentNode() { + if self.additionalContentNode.supernode !== self { + self.insertSubnode(self.additionalContentNode, aboveSubnode: self.clippingNode) + } + } + + public var secondaryContentHeight: CGFloat + + private var edgeEffectExtension: CGFloat = 0.0 + private var edgeEffectView: EdgeEffectView? + private var backgroundContainer: GlassBackgroundContainerView? + + public var backgroundView: UIView { + if let edgeEffectView = self.edgeEffectView { + return edgeEffectView + } else { + return self.backgroundNode.view + } + } + + public let customOverBackgroundContentView: UIView + + public init(presentationData: NavigationBarPresentationData) { + self.presentationData = presentationData + self.stripeNode = ASDisplayNode() + + self.titleNode = ImmediateTextNode() + self.titleNode.isAccessibilityElement = true + self.titleNode.accessibilityTraits = .header + + self.backButtonNodeImpl = NavigationButtonNodeImpl(isGlass: presentationData.theme.style == .glass) + if case .glass = presentationData.theme.style { + } else { + self.backButtonNodeImpl.hitTestSlop = UIEdgeInsets(top: 0.0, left: -20.0, bottom: 0.0, right: 0.0) + } + + self.badgeNode = NavigationBarBadgeNode(fillColor: self.presentationData.theme.buttonColor, strokeColor: self.presentationData.theme.buttonColor, textColor: self.presentationData.theme.badgeTextColor) + self.badgeNode.isUserInteractionEnabled = false + self.badgeNode.isHidden = true + self.backButtonArrow = ASImageNode() + self.backButtonArrow.displayWithoutProcessing = true + self.backButtonArrow.displaysAsynchronously = false + self.backButtonArrow.isUserInteractionEnabled = false + self.leftButtonNodeImpl = NavigationButtonNodeImpl(isGlass: presentationData.theme.style == .glass) + self.rightButtonNodeImpl = NavigationButtonNodeImpl(isGlass: presentationData.theme.style == .glass) + if case .glass = presentationData.theme.style { + } else { + self.rightButtonNodeImpl.hitTestSlop = UIEdgeInsets(top: -4.0, left: -4.0, bottom: -4.0, right: -10.0) + } + + self.clippingNode = SparseNode() + if case .glass = presentationData.theme.style { + } else { + self.clippingNode.clipsToBounds = true + } + + self.buttonsContainerNode = SparseNode() + self.buttonsContainerNode.clipsToBounds = true + + self.backButtonNodeImpl.color = self.presentationData.theme.buttonColor + self.backButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor + self.leftButtonNodeImpl.color = self.presentationData.theme.buttonColor + self.leftButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor + self.rightButtonNodeImpl.color = self.presentationData.theme.buttonColor + self.rightButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor + self.backButtonArrow.image = presentationData.theme.style == .glass ? generateTintedImage(image: glassBackArrowImage, color: self.presentationData.theme.buttonColor) : navigationBarBackArrowImage(color: self.presentationData.theme.buttonColor) + if let title = self.title { + self.titleNode.attributedText = NSAttributedString(string: title, font: NavigationBarImpl.titleFont, textColor: self.presentationData.theme.primaryTextColor) + self.titleNode.accessibilityLabel = title + } + self.stripeNode.backgroundColor = self.presentationData.theme.separatorColor + + self.backgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.backgroundColor, enableBlur: self.presentationData.theme.enableBackgroundBlur) + self.additionalContentNode = SparseNode() + + self.secondaryContentHeight = NavigationBarImpl.defaultSecondaryContentHeight + + self.customOverBackgroundContentView = SparseContainerView() + + super.init() + + if case .glass = presentationData.theme.style { + let edgeEffectView = EdgeEffectView() + self.edgeEffectView = edgeEffectView + self.view.addSubview(edgeEffectView) + + let backgroundContainer = GlassBackgroundContainerView() + self.backgroundContainer = backgroundContainer + self.view.addSubview(backgroundContainer) + + backgroundContainer.contentView.addSubview(self.customOverBackgroundContentView) + + let leftButtonsBackgroundView: (background: GlassBackgroundView, container: UIView) = (GlassBackgroundView(), UIView()) + leftButtonsBackgroundView.background.contentView.addSubview(leftButtonsBackgroundView.container) + self.leftButtonsBackgroundView = leftButtonsBackgroundView + backgroundContainer.contentView.addSubview(leftButtonsBackgroundView.background) + + let rightButtonsBackgroundView: (background: GlassBackgroundView, container: UIView) = (GlassBackgroundView(), UIView()) + rightButtonsBackgroundView.background.contentView.addSubview(rightButtonsBackgroundView.container) + self.rightButtonsBackgroundView = rightButtonsBackgroundView + backgroundContainer.contentView.addSubview(rightButtonsBackgroundView.background) + } else { + self.addSubnode(self.backgroundNode) + self.view.addSubview(self.customOverBackgroundContentView) + } + self.addSubnode(self.buttonsContainerNode) + self.addSubnode(self.clippingNode) + self.addSubnode(self.additionalContentNode) + + self.stripeNode.isLayerBacked = true + self.stripeNode.displaysAsynchronously = false + if case .legacy = presentationData.theme.style { + self.addSubnode(self.stripeNode) + } + + self.backgroundColor = nil + self.isOpaque = false + + self.titleNode.displaysAsynchronously = false + self.titleNode.maximumNumberOfLines = 1 + self.titleNode.truncationType = .end + self.titleNode.isOpaque = false + + self.backButtonNodeImpl.highlightChanged = { [weak self] index, highlighted in + if let strongSelf = self, index == 0 { + strongSelf.backButtonArrow.alpha = (highlighted ? 0.4 : 1.0) + strongSelf.badgeNode.alpha = (highlighted ? 0.4 : 1.0) + } + } + self.backButtonNodeImpl.pressed = { [weak self] index in + if let strongSelf = self, index == 0 { + if let leftBarButtonItem = strongSelf.item?.leftBarButtonItem, leftBarButtonItem.backButtonAppearance { + leftBarButtonItem.performActionOnTarget() + } else { + strongSelf.backPressed() + } + } + } + + self.leftButtonNodeImpl.pressed = { [weak self] index in + if let item = self?.item { + if index == 0 { + if let leftBarButtonItem = item.leftBarButtonItem { + leftBarButtonItem.performActionOnTarget() + } else if let previousItem = self?.previousItem, case .close = previousItem { + self?.backPressed() + } + } + } + } + + self.rightButtonNodeImpl.pressed = { [weak self] index in + if let item = self?.item { + if let rightBarButtonItems = item.rightBarButtonItems, !rightBarButtonItems.isEmpty { + if index < rightBarButtonItems.count { + rightBarButtonItems[index].performActionOnTarget() + } + } else if let rightBarButtonItem = item.rightBarButtonItem { + rightBarButtonItem.performActionOnTarget() + } + } + } + } + + public var isBackgroundVisible: Bool { + return self.backgroundNode.alpha == 1.0 + } + + public func updateBackgroundAlpha(_ alpha: CGFloat, transition: ContainedViewLayoutTransition) { + let alpha = max(0.0, min(1.0, alpha)) + transition.updateAlpha(node: self.backgroundNode, alpha: alpha, delay: 0.15) + transition.updateAlpha(node: self.stripeNode, alpha: alpha, delay: 0.15) + } + + public func updatePresentationData(_ presentationData: NavigationBarPresentationData, transition: ContainedViewLayoutTransition = .immediate) { + if presentationData.theme !== self.presentationData.theme || presentationData.strings !== self.presentationData.strings { + self.presentationData = presentationData + + self.backgroundNode.updateColor(color: self.presentationData.theme.backgroundColor, transition: transition) + + self.backButtonNodeImpl.color = self.presentationData.theme.buttonColor + self.backButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor + self.leftButtonNodeImpl.color = self.presentationData.theme.buttonColor + self.leftButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor + self.rightButtonNodeImpl.color = self.presentationData.theme.buttonColor + self.rightButtonNodeImpl.disabledColor = self.presentationData.theme.disabledButtonColor + self.backButtonArrow.image = self.presentationData.theme.style == .glass ? generateTintedImage(image: glassBackArrowImage, color: self.presentationData.theme.buttonColor) : navigationBarBackArrowImage(color: self.presentationData.theme.buttonColor) + if let title = self.title { + self.titleNode.attributedText = NSAttributedString(string: title, font: NavigationBarImpl.titleFont, textColor: self.presentationData.theme.primaryTextColor) + self.titleNode.accessibilityLabel = title + } + self.stripeNode.backgroundColor = self.presentationData.theme.separatorColor + + self.badgeNode.updateTheme(fillColor: self.presentationData.theme.buttonColor, strokeColor: self.presentationData.theme.buttonColor, textColor: self.presentationData.theme.badgeTextColor) + + self.updateLeftButton(animated: false) + self.requestLayout() + } + } + + private func requestLayout() { + self.requestedLayout = true + self.setNeedsLayout() + } + + override public func layout() { + super.layout() + + if let validLayout = self.validLayout, self.requestedLayout { + self.requestedLayout = false + self.updateLayout(size: validLayout.size, defaultHeight: validLayout.defaultHeight, additionalTopHeight: validLayout.additionalTopHeight, additionalContentHeight: validLayout.additionalContentHeight, additionalBackgroundHeight: validLayout.additionalBackgroundHeight, additionalCutout: validLayout.additionalCutout, leftInset: validLayout.leftInset, rightInset: validLayout.rightInset, appearsHidden: validLayout.appearsHidden, isLandscape: validLayout.isLandscape, transition: .immediate) + } + } + + public func updateLayout(size: CGSize, defaultHeight: CGFloat, additionalTopHeight: CGFloat, additionalContentHeight: CGFloat, additionalBackgroundHeight: CGFloat, additionalCutout: CGSize?, leftInset: CGFloat, rightInset: CGFloat, appearsHidden: Bool, isLandscape: Bool, transition: ContainedViewLayoutTransition) { + if self.layoutSuspended { + return + } + + self.validLayout = (size, defaultHeight, additionalTopHeight, additionalContentHeight, additionalBackgroundHeight, additionalCutout, leftInset, rightInset, appearsHidden, isLandscape) + + var contentVerticalOrigin = additionalTopHeight + if case .glass = self.presentationData.theme.style { + contentVerticalOrigin += 2.0 + } + + let backgroundFrame = CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height + additionalBackgroundHeight)) + if self.backgroundNode.frame != backgroundFrame { + transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame) + self.backgroundNode.update(size: backgroundFrame.size, transition: transition) + } + + if let backgroundContainer = self.backgroundContainer { + var backgroundContainerFrame = backgroundFrame + backgroundContainerFrame.size.height += 44.0 + transition.updateFrame(view: backgroundContainer, frame: backgroundContainerFrame) + backgroundContainer.update(size: backgroundContainerFrame.size, isDark: self.presentationData.theme.overallDarkAppearance, transition: ComponentTransition(transition)) + } + + if let edgeEffectView = self.edgeEffectView { + if let edgeEffectColor = self.presentationData.theme.edgeEffectColor, edgeEffectColor.alpha == 0.0 { + edgeEffectView.isHidden = true + } else { + edgeEffectView.isHidden = false + let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: -20.0), size: CGSize(width: size.width, height: size.height + additionalBackgroundHeight + 20.0 + 20.0)) + transition.updatePosition(layer: edgeEffectView.layer, position: edgeEffectFrame.center) + transition.updateBounds(layer: edgeEffectView.layer, bounds: CGRect(origin: CGPoint(), size: edgeEffectFrame.size)) + edgeEffectView.update(content: self.presentationData.theme.edgeEffectColor ?? .white, blur: true, rect: CGRect(origin: CGPoint(), size: edgeEffectFrame.size), edge: .top, edgeSize: 50.0, transition: ComponentTransition(transition)) + } + } + + let apparentAdditionalHeight: CGFloat = self.secondaryContentNode != nil ? (self.secondaryContentHeight * self.secondaryContentNodeDisplayFraction) : 0.0 + + let leftButtonInset: CGFloat = leftInset + 16.0 + let backButtonInset: CGFloat = leftInset + 27.0 + + transition.updateFrame(node: self.clippingNode, frame: CGRect(origin: CGPoint(), size: size)) + transition.updateFrame(node: self.additionalContentNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height + additionalBackgroundHeight))) + transition.updateFrame(node: self.buttonsContainerNode, frame: CGRect(origin: CGPoint(), size: size)) + var expansionHeight: CGFloat = 0.0 + if let contentNode = self.contentNode { + var contentNodeFrame: CGRect + switch contentNode.mode { + case .replacement: + expansionHeight = contentNode.height - defaultHeight + if case .glass = self.presentationData.theme.style { + contentNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: contentVerticalOrigin), size: CGSize(width: size.width, height: contentNode.nominalHeight)) + } else { + contentNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height - additionalContentHeight)) + } + + transition.updateFrame(node: contentNode, frame: contentNodeFrame) + let _ = contentNode.updateLayout(size: contentNodeFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition) + case .expansion: + expansionHeight = contentNode.height + + let additionalExpansionHeight: CGFloat = self.secondaryContentNode != nil && appearsHidden ? (self.secondaryContentHeight * self.secondaryContentNodeDisplayFraction) : 0.0 + contentNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - (appearsHidden ? 0.0 : additionalContentHeight) - expansionHeight - apparentAdditionalHeight - additionalExpansionHeight), size: CGSize(width: size.width, height: expansionHeight)) + if appearsHidden { + contentNodeFrame.origin.y = size.height - contentNode.height + contentVerticalOrigin + } + if appearsHidden { + if self.secondaryContentNode != nil { + contentNodeFrame.origin.y += self.secondaryContentHeight * self.secondaryContentNodeDisplayFraction + } + } + + transition.updateFrame(node: contentNode, frame: contentNodeFrame) + let _ = contentNode.updateLayout(size: contentNodeFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition) + } + } + + transition.updateFrame(node: self.stripeNode, frame: CGRect(x: (additionalCutout?.width ?? 0.0), y: size.height + additionalBackgroundHeight, width: size.width - (additionalCutout?.width ?? 0.0), height: UIScreenPixel)) + + let nominalHeight: CGFloat = 60.0 + + var leftTitleInset: CGFloat = leftInset + var rightTitleInset: CGFloat = rightInset + + var leftButtonsWidth: CGFloat = 0.0 + if self.backButtonNodeImpl.view.superview != nil { + let backButtonSize = self.backButtonNodeImpl.updateLayout(constrainedSize: CGSize(width: size.width, height: 44.0), isLandscape: isLandscape, isLeftAligned: true) + leftTitleInset = backButtonSize.width + backButtonInset + + if case .glass = self.presentationData.theme.style { + } else { + let topHitTestSlop = (nominalHeight - backButtonSize.height) * 0.5 + self.backButtonNodeImpl.hitTestSlop = UIEdgeInsets(top: -topHitTestSlop, left: -27.0, bottom: -topHitTestSlop, right: -8.0) + } + + do { + self.backButtonNodeImpl.alpha = 1.0 + if case .glass = self.presentationData.theme.style { + } else { + transition.updateFrame(node: self.backButtonNodeImpl, frame: CGRect(origin: CGPoint(x: backButtonInset, y: contentVerticalOrigin + floor((nominalHeight - backButtonSize.height) / 2.0)), size: backButtonSize)) + } + + self.backButtonArrow.alpha = 1.0 + + let backButtonArrowFrame: CGRect + if case .glass = self.presentationData.theme.style { + let backButtonArrowSize = CGSize(width: 44.0, height: 44.0) + backButtonArrowFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: backButtonArrowSize) + } else { + let backButtonArrowSize = CGSize(width: 13.0, height: 22.0) + backButtonArrowFrame = CGRect(origin: CGPoint(x: leftInset + 8.0, y: contentVerticalOrigin + floor((nominalHeight - backButtonArrowSize.height) / 2.0)), size: backButtonArrowSize) + } + leftButtonsWidth += backButtonArrowFrame.width + transition.updateFrame(node: self.backButtonArrow, frame: backButtonArrowFrame) + self.badgeNode.alpha = 1.0 + } + } else if self.leftButtonNodeImpl.view.superview != nil { + let leftButtonSize = self.leftButtonNodeImpl.updateLayout(constrainedSize: CGSize(width: size.width, height: 44.0), isLandscape: isLandscape, isLeftAligned: true) + leftTitleInset = leftButtonSize.width + leftButtonInset + 1.0 + + var transition = transition + if self.leftButtonNodeImpl.frame.width.isZero { + transition = .immediate + } + + self.leftButtonNodeImpl.alpha = 1.0 + if case .glass = self.presentationData.theme.style { + transition.updateFrame(node: self.leftButtonNodeImpl, frame: CGRect(origin: CGPoint(x: leftButtonsWidth, y: floor((44.0 - leftButtonSize.height) / 2.0)), size: leftButtonSize)) + } else { + transition.updateFrame(node: self.leftButtonNodeImpl, frame: CGRect(origin: CGPoint(x: leftButtonInset, y: contentVerticalOrigin + floor((nominalHeight - leftButtonSize.height) / 2.0)), size: leftButtonSize)) + } + + if !self.leftButtonNodeImpl.isEmpty { + leftButtonsWidth += leftButtonSize.width + } + } + + let badgeSize = self.badgeNode.measure(CGSize(width: 200.0, height: 100.0)) + let backButtonArrowFrame = self.backButtonArrow.frame + if case .glass = self.presentationData.theme.style, self.badgeNode.view.superview != nil, !self.badgeNode.isHidden { + transition.updateFrame(node: self.badgeNode, frame: CGRect(origin: CGPoint(x: leftButtonsWidth - 14.0, y: floor((44.0 - badgeSize.height) * 0.5)), size: badgeSize)) + leftButtonsWidth += badgeSize.width - 3.0 + } else { + transition.updateFrame(node: self.badgeNode, frame: CGRect(origin: backButtonArrowFrame.origin.offsetBy(dx: 16.0, dy: 2.0), size: badgeSize)) + } + + var rightButtonsWidth: CGFloat = 0.0 + if self.rightButtonNodeImpl.view.superview != nil { + let rightButtonSize = self.rightButtonNodeImpl.updateLayout(constrainedSize: (CGSize(width: size.width, height: 44.0)), isLandscape: isLandscape, isLeftAligned: false) + if !self.rightButtonNodeImpl.isEmpty { + rightButtonsWidth += rightButtonSize.width + } + self.rightButtonNodeImpl.alpha = 1.0 + + var transition = transition + if self.rightButtonNodeImpl.frame.width.isZero || self.rightButtonNodeUpdated { + transition = .immediate + } + if case .glass = self.presentationData.theme.style { + transition.updateFrame(node: self.rightButtonNodeImpl, frame: CGRect(origin: CGPoint(x: 0.0, y: floor((44.0 - rightButtonSize.height) / 2.0)), size: rightButtonSize)) + } else { + rightTitleInset = rightButtonSize.width + leftButtonInset + 4.0 + transition.updateFrame(node: self.rightButtonNodeImpl, frame: CGRect(origin: CGPoint(x: size.width - leftButtonInset - rightButtonSize.width, y: contentVerticalOrigin + floor((nominalHeight - rightButtonSize.height) / 2.0)), size: rightButtonSize)) + } + } + self.rightButtonNodeUpdated = false + + if let leftButtonsBackgroundView = self.leftButtonsBackgroundView { + if leftButtonsWidth != 0.0 { + leftTitleInset = leftInset + 16.0 + leftButtonsWidth + 10.0 + } + + if self.backButtonNodeImpl.view.superview != nil { + transition.updateFrame(node: self.backButtonNodeImpl, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: leftButtonsWidth, height: 44.0))) + } + + let leftButtonsBackgroundFrame = CGRect(origin: CGPoint(x: leftInset + 16.0, y: contentVerticalOrigin + floor((nominalHeight - 44.0) * 0.5)), size: CGSize(width: max(44.0, leftButtonsWidth), height: 44.0)) + var leftButtonsBackgroundTransition = ComponentTransition(transition) + if leftButtonsBackgroundView.background.alpha == 0.0 { + leftButtonsBackgroundTransition = .immediate + } + + leftButtonsBackgroundTransition.setPosition(view: leftButtonsBackgroundView.background, position: leftButtonsBackgroundFrame.center) + leftButtonsBackgroundTransition.setBounds(view: leftButtonsBackgroundView.background, bounds: CGRect(origin: CGPoint(), size: leftButtonsBackgroundFrame.size)) + leftButtonsBackgroundTransition.setFrame(view: leftButtonsBackgroundView.container, frame: CGRect(origin: CGPoint(), size: leftButtonsBackgroundFrame.size)) + ComponentTransition(transition).setAlpha(view: leftButtonsBackgroundView.background, alpha: leftButtonsWidth == 0.0 ? 0.0 : 1.0) + leftButtonsBackgroundView.background.update(size: leftButtonsBackgroundFrame.size, cornerRadius: leftButtonsBackgroundFrame.height * 0.5, isDark: self.presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.presentationData.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, isVisible: leftButtonsWidth != 0.0, transition: leftButtonsBackgroundTransition) + } + + if let rightButtonsBackgroundView = self.rightButtonsBackgroundView { + if rightButtonsWidth != 0.0 { + rightTitleInset = rightInset + 16.0 + rightButtonsWidth + 10.0 + + let rightButtonsBackgroundFrame = CGRect(origin: CGPoint(x: size.width - rightInset - 16.0 - rightButtonsWidth, y: contentVerticalOrigin + floor((nominalHeight - 44.0) * 0.5)), size: CGSize(width: rightButtonsWidth, height: 44.0)) + var rightButtonsBackgroundTransition = ComponentTransition(transition) + if rightButtonsBackgroundView.background.isHidden { + rightButtonsBackgroundTransition = .immediate + } + rightButtonsBackgroundView.container.layer.cornerRadius = 44.0 * 0.5 + + rightButtonsBackgroundTransition.setFrame(view: rightButtonsBackgroundView.background, frame: rightButtonsBackgroundFrame) + + if rightButtonsBackgroundView.container.bounds.size != rightButtonsBackgroundFrame.size { + rightButtonsBackgroundView.container.clipsToBounds = true + let rightButtonsBackgroundViewContainer = rightButtonsBackgroundView.container + rightButtonsBackgroundTransition.setFrame(view: rightButtonsBackgroundView.container, frame: CGRect(origin: CGPoint(), size: rightButtonsBackgroundFrame.size), completion: { [weak rightButtonsBackgroundViewContainer] flag in + if flag, let rightButtonsBackgroundViewContainer { + rightButtonsBackgroundViewContainer.clipsToBounds = false + } + }) + } + + rightButtonsBackgroundView.background.isHidden = false + rightButtonsBackgroundView.background.update(size: rightButtonsBackgroundFrame.size, cornerRadius: rightButtonsBackgroundFrame.height * 0.5, isDark: self.presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.presentationData.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: rightButtonsBackgroundTransition) + } else { + rightButtonsBackgroundView.background.isHidden = true + } + } + + if self.titleNode.view.superview != nil { + let titleSize = self.titleNode.updateLayout(CGSize(width: max(1.0, size.width - max(leftTitleInset, rightTitleInset) * 2.0), height: nominalHeight)) + + do { + var transition = transition + if self.titleNode.frame.width.isZero { + transition = .immediate + } + self.titleNode.alpha = 1.0 + + let titleOffset: CGFloat = 0.0 + transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: contentVerticalOrigin + titleOffset + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize)) + } + } + + if let titleView = self.titleView { + if let titleView = titleView as? NavigationBarTitleView { + var titleViewTransition = transition + if titleView.frame.isEmpty { + titleViewTransition = .immediate + } + + let titleSize = titleView.updateLayout(availableSize: CGSize(width: size.width - leftTitleInset - rightTitleInset, height: nominalHeight), transition: titleViewTransition) + + var titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) * 0.5), y: contentVerticalOrigin + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize) + if titleFrame.origin.x < leftTitleInset { + titleFrame.origin.x = leftTitleInset + floorToScreenPixels((size.width - leftTitleInset - rightTitleInset - titleFrame.width) * 0.5) + } + + titleViewTransition.updateFrame(view: titleView, frame: titleFrame) + } else { + let titleSize = CGSize(width: max(1.0, size.width - max(leftTitleInset, rightTitleInset) * 2.0), height: nominalHeight) + let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: contentVerticalOrigin + floorToScreenPixels((nominalHeight - titleSize.height) / 2.0)), size: titleSize) + var titleViewTransition = transition + if titleView.frame.isEmpty { + titleViewTransition = .immediate + titleView.frame = titleFrame + } + + titleViewTransition.updateFrame(view: titleView, frame: titleFrame) + } + } + } + + public func updateEdgeEffectExtension(value: CGFloat, transition: ContainedViewLayoutTransition) { + if self.edgeEffectExtension == value { + return + } + self.edgeEffectExtension = value + self.applyEdgeEffectExtension(transition: transition) + } + + private func applyEdgeEffectExtension(transition: ContainedViewLayoutTransition) { + if let edgeEffectView = self.edgeEffectView { + transition.updateTransform(layer: edgeEffectView.layer, transform: CATransform3DMakeTranslation(0.0, max(0.0, min(20.0, self.edgeEffectExtension)), 0.0)) + } + } + + public var intrinsicCanTransitionInline: Bool = true + + public var passthroughTouches = true + + public var canTransitionInline: Bool { + if let contentNode = self.contentNode, case .replacement = contentNode.mode { + return false + } else { + return self.intrinsicCanTransitionInline + } + } + + public func contentHeight(defaultHeight: CGFloat) -> CGFloat { + var result: CGFloat = 0.0 + if let contentNode = self.contentNode { + switch contentNode.mode { + case .expansion: + result += defaultHeight + contentNode.height + case .replacement: + result += contentNode.height + } + } else { + result += defaultHeight + } + + if let _ = self.secondaryContentNode { + result += self.secondaryContentHeight * self.secondaryContentNodeDisplayFraction + } + + return result + } + + public func setContentNode(_ contentNode: NavigationBarContentNode?, animated: Bool) { + let transition: ComponentTransition = animated ? .easeInOut(duration: 0.2) : .immediate + + if self.contentNode !== contentNode { + if let previous = self.contentNode { + if animated { + previous.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak self, weak previous] _ in + if let strongSelf = self, let previous = previous { + if previous !== strongSelf.contentNode { + previous.removeFromSupernode() + } + } + }) + } else { + previous.removeFromSupernode() + } + } + self.contentNode = contentNode + self.contentNode?.requestContainerLayout = { [weak self] transition in + guard let self else { + return + } + self.requestContainerLayout?(transition) + } + if let contentNode { + contentNode.layer.removeAnimation(forKey: "opacity") + if case .glass = self.presentationData.theme.style { + self.addSubnode(contentNode) + } else { + contentNode.clipsToBounds = true + if self.stripeNode.supernode != nil { + self.insertSubnode(contentNode, belowSubnode: self.stripeNode) + } else { + self.insertSubnode(contentNode, at: 0) + } + } + if animated { + contentNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + + if case .replacement = contentNode.mode { + if !self.buttonsContainerNode.alpha.isZero { + self.buttonsContainerNode.alpha = 0.0 + if animated { + self.buttonsContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) + } + } + if let backgroundContainer = self.backgroundContainer, !backgroundContainer.alpha.isZero { + transition.setAlpha(view: backgroundContainer, alpha: 0.0) + } + } + + if !self.bounds.size.width.isZero { + self.requestedLayout = true + self.layout() + } else { + self.requestLayout() + } + } else { + if self.buttonsContainerNode.alpha.isZero { + self.buttonsContainerNode.alpha = 1.0 + if animated { + self.buttonsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + } + if let backgroundContainer = self.backgroundContainer, backgroundContainer.alpha.isZero { + transition.setAlpha(view: backgroundContainer, alpha: 1.0) + } + } + } + } + + public func setSecondaryContentNode(_ secondaryContentNode: ASDisplayNode?, animated: Bool = false) { + if self.secondaryContentNode !== secondaryContentNode { + if let previous = self.secondaryContentNode, previous.supernode === self.clippingNode { + if animated { + previous.layer.animateAlpha(from: previous.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak previous] finished in + if finished { + previous?.removeFromSupernode() + previous?.layer.removeAllAnimations() + } + }) + } else { + previous.removeFromSupernode() + } + } + self.secondaryContentNode = secondaryContentNode + if let secondaryContentNode = secondaryContentNode { + self.clippingNode.addSubnode(secondaryContentNode) + + if animated { + secondaryContentNode.layer.animateAlpha(from: 0.0, to: secondaryContentNode.alpha, duration: 0.3) + } + } + } + } + + public func executeBack() -> Bool { + if self.backButtonNodeImpl.isInHierarchy { + self.backButtonNodeImpl.pressed(0) + } else if self.leftButtonNodeImpl.isInHierarchy { + self.leftButtonNodeImpl.pressed(0) + } else { + self.backButtonNodeImpl.pressed(0) + } + return true + } + + public func setHidden(_ hidden: Bool, animated: Bool) { + if let contentNode = self.contentNode, case .replacement = contentNode.mode { + } else { + let targetAlpha: CGFloat = hidden ? 0.0 : 1.0 + let previousAlpha = self.buttonsContainerNode.alpha + if previousAlpha != targetAlpha { + self.buttonsContainerNode.alpha = targetAlpha + if animated { + self.buttonsContainerNode.layer.animateAlpha(from: previousAlpha, to: targetAlpha, duration: 0.2) + } + } + } + } + + override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if let result = self.additionalContentNode.view.hitTest(self.view.convert(point, to: self.additionalContentNode.view), with: event) { + return result + } + + guard let result = super.hitTest(point, with: event) else { + if !self.bounds.contains(point) { + if let result = self.customOverBackgroundContentView.hitTest(self.view.convert(point, to: self.customOverBackgroundContentView), with: event) { + if result !== self.backgroundContainer?.contentView { + return result + } + } + } + return nil + } + + if self.passthroughTouches && (result == self.view || result == self.buttonsContainerNode.view || result == self.backgroundNode.view || result == self.backgroundNode.backgroundView || result == self.backgroundContainer?.contentView) { + return nil + } + + return result + } +} diff --git a/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationButtonNode.swift b/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationButtonNode.swift new file mode 100644 index 00000000..0446f196 --- /dev/null +++ b/submodules/TelegramUI/Components/NavigationBarImpl/Sources/NavigationButtonNode.swift @@ -0,0 +1,794 @@ +import UIKit +import AsyncDisplayKit +import Display +import ComponentFlow +import MultilineTextComponent +import AppBundle + +let glassBackArrowImage: UIImage? = { + let imageSize = CGSize(width: 44.0, height: 44.0) + let topRightPoint = CGPoint(x: 24.6, y: 14.0) + let centerPoint = CGPoint(x: 17.0, y: imageSize.height * 0.5) + return generateImage(imageSize, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setStrokeColor(UIColor.white.cgColor) + context.setLineWidth(2.0) + context.setLineCap(.round) + context.setLineJoin(.round) + context.move(to: topRightPoint) + context.addLine(to: centerPoint) + context.addLine(to: CGPoint(x: topRightPoint.x, y: size.height - topRightPoint.y)) + context.strokePath() + })?.withRenderingMode(.alwaysTemplate) +}() + +let glassCloseImage: UIImage? = { + return generateTintedImage(image: UIImage(bundleImageName: "Navigation/Close"), color: .white)?.withRenderingMode(.alwaysTemplate) +}() + +private final class ItemComponent: Component { + enum Content: Equatable { + case back + case item(UIBarButtonItem) + } + + let color: UIColor + let content: Content + + init( + color: UIColor, + content: Content + ) { + self.color = color + self.content = content + } + + static func ==(lhs: ItemComponent, rhs: ItemComponent) -> Bool { + if lhs.color != rhs.color { + return false + } + if lhs.content != rhs.content { + return false + } + return true + } + + final class View: UIView { + private var iconView: UIImageView? + private var title: ComponentView? + + private var component: ItemComponent? + private weak var state: EmptyComponentState? + var isUpdating: Bool = false + + private var setEnabledListener: Int? + private var setTitleListener: Int? + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(component: ItemComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.isUpdating = true + defer { + self.isUpdating = false + } + + if component.content != self.component?.content { + if case let .item(item) = self.component?.content { + if let setEnabledListener = self.setEnabledListener { + self.setEnabledListener = nil + item.removeSetEnabledListener(setEnabledListener) + } + if let setTitleListener = self.setTitleListener { + self.setTitleListener = nil + item.removeSetTitleListener(setTitleListener) + } + } + + switch component.content { + case .back: + break + case let .item(item): + self.setEnabledListener = item.addSetEnabledListener { [weak self] _ in + guard let self else { + return + } + if !self.isUpdating { + self.state?.updated(transition: .immediate) + } + } + self.setTitleListener = item.addSetTitleListener { [weak self] _ in + guard let self else { + return + } + if !self.isUpdating { + self.state?.updated(transition: .immediate) + } + } + } + } + + self.component = component + self.state = state + + var iconImage: UIImage? + var titleString: String? + switch component.content { + case .back: + iconImage = glassBackArrowImage + case let .item(item): + if item.image != nil { + iconImage = item.image + } else if let title = item.title { + if title == "___close" { + iconImage = glassCloseImage + } else { + titleString = title + } + } + } + + var size = CGSize(width: 44.0, height: 44.0) + + if let iconImage { + let iconView: UIImageView + var iconTransition = transition + if let current = self.iconView { + iconView = current + } else { + iconTransition = iconTransition.withAnimation(.none) + iconView = UIImageView() + self.iconView = iconView + } + iconView.image = iconImage + iconView.tintColor = component.color + + let iconFrame = iconImage.size.centered(in: CGRect(origin: CGPoint(), size: size)) + iconTransition.setFrame(view: iconView, frame: iconFrame) + } else if let iconView = self.iconView { + self.iconView = nil + iconView.removeFromSuperview() + } + + if let titleString { + let titleFont: UIFont + if case let .item(item) = component.content, case .done = item.style { + titleFont = Font.bold(17.0) + } else { + titleFont = Font.medium(17.0) + } + + let title: ComponentView + if let current = self.title { + title = current + } else { + title = ComponentView() + self.title = title + } + let titleSize = title.update( + transition: .immediate, + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: titleString, font: titleFont, textColor: component.color)) + )), + environment: {}, + containerSize: CGSize(width: 200.0, height: 100.0) + ) + + let titleInset: CGFloat = 6.0 + size.width = titleInset * 2.0 + titleSize.width + + let titleFrame = CGRect(origin: CGPoint(x: titleInset, y: floorToScreenPixels((size.height - titleSize.height) * 0.5)), size: titleSize) + if let titleView = title.view { + if titleView.superview == nil { + self.addSubview(titleView) + } + titleView.frame = titleFrame + } + } else if let title = self.title { + self.title = nil + if let titleView = title.view { + titleView.removeFromSuperview() + } + } + + return size + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + +private final class NavigationButtonItemNode: ImmediateTextNode { + private let isGlass: Bool + + private func fontForCurrentState() -> UIFont { + return self.bold ? Font.semibold(17.0) : Font.medium(17.0) + } + + private func attributesForCurrentState() -> [NSAttributedString.Key: AnyObject] { + return [ + NSAttributedString.Key.font: self.fontForCurrentState(), + NSAttributedString.Key.foregroundColor: self.isEnabled ? self.color : self.disabledColor + ] + } + + private var setEnabledListener: Int? + + var item: UIBarButtonItem? { + didSet { + if self.item !== oldValue { + if let oldValue = oldValue, let setEnabledListener = self.setEnabledListener { + oldValue.removeSetEnabledListener(setEnabledListener) + self.setEnabledListener = nil + } + + if let item = self.item { + self.setEnabledListener = item.addSetEnabledListener { [weak self] value in + self?.isEnabled = value + } + self.accessibilityHint = item.accessibilityHint + self.accessibilityLabel = item.accessibilityLabel + } + } + } + } + + private var _text: String? + public var text: String { + get { + return _text ?? "" + } + set(value) { + _text = value + + self.attributedText = NSAttributedString(string: text, attributes: self.attributesForCurrentState()) + if _image == nil { + if self.item?.accessibilityLabel == nil { + self.item?.accessibilityLabel = value + } + } + } + } + + private(set) var imageNode: ASImageNode? + + private var _image: UIImage? + public var image: UIImage? { + get { + return _image + } set(value) { + _image = value + + if let _ = value { + if self.imageNode == nil { + let imageNode = ASImageNode() + imageNode.displayWithoutProcessing = true + imageNode.displaysAsynchronously = false + self.imageNode = imageNode + + self.addSubnode(imageNode) + } + self.imageNode?.image = image + if self.imageNode?.image?.renderingMode == .alwaysTemplate { + self.imageNode?.tintColor = self.color + } else { + self.imageNode?.tintColor = nil + } + } else if let imageNode = self.imageNode { + imageNode.removeFromSupernode() + self.imageNode = nil + } + + self.invalidateCalculatedLayout() + self.setNeedsLayout() + } + } + + public var node: ASDisplayNode? { + didSet { + if self.node !== oldValue { + oldValue?.removeFromSupernode() + if let node = self.node { + self.addSubnode(node) + self.invalidateCalculatedLayout() + self.setNeedsLayout() + self.updatePointerInteraction() + } + } + } + } + + public var color: UIColor = UIColor(rgb: 0x0088ff) { + didSet { + if self.imageNode?.image?.renderingMode == .alwaysTemplate { + self.imageNode?.tintColor = self.color + } + if let text = self._text { + self.attributedText = NSAttributedString(string: text, attributes: self.attributesForCurrentState()) + } + } + } + + public var disabledColor: UIColor = UIColor(rgb: 0xd0d0d0) { + didSet { + if let text = self._text { + self.attributedText = NSAttributedString(string: text, attributes: self.attributesForCurrentState()) + } + } + } + + private var _bold: Bool = false + public var bold: Bool { + get { + return _bold + } + set(value) { + if _bold != value { + _bold = value + + self.attributedText = NSAttributedString(string: text, attributes: self.attributesForCurrentState()) + } + } + } + + private var touchCount = 0 + public var pressed: () -> () = { } + public var highlightChanged: (Bool) -> () = { _ in } + + override public var isAccessibilityElement: Bool { + get { + return true + } set(value) { + super.isAccessibilityElement = true + } + } + + override public var accessibilityLabel: String? { + get { + if let item = self.item, let accessibilityLabel = item.accessibilityLabel { + return accessibilityLabel + } else { + return self.attributedText?.string + } + } set(value) { + + } + } + + override public var accessibilityHint: String? { + get { + if let item = self.item, let accessibilityHint = item.accessibilityHint { + return accessibilityHint + } else { + return nil + } + } set(value) { + + } + } + + var pointerInteraction: PointerInteraction? + + init(isGlass: Bool) { + self.isGlass = isGlass + + super.init() + + self.isAccessibilityElement = true + + self.isUserInteractionEnabled = true + self.isExclusiveTouch = true + if !isGlass { + self.hitTestSlop = UIEdgeInsets(top: -16.0, left: -10.0, bottom: -16.0, right: -10.0) + } + self.displaysAsynchronously = false + + self.verticalAlignment = .middle + + self.accessibilityTraits = .button + } + + override func didLoad() { + super.didLoad() + self.updatePointerInteraction() + } + + func updatePointerInteraction() { + let pointerStyle: PointerStyle + if self.node != nil { + pointerStyle = .lift + } else { + pointerStyle = .insetRectangle(-8.0, 2.0) + } + self.pointerInteraction = PointerInteraction(node: self, style: pointerStyle) + } + + override func updateLayout(_ constrainedSize: CGSize) -> CGSize { + var superSize = super.updateLayout(constrainedSize) + + if let node = self.node { + let nodeSize = node.measure(constrainedSize) + let size = CGSize(width: max(nodeSize.width, superSize.width), height: max(nodeSize.height, superSize.height)) + node.frame = CGRect(origin: CGPoint(), size: nodeSize) + return size + } else if let imageNode = self.imageNode { + let nodeSize = imageNode.image?.size ?? CGSize() + let size = CGSize(width: max(nodeSize.width, superSize.width), height: max(44.0, max(nodeSize.height, superSize.height))) + let imageFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - nodeSize.width) / 2.0), y: floorToScreenPixels((size.height - nodeSize.height) / 2.0)), size: nodeSize) + imageNode.frame = imageFrame + return size + } else { + superSize.height = max(44.0, superSize.height) + } + return superSize + } + + private func touchInsideApparentBounds(_ touch: UITouch) -> Bool { + var apparentBounds = self.bounds + let hitTestSlop = self.hitTestSlop + apparentBounds.origin.x += hitTestSlop.left + apparentBounds.size.width += -hitTestSlop.left - hitTestSlop.right + apparentBounds.origin.y += hitTestSlop.top + apparentBounds.size.height += -hitTestSlop.top - hitTestSlop.bottom + + return apparentBounds.contains(touch.location(in: self.view)) + } + + public override func touchesBegan(_ touches: Set, with event: UIEvent?) { + super.touchesBegan(touches, with: event) + self.touchCount += touches.count + self.updateHighlightedState(true, animated: false) + } + + public override func touchesMoved(_ touches: Set, with event: UIEvent?) { + super.touchesMoved(touches, with: event) + } + + public override func touchesEnded(_ touches: Set, with event: UIEvent?) { + super.touchesEnded(touches, with: event) + self.updateHighlightedState(false, animated: false) + + let previousTouchCount = self.touchCount + self.touchCount = max(0, self.touchCount - touches.count) + + var touchInside = true + if let touch = touches.first, !self.isGlass { + touchInside = self.touchInsideApparentBounds(touch) + } + if previousTouchCount != 0 && self.touchCount == 0 && self.isEnabled && touchInside { + self.pressed() + } + } + + public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if let node = self.node as? HighlightableButtonNode { + let result = node.view.hitTest(self.view.convert(point, to: node.view), with: event) + return result + } else { + let previousAlpha = self.alpha + self.alpha = 1.0 + let result = super.hitTest(point, with: event) + self.alpha = previousAlpha + return result + } + } + + public override func touchesCancelled(_ touches: Set?, with event: UIEvent?) { + super.touchesCancelled(touches, with: event) + + self.touchCount = max(0, self.touchCount - (touches?.count ?? 0)) + self.updateHighlightedState(false, animated: false) + } + + private var _highlighted = false + private func updateHighlightedState(_ highlighted: Bool, animated: Bool) { + if _highlighted != highlighted { + _highlighted = highlighted + + var shouldChangeHighlight = true + if let node = self.node as? NavigationButtonCustomDisplayNode { + shouldChangeHighlight = node.isHighlightable + } + + if shouldChangeHighlight { + if self.alpha > 0.0 { + self.alpha = !self.isEnabled ? 1.0 : (highlighted ? 0.4 : 1.0) + } + self.highlightChanged(highlighted) + } + } + } + + public var isEnabled: Bool = true { + didSet { + if self.isEnabled != oldValue { + self.attributedText = NSAttributedString(string: self.text, attributes: self.attributesForCurrentState()) + if let constrainedSize = self.constrainedSize { + let _ = self.updateLayout(constrainedSize) + } + } + } + } +} + + +public final class NavigationButtonNodeImpl: ContextControllerSourceNode, NavigationButtonNode { + private let isGlass: Bool + private var isBack: Bool = false + + private var nodes: [NavigationButtonItemNode] = [] + + private var disappearingNodes: [(frame: CGRect, size: CGSize, node: NavigationButtonItemNode)] = [] + + public var singleCustomNode: ASDisplayNode? { + for node in self.nodes { + return node.node + } + return nil + } + + public var mainContentNode: ASDisplayNode? { + return self.nodes.first + } + + public var pressed: (Int) -> () = { _ in } + public var highlightChanged: (Int, Bool) -> () = { _, _ in } + + public var color: UIColor = UIColor(rgb: 0x0088ff) { + didSet { + if !self.color.isEqual(oldValue) { + for node in self.nodes { + node.color = self.color + } + } + } + } + + public var disabledColor: UIColor = UIColor(rgb: 0xd0d0d0) { + didSet { + if !self.disabledColor.isEqual(oldValue) { + for node in self.nodes { + node.disabledColor = self.disabledColor + } + } + } + } + + override public var accessibilityElements: [Any]? { + get { + return self.nodes + } set(value) { + } + } + + public init(isGlass: Bool) { + self.isGlass = isGlass + + super.init() + + self.isAccessibilityElement = false + self.isGestureEnabled = false + } + + public var manualText: String { + return self.nodes.first?.text ?? "" + } + + public var manualAlpha: CGFloat = 1.0 { + didSet { + for node in self.nodes { + node.alpha = self.manualAlpha + } + } + } + + public var contentsColor: UIColor? + + public func updateManualAlpha(alpha: CGFloat, transition: ContainedViewLayoutTransition) { + for node in self.nodes { + transition.updateAlpha(node: node, alpha: alpha) + } + } + + public func updateManualText(_ text: String, isBack: Bool = true) { + self.isBack = isBack + + let node: NavigationButtonItemNode + if self.nodes.count > 0 { + node = self.nodes[0] + } else { + node = NavigationButtonItemNode(isGlass: self.isGlass) + node.color = self.color + node.layer.layerTintColor = self.contentsColor?.cgColor + node.highlightChanged = { [weak node, weak self] value in + if let strongSelf = self, let node = node { + if let index = strongSelf.nodes.firstIndex(where: { $0 === node }) { + strongSelf.highlightChanged(index, value) + } + } + } + node.pressed = { [weak self, weak node] in + if let strongSelf = self, let node = node { + if let index = strongSelf.nodes.firstIndex(where: { $0 === node }) { + strongSelf.pressed(index) + } + } + } + self.nodes.append(node) + self.addSubnode(node) + } + node.alpha = self.manualAlpha + node.item = nil + node.image = nil + node.text = text + node.bold = false + node.isEnabled = true + node.node = nil + if !self.isGlass { + node.hitTestSlop = isBack ? UIEdgeInsets(top: 0.0, left: -20.0, bottom: 0.0, right: 0.0) : UIEdgeInsets() + } + + if 1 < self.nodes.count { + for i in 1 ..< self.nodes.count { + self.nodes[i].removeFromSupernode() + } + self.nodes.removeSubrange(1...) + } + } + + public func updateItems(_ items: [UIBarButtonItem], animated: Bool) { + for i in 0 ..< items.count { + let node: NavigationButtonItemNode + if self.nodes.count > i { + node = self.nodes[i] + } else { + node = NavigationButtonItemNode(isGlass: self.isGlass) + node.color = self.color + node.layer.layerTintColor = self.contentsColor?.cgColor + node.highlightChanged = { [weak node, weak self] value in + if let strongSelf = self, let node = node { + if let index = strongSelf.nodes.firstIndex(where: { $0 === node }) { + strongSelf.highlightChanged(index, value) + } + } + } + node.pressed = { [weak self, weak node] in + if let strongSelf = self, let node = node { + if let index = strongSelf.nodes.firstIndex(where: { $0 === node }) { + strongSelf.pressed(index) + } + } + } + self.nodes.append(node) + self.addSubnode(node) + } + node.alpha = self.manualAlpha + node.item = items[i] + if items[i].title == "___close" { + //node.image = glassCloseImage + node.image = generateTintedImage(image: UIImage(bundleImageName: "Navigation/Close"), color: self.color) + } else { + node.image = items[i].image + node.text = items[i].title ?? "" + } + node.bold = items[i].style == .done + node.isEnabled = items[i].isEnabled + node.node = items[i].customDisplayNode + + if animated { + node.layer.animateAlpha(from: 0.0, to: self.manualAlpha, duration: 0.16) + node.layer.animateScale(from: 0.001, to: 1.0, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring) + } + } + if items.count < self.nodes.count { + for i in items.count ..< self.nodes.count { + let itemNode = self.nodes[i] + if animated { + disappearingNodes.append((itemNode.frame, self.bounds.size, itemNode)) + itemNode.layer.animateAlpha(from: self.manualAlpha, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak self, weak itemNode] _ in + guard let itemNode else { + return + } + + itemNode.removeFromSupernode() + + guard let self else { + return + } + if let index = self.disappearingNodes.firstIndex(where: { $0.node === itemNode }) { + self.disappearingNodes.remove(at: index) + } + }) + itemNode.layer.animateScale(from: 1.0, to: 0.001, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) + } else { + itemNode.removeFromSupernode() + } + } + self.nodes.removeSubrange(items.count...) + } + } + + public func updateLayout(constrainedSize: CGSize, isLandscape: Bool, isLeftAligned: Bool) -> CGSize { + var nodeOrigin = CGPoint(x: 0.0, y: 0.0) + var totalHeight: CGFloat = 0.0 + for i in 0 ..< self.nodes.count { + if i != 0 && !self.isGlass { + nodeOrigin.x += 15.0 + } + + let node = self.nodes[i] + + var nodeSize = node.updateLayout(constrainedSize) + var nodeInset: CGFloat = 0.0 + if self.isGlass { + if node.image == nil && node.node == nil { + nodeInset += 12.0 + } + if nodeSize.width + nodeInset * 2.0 < 44.0 { + nodeInset = floorToScreenPixels((44.0 - nodeSize.width) * 0.5) + } + } + + nodeSize.width = ceil(nodeSize.width) + nodeSize.height = ceil(nodeSize.height) + totalHeight = max(totalHeight, nodeSize.height) + node.frame = CGRect(origin: CGPoint(x: nodeOrigin.x + nodeInset, y: floor((totalHeight - nodeSize.height) / 2.0)), size: nodeSize) + nodeOrigin.x += nodeInset + node.bounds.width + nodeInset + if isLandscape && !self.isGlass { + nodeOrigin.x += 16.0 + } + + if !self.isGlass && node.node == nil && node.imageNode != nil && i == self.nodes.count - 1 { + nodeOrigin.x -= 5.0 + } + } + + /*if !isLeftAligned { + for disappearingNode in self.disappearingNodes { + disappearingNode.node.frame = disappearingNode.frame.offsetBy(dx: nodeOrigin.x - disappearingNode.size.width, dy: (totalHeight - disappearingNode.size.height) * 0.5) + } + }*/ + + return CGSize(width: nodeOrigin.x, height: totalHeight) + } + + override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if self.nodes.count == 1 { + if self.isGlass && self.isBack { + if self.bounds.contains(point) { + return self.nodes[0].view + } + } + if self.bounds.contains(point) { + return self.nodes[0].view.hitTest(self.view.convert(point, to: self.nodes[0].view), with: event) + } else { + return nil + } + } else { + return super.hitTest(point, with: event) + } + } + + var isEmpty: Bool { + if self.isBack { + return false + } + for node in self.nodes { + if node.bounds.width != 0.0 { + return false + } + } + return true + } +} diff --git a/submodules/TelegramUI/Components/NotificationPeerExceptionController/Sources/NotificationPeerExceptionController.swift b/submodules/TelegramUI/Components/NotificationPeerExceptionController/Sources/NotificationPeerExceptionController.swift index 38b2e173..c9fcea3e 100644 --- a/submodules/TelegramUI/Components/NotificationPeerExceptionController/Sources/NotificationPeerExceptionController.swift +++ b/submodules/TelegramUI/Components/NotificationPeerExceptionController/Sources/NotificationPeerExceptionController.swift @@ -1095,7 +1095,7 @@ public func notificationPeerExceptionController( let presentationData = context.sharedContext.currentPresentationData.with { $0 } - controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.PeerInfo_DeleteToneTitle, text: presentationData.strings.PeerInfo_DeleteToneText(title).string, actions: [ + controller.present(textAlertController(context: context, title: presentationData.strings.PeerInfo_DeleteToneTitle, text: presentationData.strings.PeerInfo_DeleteToneText(title).string, actions: [ TextAlertAction(type: .destructiveAction, title: presentationData.strings.Common_Delete, action: { updateState { state in var state = state diff --git a/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/PeerAllowedReactionsScreen.swift b/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/PeerAllowedReactionsScreen.swift index 0842e3d3..a432b7ce 100644 --- a/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/PeerAllowedReactionsScreen.swift +++ b/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/PeerAllowedReactionsScreen.swift @@ -24,6 +24,7 @@ import PremiumLockButtonSubtitleComponent import ListSectionComponent import ListItemSliderSelectorComponent import ListSwitchItemComponent +import PresentationDataUtils final class PeerAllowedReactionsScreenComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment @@ -202,7 +203,7 @@ final class PeerAllowedReactionsScreenComponent: Component { self.applySettings(standalone: true) } else { let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } - self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.ChannelReactions_UnsavedChangesAlertTitle, text: presentationData.strings.ChannelReactions_UnsavedChangesAlertText, actions: [ + self.environment?.controller()?.present(textAlertController(context: component.context, title: presentationData.strings.ChannelReactions_UnsavedChangesAlertTitle, text: presentationData.strings.ChannelReactions_UnsavedChangesAlertText, actions: [ TextAlertAction(type: .genericAction, title: presentationData.strings.ChannelReactions_UnsavedChangesAlertDiscard, action: { [weak self] in guard let self else { return @@ -306,7 +307,7 @@ final class PeerAllowedReactionsScreenComponent: Component { self.displayPremiumScreen(reactionCount: customReactions.count) case .generic: let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } - self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + self.environment?.controller()?.present(textAlertController(context: component.context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } } }, completed: { [weak self] in diff --git a/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/BUILD b/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/BUILD index 7f66e412..f1e7a53a 100644 --- a/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/BUILD +++ b/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/BUILD @@ -34,6 +34,8 @@ swift_library( "//submodules/TelegramUI/Components/Stories/PeerListItemComponent", "//submodules/ContextUI", "//submodules/TelegramUI/Components/AlertComponent", + "//submodules/TelegramUI/Components/AlertComponent/AlertTableComponent", + "//submodules/TelegramUI/Components/Gifts/TableComponent", "//submodules/TelegramUI/Components/PlainButtonComponent", "//submodules/TelegramUI/Components/ToastComponent", "//submodules/AvatarNode", diff --git a/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/AffiliateProgramSetupScreen.swift b/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/AffiliateProgramSetupScreen.swift index 0940ba23..73d83595 100644 --- a/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/AffiliateProgramSetupScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/AffiliateProgramSetupScreen.swift @@ -26,6 +26,9 @@ import ContextUI import BalancedTextComponent import AlertComponent import PremiumCoinComponent +import AlertComponent +import AlertTableComponent +import TableComponent private func textForTimeout(value: Int32) -> String { if value < 3600 { @@ -64,13 +67,16 @@ final class AffiliateProgramSetupScreenComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment let context: AccountContext + let overNavigationContainer: UIView let initialContent: AffiliateProgramSetupScreen.Content init( context: AccountContext, + overNavigationContainer: UIView, initialContent: AffiliateProgramSetupScreen.Content ) { self.context = context + self.overNavigationContainer = overNavigationContainer self.initialContent = initialContent } @@ -93,6 +99,7 @@ final class AffiliateProgramSetupScreenComponent: Component { private let coinIcon = ComponentView() private let title = ComponentView() + private let titleContainer: UIView private let titleTransformContainer: UIView private var titleNeutralScale: CGFloat = 1.0 private let subtitle = ComponentView() @@ -157,6 +164,8 @@ final class AffiliateProgramSetupScreenComponent: Component { self.scrollView.contentInsetAdjustmentBehavior = .never self.scrollView.alwaysBounceVertical = true + self.titleContainer = SparseContainerView() + self.titleTransformContainer = UIView() self.titleTransformContainer.isUserInteractionEnabled = false @@ -208,30 +217,50 @@ final class AffiliateProgramSetupScreenComponent: Component { } else { durationTitle = environment.strings.AffiliateProgram_DurationLifetime } + + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: environment.strings.AffiliateSetup_AlertApply_Title) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(environment.strings.AffiliateSetup_AlertApply_Text)) + ) + )) - let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }) - self.environment?.controller()?.present(tableAlert( - theme: presentationData.theme, - title: environment.strings.AffiliateSetup_AlertApply_Title, - text: environment.strings.AffiliateSetup_AlertApply_Text, - table: TableComponent(theme: environment.theme, items: [ - TableComponent.Item(id: 0, title: environment.strings.AffiliateSetup_AlertApply_SectionCommission, component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: commissionTitle, font: Font.regular(17.0), textColor: environment.theme.actionSheet.primaryTextColor)) - ))), - TableComponent.Item(id: 1, title: environment.strings.AffiliateSetup_AlertApply_SectionDuration, component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: durationTitle, font: Font.regular(17.0), textColor: environment.theme.actionSheet.primaryTextColor)) - ))) - ]), + let tableItems: [TableComponent.Item] = [ + TableComponent.Item(id: 0, title: environment.strings.AffiliateSetup_AlertApply_SectionCommission, component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: commissionTitle, font: Font.regular(15.0), textColor: environment.theme.actionSheet.primaryTextColor)) + ))), + TableComponent.Item(id: 1, title: environment.strings.AffiliateSetup_AlertApply_SectionDuration, component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: durationTitle, font: Font.regular(15.0), textColor: environment.theme.actionSheet.primaryTextColor)) + ))) + ] + content.append(AnyComponentWithIdentity( + id: "table", + component: AnyComponent( + AlertTableComponent(items: tableItems) + ) + )) + + let alertController = AlertScreen( + context: component.context, + content: content, actions: [ - ComponentAlertAction(type: .genericAction, title: environment.strings.Common_Cancel, action: {}), - ComponentAlertAction(type: .defaultAction, title: environment.strings.AffiliateSetup_AlertApply_Action, action: { [weak self] in + .init(title: environment.strings.Common_Cancel), + .init(title: environment.strings.AffiliateSetup_AlertApply_Action, type: .default, action: { [weak self] in guard let self else { return } self.applyProgram() }) ] - ), in: .window(.root)) + ) + self.environment?.controller()?.present(alertController, in: .window(.root)) } private func requestApplyEndProgram() { @@ -239,8 +268,8 @@ final class AffiliateProgramSetupScreenComponent: Component { return } let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }) - self.environment?.controller()?.present(standardTextAlertController( - theme: AlertControllerTheme(presentationData: presentationData), + self.environment?.controller()?.present(textAlertController( + context: component.context, title: environment.strings.AffiliateSetup_AlertTerminate_Title, text: environment.strings.AffiliateSetup_AlertTerminate_Text, actions: [ @@ -337,6 +366,8 @@ final class AffiliateProgramSetupScreenComponent: Component { transition.setSublayerTransform(view: self.titleTransformContainer, transform: CATransform3DMakeTranslation(0.0, titleY - self.titleTransformContainer.center.y, 0.0)) + transition.setSublayerTransform(view: self.titleContainer, transform: CATransform3DMakeTranslation(0.0, -self.scrollView.contentOffset.y, 0.0)) + let titleYDistance: CGFloat = titleY - titleCenterY let titleTransformFraction: CGFloat = 1.0 - max(0.0, min(1.0, titleYDistance / titleTransformDistance)) let titleMinScale: CGFloat = 17.0 / 30.0 @@ -625,6 +656,10 @@ final class AffiliateProgramSetupScreenComponent: Component { self.component = component self.state = state + if self.titleContainer.superview == nil { + component.overNavigationContainer.addSubview(self.titleContainer) + } + let topInset: CGFloat = environment.navigationHeight + 90.0 let bottomInset: CGFloat = 8.0 let sideInset: CGFloat = 16.0 + environment.safeInsets.left @@ -648,7 +683,7 @@ final class AffiliateProgramSetupScreenComponent: Component { let coinIconFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - coinIconSize.width) * 0.5), y: contentHeight - coinIconSize.height + 30.0), size: coinIconSize) if let coinIconView = self.coinIcon.view { if coinIconView.superview == nil { - self.scrollView.addSubview(coinIconView) + self.titleContainer.addSubview(coinIconView) } transition.setFrame(view: coinIconView, frame: coinIconFrame) } @@ -1158,6 +1193,7 @@ final class AffiliateProgramSetupScreenComponent: Component { transition: transition, component: AnyComponent(ButtonComponent( background: ButtonComponent.Background( + style: .glass, color: environment.theme.list.itemCheckColors.fillColor, foreground: environment.theme.list.itemCheckColors.foregroundColor, pressedColor: environment.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.8) @@ -1174,7 +1210,7 @@ final class AffiliateProgramSetupScreenComponent: Component { } )), environment: {}, - containerSize: CGSize(width: availableSize.width - bottomPanelButtonInsets.left - bottomPanelButtonInsets.right, height: 50.0) + containerSize: CGSize(width: availableSize.width - bottomPanelButtonInsets.left - bottomPanelButtonInsets.right, height: 52.0) ) let bottomPanelHeight: CGFloat = bottomPanelButtonInsets.top + bottomPanelButtonSize.height + bottomPanelButtonInsets.bottom + bottomPanelTextSize.height + 8.0 + environment.safeInsets.bottom @@ -1620,16 +1656,21 @@ public class AffiliateProgramSetupScreen: ViewControllerComponentContainer { private let context: AccountContext private var isDismissed: Bool = false + private let overNavigationContainer: UIView + public init( context: AccountContext, initialContent: AffiliateProgramSetupScreenInitialData ) { self.context = context + self.overNavigationContainer = SparseContainerView() + let initialContent = initialContent as! AffiliateProgramSetupScreen.Content super.init(context: context, component: AffiliateProgramSetupScreenComponent( context: context, + overNavigationContainer: self.overNavigationContainer, initialContent: initialContent ), navigationBarAppearance: .default, theme: .default) @@ -1647,6 +1688,10 @@ public class AffiliateProgramSetupScreen: ViewControllerComponentContainer { return componentView.attemptNavigation(complete: complete) } + + if let navigationBar = self.navigationBar { + navigationBar.customOverBackgroundContentView.insertSubview(self.overNavigationContainer, at: 0) + } } required public init(coder aDecoder: NSCoder) { diff --git a/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/JoinAffiliateProgramScreen.swift b/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/JoinAffiliateProgramScreen.swift index d8d473f8..3df157c9 100644 --- a/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/JoinAffiliateProgramScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen/Sources/JoinAffiliateProgramScreen.swift @@ -1130,11 +1130,11 @@ private final class JoinAffiliateProgramScreenComponent: Component { )), background: AnyComponent(FilledRoundedRectangleComponent( color: environment.theme.list.itemInputField.backgroundColor, - cornerRadius: .value(8.0), + cornerRadius: .minEdge, smoothCorners: true )), effectAlignment: .center, - minSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 50.0), + minSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 52.0), contentInsets: UIEdgeInsets(top: 0.0, left: 10.0, bottom: 0.0, right: 10.0), action: { [weak self] in guard let self, case let .active(active) = self.currentMode else { @@ -1148,7 +1148,7 @@ private final class JoinAffiliateProgramScreenComponent: Component { animateContents: false )), environment: {}, - containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 50.0) + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 52.0) ) let linkTextFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - linkTextSize.width) * 0.5), y: contentHeight), size: linkTextSize) if let linkTextView = self.linkText.view { @@ -1170,6 +1170,7 @@ private final class JoinAffiliateProgramScreenComponent: Component { actionButtonTitle = environment.strings.AffiliateProgram_ActionCopyLink } + let buttonSideInset: CGFloat = 30.0 let actionButtonSize = self.actionButton.update( transition: transition, component: AnyComponent(ButtonComponent( @@ -1208,7 +1209,7 @@ private final class JoinAffiliateProgramScreenComponent: Component { } )), environment: {}, - containerSize: CGSize(width: availableSize.width - 30.0 * 2.0, height: 52.0) + containerSize: CGSize(width: availableSize.width - buttonSideInset * 2.0, height: 52.0) ) let bottomTextSize = self.bottomText.update( @@ -1238,7 +1239,7 @@ private final class JoinAffiliateProgramScreenComponent: Component { let bottomPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - bottomPanelHeight), size: CGSize(width: availableSize.width, height: bottomPanelHeight)) transition.setFrame(view: self.bottomPanelContainer, frame: bottomPanelFrame) - let actionButtonFrame = CGRect(origin: CGPoint(x: sideInset, y: 0.0), size: actionButtonSize) + let actionButtonFrame = CGRect(origin: CGPoint(x: buttonSideInset, y: 0.0), size: actionButtonSize) if let actionButtonView = self.actionButton.view { if actionButtonView.superview == nil { self.bottomPanelContainer.addSubview(actionButtonView) diff --git a/submodules/TelegramUI/Components/PeerInfo/CollectionTabItemComponent/Sources/CollectionTabItemComponent.swift b/submodules/TelegramUI/Components/PeerInfo/CollectionTabItemComponent/Sources/CollectionTabItemComponent.swift index f4d9a8ba..f325d960 100644 --- a/submodules/TelegramUI/Components/PeerInfo/CollectionTabItemComponent/Sources/CollectionTabItemComponent.swift +++ b/submodules/TelegramUI/Components/PeerInfo/CollectionTabItemComponent/Sources/CollectionTabItemComponent.swift @@ -63,8 +63,8 @@ public final class CollectionTabItemComponent: Component { let iconSpacing: CGFloat = 3.0 - let normalColor = component.theme.list.itemSecondaryTextColor - let selectedColor = component.theme.list.freeTextColor + let normalColor = component.theme.list.itemPrimaryTextColor + let selectedColor = component.theme.list.itemPrimaryTextColor let effectiveColor = normalColor.mixedWith(selectedColor, alpha: environment.selectionFraction) let titleSize = self.title.update( @@ -99,6 +99,7 @@ public final class CollectionTabItemComponent: Component { pointSize: iconSize, loopCount: 1 ) + iconLayer.isVisibleForAnimations = true self.layer.addSublayer(iconLayer) self.iconLayer = iconLayer } @@ -109,7 +110,7 @@ public final class CollectionTabItemComponent: Component { transition: .immediate, component: AnyComponent(BundleIconComponent( name: "Chat/Input/Media/PanelBadgeAdd", - tintColor: component.theme.list.itemSecondaryTextColor + tintColor: component.theme.list.itemPrimaryTextColor )), environment: {}, containerSize: CGSize(width: 100.0, height: 100.0) diff --git a/submodules/TelegramUI/Components/PeerInfo/MessagePriceItem/Sources/MessagePriceItem.swift b/submodules/TelegramUI/Components/PeerInfo/MessagePriceItem/Sources/MessagePriceItem.swift index 02aa0262..2323498d 100644 --- a/submodules/TelegramUI/Components/PeerInfo/MessagePriceItem/Sources/MessagePriceItem.swift +++ b/submodules/TelegramUI/Components/PeerInfo/MessagePriceItem/Sources/MessagePriceItem.swift @@ -245,7 +245,7 @@ private class MessagePriceItemNode: ListViewItemNode { self.button = ComponentView() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.leftTextNode) self.addSubnode(self.rightTextNode) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift index 32e80566..ba66c7c4 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatListPaneNode/Sources/PeerInfoChatListPaneNode.swift @@ -18,6 +18,7 @@ import PeerInfoPaneNode import ChatListUI import DeleteChatPeerActionSheetItem import UndoUI +import ComponentDisplayAdapters private final class SearchNavigationContentNode: ASDisplayNode, PeerInfoPanelNodeNavigationContentNode { private struct Params: Equatable { @@ -58,15 +59,15 @@ private final class SearchNavigationContentNode: ASDisplayNode, PeerInfoPanelNod func update(width: CGFloat, defaultHeight: CGFloat, insets: UIEdgeInsets, transition: ContainedViewLayoutTransition) -> CGFloat { self.params = Params(width: width, defaultHeight: defaultHeight, insets: insets) - let size = CGSize(width: width, height: defaultHeight) - transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 10.0), size: size)) - self.contentNode.updateLayout(size: size, leftInset: insets.left, rightInset: insets.right, transition: transition) + let size = CGSize(width: width, height: 60.0) + transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -6.0), size: size)) + let _ = self.contentNode.updateLayout(size: size, leftInset: insets.left, rightInset: insets.right, transition: transition) - var contentHeight: CGFloat = size.height + 10.0 + var contentHeight: CGFloat = size.height if self.appliedPanelNode !== self.panelNode { if let previous = self.appliedPanelNode { - transition.updateAlpha(node: previous, alpha: 0.0, completion: { [weak previous] _ in + ComponentTransition(transition).setAlpha(view: previous.view, alpha: 0.0, completion: { [weak previous] _ in previous?.removeFromSupernode() }) } @@ -79,9 +80,10 @@ private final class SearchNavigationContentNode: ASDisplayNode, PeerInfoPanelNod let panelFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: width, height: panelHeight)) panelNode.frame = panelFrame panelNode.alpha = 0.0 - transition.updateAlpha(node: panelNode, alpha: 1.0) + ComponentTransition(transition).setAlpha(view: panelNode.view, alpha: 1.0) - contentHeight += panelHeight - 1.0 + contentHeight += 14.0 + 66.0 + contentHeight += panelHeight } } else if let panelNode = self.panelNode, let chatController = self.chatController { let panelLayout = panelNode.updateLayout(width: width, leftInset: insets.left, rightInset: insets.right, transition: transition, chatController: chatController) @@ -89,7 +91,8 @@ private final class SearchNavigationContentNode: ASDisplayNode, PeerInfoPanelNod let panelFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: width, height: panelHeight)) transition.updateFrame(node: panelNode, frame: panelFrame) - contentHeight += panelHeight - 1.0 + contentHeight += 14.0 + 66.0 + contentHeight += panelHeight } return contentHeight @@ -256,11 +259,11 @@ public final class PeerInfoChatListPaneNode: ASDisplayNode, PeerInfoPaneNode, AS } else if let emptyShimmerEffectNode = self.emptyShimmerEffectNode { self.emptyShimmerEffectNode = nil let emptyNodeTransition = transition.isAnimated ? transition : .animated(duration: 0.3, curve: .easeInOut) - emptyNodeTransition.updateAlpha(node: emptyShimmerEffectNode, alpha: 0.0, completion: { [weak emptyShimmerEffectNode] _ in + ComponentTransition(emptyNodeTransition).setAlpha(view: emptyShimmerEffectNode.view, alpha: 0.0, completion: { [weak emptyShimmerEffectNode] _ in emptyShimmerEffectNode?.removeFromSupernode() }) self.chatListNode.alpha = 0.0 - emptyNodeTransition.updateAlpha(node: self.chatListNode, alpha: 1.0) + ComponentTransition(emptyNodeTransition).setAlpha(view: self.chatListNode.view, alpha: 1.0) } } @@ -443,7 +446,7 @@ public final class PeerInfoChatListPaneNode: ASDisplayNode, PeerInfoPaneNode, AS chatController.displayNode.layer.allowsGroupOpacity = true if transition.isAnimated { - ComponentTransition.easeInOut(duration: 0.2).setAlpha(layer: chatController.displayNode.layer, alpha: 1.0) + ComponentTransition.easeInOut(duration: 0.2).setAlpha(view: chatController.displayNode.view, alpha: 1.0) } if self.searchNavigationContentNode?.contentNode !== contentNode { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatPaneNode/Sources/PeerInfoChatPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatPaneNode/Sources/PeerInfoChatPaneNode.swift index c3845d1c..f591cb18 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatPaneNode/Sources/PeerInfoChatPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoChatPaneNode/Sources/PeerInfoChatPaneNode.swift @@ -54,7 +54,7 @@ private final class SearchNavigationContentNode: ASDisplayNode, PeerInfoPanelNod let size = CGSize(width: width, height: defaultHeight) transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 10.0), size: size)) - self.contentNode.updateLayout(size: size, leftInset: insets.left, rightInset: insets.right, transition: transition) + let _ = self.contentNode.updateLayout(size: size, leftInset: insets.left, rightInset: insets.right, transition: transition) var contentHeight: CGFloat = size.height + 10.0 @@ -182,8 +182,6 @@ public final class PeerInfoChatPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScro self.addSubnode(self.chatController.displayNode) self.chatController.displayNode.clipsToBounds = true - self.view.addSubview(self.coveringView) - self.chatController.stateUpdated = { [weak self] transition in guard let self else { return @@ -276,9 +274,9 @@ public final class PeerInfoChatPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScro public func update(size: CGSize, topInset: CGFloat, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, isScrollingLockedAtTop: Bool, expandProgress: CGFloat, navigationHeight: CGFloat, presentationData: PresentationData, synchronous: Bool, transition: ContainedViewLayoutTransition) { self.currentParams = (size, topInset, sideInset, bottomInset, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) - let fullHeight = navigationHeight + size.height + let fullHeight = size.height - topInset - let chatFrame = CGRect(origin: CGPoint(x: 0.0, y: -navigationHeight), size: CGSize(width: size.width, height: fullHeight)) + let chatFrame = CGRect(origin: CGPoint(x: 0.0, y: topInset), size: CGSize(width: size.width, height: fullHeight)) if !self.chatController.displayNode.bounds.isEmpty { if let contextController = self.chatController.visibleContextController as? ContextController { @@ -293,7 +291,7 @@ public final class PeerInfoChatPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScro let combinedBottomInset = bottomInset transition.updateFrame(node: self.chatController.displayNode, frame: chatFrame) self.chatController.updateIsScrollingLockedAtTop(isScrollingLockedAtTop: isScrollingLockedAtTop) - self.chatController.containerLayoutUpdated(ContainerViewLayout(size: chatFrame.size, metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact, orientation: nil), deviceMetrics: deviceMetrics, intrinsicInsets: UIEdgeInsets(top: topInset + navigationHeight, left: sideInset, bottom: combinedBottomInset, right: sideInset), safeInsets: UIEdgeInsets(top: navigationHeight + topInset + 4.0, left: sideInset, bottom: combinedBottomInset, right: sideInset), additionalInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: transition) + self.chatController.containerLayoutUpdated(ContainerViewLayout(size: chatFrame.size, metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact, orientation: nil), deviceMetrics: deviceMetrics, intrinsicInsets: UIEdgeInsets(top: 0.0, left: sideInset, bottom: combinedBottomInset, right: sideInset), safeInsets: UIEdgeInsets(top: 0.0 + 4.0, left: sideInset, bottom: combinedBottomInset, right: sideInset), additionalInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: transition) } override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD index a7d4c39c..532cde91 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/BUILD @@ -168,6 +168,11 @@ swift_library( "//submodules/TelegramUI/Components/MediaManager/PeerMessagesMediaPlaylist", "//submodules/TelegramUI/Components/TextFieldComponent", "//submodules/TelegramUI/Components/EdgeEffect", + "//submodules/TelegramUI/Components/GlassBackgroundComponent", + "//submodules/TelegramUI/Components/AlertComponent", + "//submodules/TelegramUI/Components/AvatarComponent", + "//submodules/TelegramUI/Components/AlertComponent/AlertTransferHeaderComponent", + "//submodules/TelegramUI/Components/HorizontalTabsComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenBusinessHoursItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenBusinessHoursItem.swift index bd56a1ee..a4c4781a 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenBusinessHoursItem.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenBusinessHoursItem.swift @@ -308,7 +308,7 @@ private final class PeerInfoScreenBusinessHoursItemNode: PeerInfoScreenItemNode let labelSize = self.labelNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: .greatestFiniteMagnitude)) - var topOffset = 10.0 + var topOffset = 15.0 let labelFrame = CGRect(origin: CGPoint(x: sideInset, y: topOffset), size: labelSize) if labelSize.height > 0.0 { topOffset += labelSize.height @@ -632,7 +632,7 @@ private final class PeerInfoScreenBusinessHoursItemNode: PeerInfoScreenItemNode topOffset += dayHeights } - topOffset += 11.0 + topOffset += 15.0 transition.updateFrame(node: self.labelNode, frame: labelFrame) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenCallListItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenCallListItem.swift index dd685d04..f8577378 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenCallListItem.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenCallListItem.swift @@ -74,13 +74,15 @@ private final class PeerInfoScreenCallListItemNode: PeerInfoScreenItemNode { let params = ListViewItemLayoutParams(width: width, leftInset: safeInsets.left, rightInset: safeInsets.right, availableHeight: 1000.0) + let verticalInset: CGFloat = 8.0 + let itemNode: ItemListCallListItemNode if let current = self.itemNode { itemNode = current addressItem.updateNode(async: { $0() }, node: { return itemNode }, params: params, previousItem: nil, nextItem: nil, animation: .None, completion: { (layout, apply) in - let nodeFrame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: layout.size.height)) + let nodeFrame = CGRect(origin: CGPoint(x: 0.0, y: verticalInset), size: CGSize(width: width, height: layout.size.height)) itemNode.contentSize = layout.contentSize itemNode.insets = layout.insets @@ -100,9 +102,8 @@ private final class PeerInfoScreenCallListItemNode: PeerInfoScreenItemNode { self.addSubnode(itemNode) } - let verticalInset: CGFloat = 8.0 - let height = itemNode.contentSize.height + verticalInset * 2.0 + let height = itemNode.contentSize.height + verticalInset * 2.0 transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(x: 0.0, y: verticalInset), size: itemNode.bounds.size)) let highlightNodeOffset: CGFloat = topItem == nil ? 0.0 : UIScreenPixel diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenPersonalChannelItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenPersonalChannelItem.swift index 815b7bfa..80f58c4f 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenPersonalChannelItem.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/ListItems/PeerInfoScreenPersonalChannelItem.swift @@ -183,7 +183,6 @@ public final class LoadingOverlayNode: ASDisplayNode { }, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, setPeerThreadHidden: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, _, gesture, _ in gesture?.cancel() }, present: { _ in }, openForumThread: { _, _ in }, openStorageManagement: {}, openPasswordSetup: {}, openPremiumIntro: {}, openPremiumGift: { _, _ in }, openPremiumManagement: {}, openActiveSessions: {}, openBirthdaySetup: {}, performActiveSessionAction: { _, _ in }, openChatFolderUpdates: {}, hideChatFolderUpdates: {}, openStories: { _, _ in }, openStarsTopup: { _ in - }, dismissNotice: { _ in }, editPeer: { _ in }, openWebApp: { _ in }, openPhotoSetup: { @@ -541,8 +540,6 @@ private final class PeerInfoScreenPersonalChannelItemNode: PeerInfoScreenItemNod }, openStarsTopup: { _ in }, - dismissNotice: { _ in - }, editPeer: { _ in }, openWebApp: { _ in diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoGifPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoGifPaneNode.swift index e5f376cf..45d59615 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoGifPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoGifPaneNode.swift @@ -500,6 +500,7 @@ private func tagMaskForType(_ type: PeerInfoGifPaneNode.ContentType) -> MessageT private enum ItemsLayout { final class Grid { let containerWidth: CGFloat + let topInset: CGFloat let itemCount: Int let itemSpacing: CGFloat let itemsInRow: Int @@ -507,8 +508,9 @@ private enum ItemsLayout { let rowCount: Int let contentHeight: CGFloat - init(containerWidth: CGFloat, itemCount: Int, bottomInset: CGFloat) { + init(containerWidth: CGFloat, itemCount: Int, topInset: CGFloat, bottomInset: CGFloat) { self.containerWidth = containerWidth + self.topInset = topInset self.itemCount = itemCount self.itemSpacing = 1.0 self.itemsInRow = max(3, min(6, Int(containerWidth / 140.0))) @@ -516,13 +518,13 @@ private enum ItemsLayout { self.rowCount = itemCount / self.itemsInRow + (itemCount % self.itemsInRow == 0 ? 0 : 1) - self.contentHeight = CGFloat(self.rowCount + 1) * self.itemSpacing + CGFloat(rowCount) * itemSize + bottomInset + self.contentHeight = topInset + CGFloat(self.rowCount + 1) * self.itemSpacing + CGFloat(rowCount) * itemSize + bottomInset } func visibleRange(rect: CGRect) -> (Int, Int) { - var minVisibleRow = Int(floor((rect.minY - self.itemSpacing) / (self.itemSize + self.itemSpacing))) + var minVisibleRow = Int(floor((rect.minY - self.topInset - self.itemSpacing) / (self.itemSize + self.itemSpacing))) minVisibleRow = max(0, minVisibleRow) - var maxVisibleRow = Int(ceil((rect.maxY - self.itemSpacing) / (self.itemSize + itemSpacing))) + var maxVisibleRow = Int(ceil((rect.maxY - self.topInset - self.itemSpacing) / (self.itemSize + itemSpacing))) maxVisibleRow = min(self.rowCount - 1, maxVisibleRow) let minVisibleIndex = minVisibleRow * itemsInRow @@ -534,7 +536,7 @@ private enum ItemsLayout { func frame(forItemAt index: Int, sideInset: CGFloat) -> CGRect { let rowIndex = index / Int(self.itemsInRow) let columnIndex = index % Int(self.itemsInRow) - let itemOrigin = CGPoint(x: sideInset + CGFloat(columnIndex) * (self.itemSize + self.itemSpacing), y: self.itemSpacing + CGFloat(rowIndex) * (self.itemSize + self.itemSpacing)) + let itemOrigin = CGPoint(x: sideInset + CGFloat(columnIndex) * (self.itemSize + self.itemSpacing), y: self.topInset + self.itemSpacing + CGFloat(rowIndex) * (self.itemSize + self.itemSpacing)) return CGRect(origin: itemOrigin, size: CGSize(width: columnIndex == self.itemsInRow ? (self.containerWidth - itemOrigin.x) : self.itemSize, height: self.itemSize)) } } @@ -906,7 +908,7 @@ final class PeerInfoGifPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScrollViewDe let previousParams = self.currentParams self.currentParams = (size, topInset, sideInset, bottomInset, deviceMetrics, visibleHeight, isScrollingLockedAtTop, expandProgress, navigationHeight, presentationData) - transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset), size: CGSize(width: size.width, height: size.height - topInset))) + transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))) let availableWidth = size.width - sideInset * 2.0 @@ -916,7 +918,7 @@ final class PeerInfoGifPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScrollViewDe } else { switch self.contentType { case .photoOrVideo, .gifs: - itemsLayout = .grid(ItemsLayout.Grid(containerWidth: availableWidth, itemCount: self.mediaItems.count, bottomInset: bottomInset)) + itemsLayout = .grid(ItemsLayout.Grid(containerWidth: availableWidth, itemCount: self.mediaItems.count, topInset: topInset, bottomInset: bottomInset)) } self.itemsLayout = itemsLayout } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoListPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoListPaneNode.swift index 41133017..2b4c09f8 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoListPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoListPaneNode.swift @@ -80,7 +80,7 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode { self.selectedMessages = chatControllerInteraction.selectionState.flatMap { $0.selectedIds } self.selectedMessagesPromise.set(.single(self.selectedMessages)) - self.listNode = context.sharedContext.makeChatHistoryListNode(context: context, updatedPresentationData: updatedPresentationData ?? (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, tag: .tag(tagMask), source: .default, subject: nil, controllerInteraction: chatControllerInteraction, selectedMessages: self.selectedMessagesPromise.get(), mode: .list(search: false, reversed: false, reverseGroups: false, displayHeaders: .allButLast, hintLinks: tagMask == .webPage, isGlobalSearch: false)) + self.listNode = context.sharedContext.makeChatHistoryListNode(context: context, updatedPresentationData: updatedPresentationData ?? (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, tag: .tag(tagMask), source: .default, subject: nil, controllerInteraction: chatControllerInteraction, selectedMessages: self.selectedMessagesPromise.get(), mode: .list(reversed: false, reverseGroups: false, displayHeaders: .allButLast, hintLinks: tagMask == .webPage, isGlobalSearch: false)) self.listNode.clipsToBounds = true self.listNode.defaultToSynchronousTransactionWhileScrolling = true self.listNode.scroller.bounces = false diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift index ef18776e..6582a26c 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/Panes/PeerInfoMembersPane.swift @@ -166,6 +166,7 @@ private enum PeerMembersListEntry: Comparable, Identifiable { actionIcon: .none, index: nil, header: nil, + hideBackground: true, action: member.peer.id == context.account.peerId ? nil : { _ in action(member, .open) }, diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift index 3da02f5c..f42fd8cf 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift @@ -13,6 +13,23 @@ private enum MoreIconNodeState: Equatable { case moreToSearch(Float) } +private let glassBackArrowImage: UIImage? = { + let imageSize = CGSize(width: 44.0, height: 44.0) + let topRightPoint = CGPoint(x: 24.6, y: 14.0) + let centerPoint = CGPoint(x: 17.0, y: imageSize.height * 0.5) + return generateImage(imageSize, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setStrokeColor(UIColor.white.cgColor) + context.setLineWidth(2.0) + context.setLineCap(.round) + context.setLineJoin(.round) + context.move(to: topRightPoint) + context.addLine(to: centerPoint) + context.addLine(to: CGPoint(x: topRightPoint.x, y: size.height - topRightPoint.y)) + context.strokePath() + })?.withRenderingMode(.alwaysTemplate) +}() + private final class MoreIconNode: ManagedAnimationNode { private let duration: Double = 0.21 private var iconState: MoreIconNodeState = .more @@ -124,14 +141,11 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { let contextSourceNode: ContextReferenceContentNode private let textNode: ImmediateTextNode private let iconNode: ASImageNode - private let backIconLayer: SimpleShapeLayer private var animationNode: MoreIconNode? - private let backgroundNode: NavigationBackgroundNode private var key: PeerInfoHeaderNavigationButtonKey? private var contentsColor: UIColor = .white - private var canBeExpanded: Bool = false var action: ((ASDisplayNode, ContextGesture?) -> Void)? @@ -146,27 +160,14 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { self.iconNode.displaysAsynchronously = false self.iconNode.displayWithoutProcessing = true - self.backIconLayer = SimpleShapeLayer() - self.backIconLayer.lineWidth = 3.0 - self.backIconLayer.lineCap = .round - self.backIconLayer.lineJoin = .round - self.backIconLayer.strokeColor = UIColor.white.cgColor - self.backIconLayer.fillColor = nil - self.backIconLayer.isHidden = true - self.backIconLayer.path = try? convertSvgPath("M10.5,2 L1.5,11 L10.5,20 ") - - self.backgroundNode = NavigationBackgroundNode(color: .clear, enableBlur: true) - - super.init(pointerStyle: .insetRectangle(-8.0, 2.0)) + super.init(pointerStyle: .insetRectangle(0.0, 0.0)) self.isAccessibilityElement = true self.accessibilityTraits = .button self.containerNode.addSubnode(self.contextSourceNode) - self.contextSourceNode.addSubnode(self.backgroundNode) self.contextSourceNode.addSubnode(self.textNode) self.contextSourceNode.addSubnode(self.iconNode) - self.contextSourceNode.layer.addSublayer(self.backIconLayer) self.addSubnode(self.containerNode) @@ -178,10 +179,6 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { } self.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside) - - if isReduceTransparencyEnabled() { - self.backgroundNode.isHidden = true - } } @objc private func pressed() { @@ -190,11 +187,7 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { } override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - var boundingRect = self.bounds - if self.textNode.alpha != 0.0 { - boundingRect = boundingRect.union(self.textNode.frame) - } - boundingRect = boundingRect.insetBy(dx: -8.0, dy: -4.0) + let boundingRect = self.bounds if boundingRect.contains(point) { return super.hitTest(self.bounds.center, with: event) } else { @@ -202,31 +195,11 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { } } - func updateContentsColor(backgroundColor: UIColor, contentsColor: UIColor, canBeExpanded: Bool, transition: ContainedViewLayoutTransition) { + func updateContentsColor(contentsColor: UIColor, transition: ContainedViewLayoutTransition) { self.contentsColor = contentsColor - self.canBeExpanded = canBeExpanded - - self.backgroundNode.updateColor(color: backgroundColor, transition: transition) transition.updateTintColor(layer: self.textNode.layer, color: self.contentsColor) transition.updateTintColor(view: self.iconNode.view, color: self.contentsColor) - transition.updateStrokeColor(layer: self.backIconLayer, strokeColor: self.contentsColor) - - switch self.key { - case .back: - transition.updateAlpha(layer: self.textNode.layer, alpha: canBeExpanded ? 1.0 : 0.0) - transition.updateTransformScale(node: self.textNode, scale: canBeExpanded ? 1.0 : 0.001) - - var iconTransform = CATransform3DIdentity - iconTransform = CATransform3DScale(iconTransform, canBeExpanded ? 1.0 : 0.8, canBeExpanded ? 1.0 : 0.8, 1.0) - iconTransform = CATransform3DTranslate(iconTransform, canBeExpanded ? -7.0 : 0.0, 0.0, 0.0) - transition.updateTransform(node: self.iconNode, transform: CATransform3DGetAffineTransform(iconTransform)) - - transition.updateTransform(layer: self.backIconLayer, transform: CATransform3DGetAffineTransform(iconTransform)) - transition.updateLineWidth(layer: self.backIconLayer, lineWidth: canBeExpanded ? 3.0 : 2.075) - default: - break - } if let animationNode = self.animationNode { transition.updateTintColor(layer: animationNode.imageNode.layer, color: self.contentsColor) @@ -239,7 +212,7 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { var iconOffset = CGPoint() switch key { case .back: - iconOffset = CGPoint(x: -1.0, y: 0.0) + iconOffset = CGPoint(x: 0.0, y: 0.0) default: break } @@ -258,9 +231,9 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { var animationState: MoreIconNodeState = .more switch key { case .back: - text = presentationData.strings.Common_Back + text = "" accessibilityText = presentationData.strings.Common_Back - icon = NavigationBar.backArrowImage(color: .white) + icon = glassBackArrowImage case .edit: text = presentationData.strings.Common_Edit accessibilityText = text @@ -323,7 +296,7 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { self.accessibilityLabel = accessibilityText self.containerNode.isGestureEnabled = isGestureEnabled - let font: UIFont = isBold ? Font.semibold(17.0) : Font.regular(17.0) + let font: UIFont = isBold ? Font.semibold(17.0) : Font.medium(17.0) self.textNode.attributedText = NSAttributedString(string: text, font: font, textColor: .white) transition.updateTintColor(layer: self.textNode.layer, color: self.contentsColor) @@ -357,70 +330,39 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { textSize = self.textNode.bounds.size } - let inset: CGFloat = 0.0 - var textInset: CGFloat = 0.0 - switch key { - case .back: - textInset += 11.0 - default: - break - } + let textInset: CGFloat = 12.0 let resultSize: CGSize - let textFrame = CGRect(origin: CGPoint(x: inset + textInset, y: floor((height - textSize.height) / 2.0)), size: textSize) + let textFrame = CGRect(origin: CGPoint(x: textInset, y: floor((height - textSize.height) / 2.0)), size: textSize) self.textNode.position = textFrame.center self.textNode.bounds = CGRect(origin: CGPoint(), size: textFrame.size) if let animationNode = self.animationNode { let animationSize = CGSize(width: 30.0, height: 30.0) - animationNode.frame = CGRect(origin: CGPoint(x: inset, y: floor((height - animationSize.height) / 2.0)), size: animationSize).offsetBy(dx: iconOffset.x, dy: iconOffset.y) + animationNode.frame = CGRect(origin: CGPoint(x: floor((height - animationSize.width) * 0.5), y: floor((height - animationSize.height) / 2.0)), size: animationSize).offsetBy(dx: iconOffset.x, dy: iconOffset.y) - let size = CGSize(width: animationSize.width + inset * 2.0, height: height) + let size = CGSize(width: height, height: height) self.containerNode.frame = CGRect(origin: CGPoint(), size: size) self.contextSourceNode.frame = CGRect(origin: CGPoint(), size: size) resultSize = size } else if let image = self.iconNode.image { - let iconFrame = CGRect(origin: CGPoint(x: inset, y: floor((height - image.size.height) / 2.0)), size: image.size).offsetBy(dx: iconOffset.x, dy: iconOffset.y) + let iconFrame = CGRect(origin: CGPoint(x: floor((height - image.size.width) * 0.5), y: floor((height - image.size.height) / 2.0)), size: image.size).offsetBy(dx: iconOffset.x, dy: iconOffset.y) self.iconNode.position = iconFrame.center self.iconNode.bounds = CGRect(origin: CGPoint(), size: iconFrame.size) - if case .back = key { - self.backIconLayer.position = iconFrame.center - self.backIconLayer.bounds = CGRect(origin: CGPoint(), size: iconFrame.size) - - self.iconNode.isHidden = true - self.backIconLayer.isHidden = false - } else { - self.iconNode.isHidden = false - self.backIconLayer.isHidden = true - } - - let size = CGSize(width: image.size.width + inset * 2.0, height: height) + let size = CGSize(width: height, height: height) self.containerNode.frame = CGRect(origin: CGPoint(), size: size) self.contextSourceNode.frame = CGRect(origin: CGPoint(), size: size) resultSize = size } else { - let size = CGSize(width: textSize.width + inset * 2.0, height: height) + let size = CGSize(width: textSize.width + textInset * 2.0, height: height) self.containerNode.frame = CGRect(origin: CGPoint(), size: size) self.contextSourceNode.frame = CGRect(origin: CGPoint(), size: size) resultSize = size } - let diameter: CGFloat = 32.0 - let backgroundWidth: CGFloat - if self.iconNode.image != nil || self.animationNode != nil { - backgroundWidth = diameter - } else { - backgroundWidth = max(diameter, resultSize.width + 12.0 * 2.0) - } - let backgroundFrame = CGRect(origin: CGPoint(x: floor((resultSize.width - backgroundWidth) * 0.5), y: floor((resultSize.height - diameter) * 0.5)), size: CGSize(width: backgroundWidth, height: diameter)) - transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame) - self.backgroundNode.update(size: backgroundFrame.size, cornerRadius: diameter * 0.5, transition: transition) - - self.hitTestSlop = UIEdgeInsets(top: -2.0, left: -12.0, bottom: -2.0, right: -12.0) - return resultSize } } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift index a40ab2c8..07b6d169 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift @@ -4,6 +4,9 @@ import AsyncDisplayKit import ContextUI import TelegramPresentationData import Display +import ComponentFlow +import ComponentDisplayAdapters +import GlassBackgroundComponent enum PeerInfoHeaderNavigationButtonKey { case back @@ -24,101 +27,125 @@ enum PeerInfoHeaderNavigationButtonKey { case postStory } -struct PeerInfoHeaderNavigationButtonSpec: Equatable { +struct PeerInfoHeaderNavigationButtonSpec: Hashable { let key: PeerInfoHeaderNavigationButtonKey let isForExpandedView: Bool } final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode { private var presentationData: PresentationData? - private(set) var leftButtonNodes: [PeerInfoHeaderNavigationButtonKey: PeerInfoHeaderNavigationButton] = [:] - private(set) var rightButtonNodes: [PeerInfoHeaderNavigationButtonKey: PeerInfoHeaderNavigationButton] = [:] + + private let backgroundContainer: GlassBackgroundContainerView + private let leftButtonsBackground: GlassBackgroundView + private let rightButtonsBackground: GlassBackgroundView + private let leftButtonsContainer: UIView + private let rightButtonsContainer: UIView + + private(set) var leftButtonNodes: [PeerInfoHeaderNavigationButtonSpec: PeerInfoHeaderNavigationButton] = [:] + private(set) var rightButtonNodes: [PeerInfoHeaderNavigationButtonSpec: PeerInfoHeaderNavigationButton] = [:] private var currentLeftButtons: [PeerInfoHeaderNavigationButtonSpec] = [] private var currentRightButtons: [PeerInfoHeaderNavigationButtonSpec] = [] private var backgroundContentColor: UIColor = .clear + private var isOverColoredContents: Bool = false private var contentsColor: UIColor = .white - private var canBeExpanded: Bool = false var performAction: ((PeerInfoHeaderNavigationButtonKey, ContextReferenceContentNode?, ContextGesture?) -> Void)? - func updateContentsColor(backgroundContentColor: UIColor, contentsColor: UIColor, canBeExpanded: Bool, transition: ContainedViewLayoutTransition) { - self.backgroundContentColor = backgroundContentColor - self.contentsColor = contentsColor - self.canBeExpanded = canBeExpanded + override init() { + self.backgroundContainer = GlassBackgroundContainerView() + self.leftButtonsBackground = GlassBackgroundView() + self.rightButtonsBackground = GlassBackgroundView() - for (_, button) in self.leftButtonNodes { - button.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, canBeExpanded: canBeExpanded, transition: transition) - transition.updateSublayerTransformOffset(layer: button.layer, offset: CGPoint(x: canBeExpanded ? -8.0 : 0.0, y: 0.0)) + self.leftButtonsContainer = UIView() + self.leftButtonsContainer.clipsToBounds = true + self.rightButtonsContainer = UIView() + self.rightButtonsContainer.clipsToBounds = true + + super.init() + + self.view.addSubview(self.backgroundContainer) + self.backgroundContainer.contentView.addSubview(self.leftButtonsBackground) + self.backgroundContainer.contentView.addSubview(self.rightButtonsBackground) + + self.leftButtonsBackground.contentView.addSubview(self.leftButtonsContainer) + self.rightButtonsBackground.contentView.addSubview(self.rightButtonsContainer) + } + + func updateContentsColor(backgroundContentColor: UIColor, contentsColor: UIColor, isOverColoredContents: Bool, transition: ContainedViewLayoutTransition) { + self.backgroundContentColor = backgroundContentColor + self.isOverColoredContents = isOverColoredContents + self.contentsColor = contentsColor + + guard let presentationData = self.presentationData else { + return } - var accumulatedRightButtonOffset: CGFloat = canBeExpanded ? 16.0 : 0.0 - for spec in self.currentRightButtons.reversed() { - guard let button = self.rightButtonNodes[spec.key] else { + let normalButtonContentsColor: UIColor = self.isOverColoredContents ? .white : presentationData.theme.chat.inputPanel.panelControlColor + let expandedButtonContentsColor: UIColor = presentationData.theme.chat.inputPanel.panelControlColor + + for (spec, button) in self.leftButtonNodes { + button.updateContentsColor(contentsColor: spec.isForExpandedView ? expandedButtonContentsColor : normalButtonContentsColor, transition: transition) + } + + for spec in self.currentRightButtons { + guard let button = self.rightButtonNodes[spec] else { continue } - button.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, canBeExpanded: canBeExpanded, transition: transition) - if !spec.isForExpandedView { - transition.updateSublayerTransformOffset(layer: button.layer, offset: CGPoint(x: accumulatedRightButtonOffset, y: 0.0)) - if self.backgroundContentColor.alpha != 0.0 { - accumulatedRightButtonOffset -= 6.0 - } - } - } - for (key, button) in self.rightButtonNodes { - if !self.currentRightButtons.contains(where: { $0.key == key }) { - button.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, canBeExpanded: canBeExpanded, transition: transition) - transition.updateSublayerTransformOffset(layer: button.layer, offset: CGPoint(x: 0.0, y: 0.0)) + button.updateContentsColor(contentsColor: spec.isForExpandedView ? expandedButtonContentsColor : normalButtonContentsColor, transition: transition) + } + for (spec, button) in self.rightButtonNodes { + if !self.currentRightButtons.contains(where: { $0 == spec }) { + button.updateContentsColor(contentsColor: spec.isForExpandedView ? expandedButtonContentsColor : normalButtonContentsColor, transition: transition) } } + + self.updateBackgroundColors(transition: ComponentTransition(transition)) } func update(size: CGSize, presentationData: PresentationData, leftButtons: [PeerInfoHeaderNavigationButtonSpec], rightButtons: [PeerInfoHeaderNavigationButtonSpec], expandFraction: CGFloat, shouldAnimateIn: Bool, transition: ContainedViewLayoutTransition) { - let sideInset: CGFloat = 24.0 - let expandedSideInset: CGFloat = 16.0 + transition.updateFrame(view: self.backgroundContainer, frame: CGRect(origin: CGPoint(), size: size)) - let maximumExpandOffset: CGFloat = 14.0 - let expandOffset: CGFloat = -expandFraction * maximumExpandOffset + let buttonHeight: CGFloat = 44.0 + + let sideInset: CGFloat = 16.0 + + var normalLeftButtonsWidth: CGFloat = 0.0 + var expandedLeftButtonsWidth: CGFloat = 0.0 + + let maxBlur: CGFloat = 5.0 + + let normalButtonContentsColor: UIColor = self.isOverColoredContents ? .white : presentationData.theme.chat.inputPanel.panelControlColor + let expandedButtonContentsColor: UIColor = presentationData.theme.chat.inputPanel.panelControlColor if self.currentLeftButtons != leftButtons || presentationData.strings !== self.presentationData?.strings { self.currentLeftButtons = leftButtons - var nextRegularButtonOrigin = sideInset - var nextExpandedButtonOrigin = sideInset for spec in leftButtons.reversed() { let buttonNode: PeerInfoHeaderNavigationButton var wasAdded = false - if let current = self.leftButtonNodes[spec.key] { + if let current = self.leftButtonNodes[spec] { buttonNode = current } else { wasAdded = true buttonNode = PeerInfoHeaderNavigationButton() - self.leftButtonNodes[spec.key] = buttonNode - self.addSubnode(buttonNode) + self.leftButtonNodes[spec] = buttonNode + self.leftButtonsContainer.addSubview(buttonNode.view) buttonNode.action = { [weak self] _, gesture in - guard let strongSelf = self, let buttonNode = strongSelf.leftButtonNodes[spec.key] else { + guard let strongSelf = self, let buttonNode = strongSelf.leftButtonNodes[spec] else { return } strongSelf.performAction?(spec.key, buttonNode.contextSourceNode, gesture) } } - let buttonSize = buttonNode.update(key: spec.key, presentationData: presentationData, height: size.height) - var nextButtonOrigin = spec.isForExpandedView ? nextExpandedButtonOrigin : nextRegularButtonOrigin + let buttonSize = buttonNode.update(key: spec.key, presentationData: presentationData, height: buttonHeight) + let buttonFrame = CGRect(origin: CGPoint(x: spec.isForExpandedView ? expandedLeftButtonsWidth : normalLeftButtonsWidth, y: 0.0), size: buttonSize) - let buttonY: CGFloat - if case .back = spec.key { - buttonY = 0.0 - } else { - buttonY = expandOffset + (spec.isForExpandedView ? maximumExpandOffset : 0.0) - } - let buttonFrame = CGRect(origin: CGPoint(x: nextButtonOrigin, y: buttonY), size: buttonSize) - - nextButtonOrigin += buttonSize.width + 4.0 if spec.isForExpandedView { - nextExpandedButtonOrigin = nextButtonOrigin + expandedLeftButtonsWidth += buttonSize.width } else { - nextRegularButtonOrigin = nextButtonOrigin + normalLeftButtonsWidth += buttonSize.width } let alphaFactor: CGFloat if case .back = spec.key { @@ -126,48 +153,40 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode { } else { alphaFactor = spec.isForExpandedView ? expandFraction : (1.0 - expandFraction) } + if wasAdded { buttonNode.frame = buttonFrame buttonNode.alpha = 0.0 + ComponentTransition.immediate.setBlur(layer: buttonNode.layer, radius: maxBlur) transition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor) - buttonNode.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, canBeExpanded: self.canBeExpanded, transition: .immediate) - - transition.updateSublayerTransformOffset(layer: buttonNode.layer, offset: CGPoint(x: canBeExpanded ? -8.0 : 0.0, y: 0.0)) + ComponentTransition(transition).setBlur(layer: buttonNode.layer, radius: 0.0) + buttonNode.updateContentsColor(contentsColor: spec.isForExpandedView ? expandedButtonContentsColor : normalButtonContentsColor, transition: .immediate) } else { transition.updateFrameAdditiveToCenter(node: buttonNode, frame: buttonFrame) transition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor) + ComponentTransition(transition).setBlur(layer: buttonNode.layer, radius: (1.0 - alphaFactor * alphaFactor) * maxBlur) } } - var removeKeys: [PeerInfoHeaderNavigationButtonKey] = [] - for (key, _) in self.leftButtonNodes { - if !leftButtons.contains(where: { $0.key == key }) { - removeKeys.append(key) + var removeKeys: [PeerInfoHeaderNavigationButtonSpec] = [] + for (spec, _) in self.leftButtonNodes { + if !leftButtons.contains(where: { $0 == spec }) { + removeKeys.append(spec) } } - for key in removeKeys { - if let buttonNode = self.leftButtonNodes.removeValue(forKey: key) { - buttonNode.removeFromSupernode() + for spec in removeKeys { + if let buttonNode = self.leftButtonNodes.removeValue(forKey: spec) { + buttonNode.view.removeFromSuperview() } } } else { - var nextRegularButtonOrigin = sideInset - var nextExpandedButtonOrigin = sideInset for spec in leftButtons.reversed() { - if let buttonNode = self.leftButtonNodes[spec.key] { + if let buttonNode = self.leftButtonNodes[spec] { let buttonSize = buttonNode.bounds.size - var nextButtonOrigin = spec.isForExpandedView ? nextExpandedButtonOrigin : nextRegularButtonOrigin - let buttonY: CGFloat - if case .back = spec.key { - buttonY = 0.0 - } else { - buttonY = expandOffset + (spec.isForExpandedView ? maximumExpandOffset : 0.0) - } - let buttonFrame = CGRect(origin: CGPoint(x: nextButtonOrigin, y: buttonY), size: buttonSize) - nextButtonOrigin += buttonSize.width + 4.0 + let buttonFrame = CGRect(origin: CGPoint(x: spec.isForExpandedView ? expandedLeftButtonsWidth : normalLeftButtonsWidth, y: 0.0), size: buttonSize) if spec.isForExpandedView { - nextExpandedButtonOrigin = nextButtonOrigin + expandedLeftButtonsWidth += buttonSize.width } else { - nextRegularButtonOrigin = nextButtonOrigin + normalLeftButtonsWidth += buttonSize.width } transition.updateFrameAdditiveToCenter(node: buttonNode, frame: buttonFrame) let alphaFactor: CGFloat @@ -182,17 +201,18 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode { buttonTransition = .animated(duration: duration * 0.25, curve: curve) } buttonTransition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor) + ComponentTransition(buttonTransition).setBlur(layer: buttonNode.layer, radius: (1.0 - alphaFactor * alphaFactor) * maxBlur) } } } - var accumulatedRightButtonOffset: CGFloat = self.canBeExpanded ? 16.0 : 0.0 + var normalRightButtonsWidth: CGFloat = 0.0 + var expandedRightButtonsWidth: CGFloat = 0.0 + if self.currentRightButtons != rightButtons || presentationData.strings !== self.presentationData?.strings { self.currentRightButtons = rightButtons - var nextRegularButtonOrigin = size.width - sideInset - 8.0 - var nextExpandedButtonOrigin = size.width - expandedSideInset - for spec in rightButtons.reversed() { + for spec in rightButtons { let buttonNode: PeerInfoHeaderNavigationButton var wasAdded = false @@ -201,32 +221,30 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode { key = .moreSearchSort } - if let current = self.rightButtonNodes[key] { + if let current = self.rightButtonNodes[spec] { buttonNode = current } else { wasAdded = true buttonNode = PeerInfoHeaderNavigationButton() - self.rightButtonNodes[key] = buttonNode - self.addSubnode(buttonNode) + self.rightButtonNodes[spec] = buttonNode + self.rightButtonsContainer.addSubview(buttonNode.view) } buttonNode.action = { [weak self] _, gesture in - guard let strongSelf = self, let buttonNode = strongSelf.rightButtonNodes[key] else { + guard let strongSelf = self, let buttonNode = strongSelf.rightButtonNodes[spec] else { return } strongSelf.performAction?(spec.key, buttonNode.contextSourceNode, gesture) } - let buttonSize = buttonNode.update(key: spec.key, presentationData: presentationData, height: size.height) - var nextButtonOrigin = spec.isForExpandedView ? nextExpandedButtonOrigin : nextRegularButtonOrigin - let buttonFrame = CGRect(origin: CGPoint(x: nextButtonOrigin - buttonSize.width, y: expandOffset + (spec.isForExpandedView ? maximumExpandOffset : 0.0)), size: buttonSize) - nextButtonOrigin -= buttonSize.width + 15.0 + let buttonSize = buttonNode.update(key: spec.key, presentationData: presentationData, height: buttonHeight) + let buttonFrame = CGRect(origin: CGPoint(x: spec.isForExpandedView ? expandedRightButtonsWidth : normalRightButtonsWidth, y: 0.0), size: buttonSize) if spec.isForExpandedView { - nextExpandedButtonOrigin = nextButtonOrigin + expandedRightButtonsWidth += buttonSize.width } else { - nextRegularButtonOrigin = nextButtonOrigin + normalRightButtonsWidth += buttonSize.width } let alphaFactor: CGFloat = spec.isForExpandedView ? expandFraction : (1.0 - expandFraction) if wasAdded { - buttonNode.updateContentsColor(backgroundColor: self.backgroundContentColor, contentsColor: self.contentsColor, canBeExpanded: self.canBeExpanded, transition: .immediate) + buttonNode.updateContentsColor(contentsColor: spec.isForExpandedView ? expandedButtonContentsColor : normalButtonContentsColor, transition: .immediate) if shouldAnimateIn { if key == .moreSearchSort || key == .searchWithTags || key == .standaloneSearch { @@ -236,62 +254,51 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode { buttonNode.frame = buttonFrame buttonNode.alpha = 0.0 + ComponentTransition.immediate.setBlur(layer: buttonNode.layer, radius: maxBlur) transition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor) - - if !spec.isForExpandedView { - transition.updateSublayerTransformOffset(layer: buttonNode.layer, offset: CGPoint(x: accumulatedRightButtonOffset, y: 0.0)) - if self.backgroundContentColor.alpha != 0.0 { - accumulatedRightButtonOffset -= 6.0 - } - } else { - transition.updateSublayerTransformOffset(layer: buttonNode.layer, offset: .zero) - } + ComponentTransition(transition).setBlur(layer: buttonNode.layer, radius: (1.0 - alphaFactor * alphaFactor) * maxBlur) } else { transition.updateFrameAdditiveToCenter(node: buttonNode, frame: buttonFrame) transition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor) + ComponentTransition(transition).setBlur(layer: buttonNode.layer, radius: (1.0 - alphaFactor * alphaFactor) * maxBlur) } } - var removeKeys: [PeerInfoHeaderNavigationButtonKey] = [] - for (key, _) in self.rightButtonNodes { - if key == .moreSearchSort { + var removeKeys: [PeerInfoHeaderNavigationButtonSpec] = [] + for (spec, _) in self.rightButtonNodes { + if spec.key == .moreSearchSort { if !rightButtons.contains(where: { $0.key == .more || $0.key == .search || $0.key == .sort }) { - removeKeys.append(key) + removeKeys.append(spec) } - } else if !rightButtons.contains(where: { $0.key == key }) { - removeKeys.append(key) + } else if !rightButtons.contains(where: { $0 == spec }) { + removeKeys.append(spec) } } - for key in removeKeys { - if let buttonNode = self.rightButtonNodes.removeValue(forKey: key) { - if key == .moreSearchSort || key == .searchWithTags || key == .standaloneSearch { + for spec in removeKeys { + if let buttonNode = self.rightButtonNodes.removeValue(forKey: spec) { + if spec.key == .moreSearchSort || spec.key == .searchWithTags || spec.key == .standaloneSearch { buttonNode.layer.animateAlpha(from: buttonNode.alpha, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak buttonNode] _ in - buttonNode?.removeFromSupernode() + buttonNode?.view.removeFromSuperview() }) buttonNode.layer.animateScale(from: 1.0, to: 0.001, duration: 0.2, removeOnCompletion: false) } else { - buttonNode.removeFromSupernode() + buttonNode.view.removeFromSuperview() } } } } else { - var nextRegularButtonOrigin = size.width - sideInset - 8.0 - var nextExpandedButtonOrigin = size.width - expandedSideInset - - for spec in rightButtons.reversed() { + for spec in rightButtons { var key = spec.key if key == .more || key == .search || key == .sort { key = .moreSearchSort } - if let buttonNode = self.rightButtonNodes[key] { + if let buttonNode = self.rightButtonNodes[spec] { let buttonSize = buttonNode.bounds.size - var nextButtonOrigin = spec.isForExpandedView ? nextExpandedButtonOrigin : nextRegularButtonOrigin - let buttonFrame = CGRect(origin: CGPoint(x: nextButtonOrigin - buttonSize.width, y: expandOffset + (spec.isForExpandedView ? maximumExpandOffset : 0.0)), size: buttonSize) - nextButtonOrigin -= buttonSize.width + 15.0 + let buttonFrame = CGRect(origin: CGPoint(x: spec.isForExpandedView ? expandedRightButtonsWidth : normalRightButtonsWidth, y: 0.0), size: buttonSize) if spec.isForExpandedView { - nextExpandedButtonOrigin = nextButtonOrigin + expandedRightButtonsWidth += buttonSize.width } else { - nextRegularButtonOrigin = nextButtonOrigin + normalRightButtonsWidth += buttonSize.width } transition.updateFrameAdditiveToCenter(node: buttonNode, frame: buttonFrame) let alphaFactor: CGFloat = spec.isForExpandedView ? expandFraction : (1.0 - expandFraction) @@ -301,9 +308,60 @@ final class PeerInfoHeaderNavigationButtonContainerNode: SparseNode { buttonTransition = .animated(duration: duration * 0.25, curve: curve) } buttonTransition.updateAlpha(node: buttonNode, alpha: alphaFactor * alphaFactor) + ComponentTransition(transition).setBlur(layer: buttonNode.layer, radius: (1.0 - alphaFactor * alphaFactor) * maxBlur) } } } self.presentationData = presentationData + + let buttonsY: CGFloat = floor((size.height - buttonHeight) * 0.5) + 2.0 + + let leftButtonsWidth = (1.0 - expandFraction) * normalLeftButtonsWidth + expandFraction * expandedLeftButtonsWidth + let rightButtonsWidth = (1.0 - expandFraction) * normalRightButtonsWidth + expandFraction * expandedRightButtonsWidth + + var leftButtonsFrame = CGRect(origin: CGPoint(x: sideInset, y: buttonsY), size: CGSize(width: max(44.0, leftButtonsWidth), height: buttonHeight)) + if leftButtonsWidth < 44.0 { + let leftFraction = leftButtonsWidth / 44.0 + leftButtonsFrame.origin.x = floorToScreenPixels(leftFraction * sideInset + (1.0 - leftFraction) * (-44.0)) + } + var rightButtonsFrame = CGRect(origin: CGPoint(x: size.width - sideInset - rightButtonsWidth, y: buttonsY), size: CGSize(width: max(44.0, rightButtonsWidth), height: buttonHeight)) + if rightButtonsWidth < 44.0 { + let rightFraction = rightButtonsWidth / 44.0 + rightButtonsFrame.origin.x = floorToScreenPixels(rightFraction * (size.width - sideInset - 44.0) + (1.0 - rightFraction) * size.width) + } + + transition.updateFrame(view: self.leftButtonsBackground, frame: leftButtonsFrame) + transition.updateFrame(view: self.leftButtonsContainer, frame: CGRect(origin: CGPoint(), size: leftButtonsFrame.size)) + self.leftButtonsContainer.layer.cornerRadius = leftButtonsFrame.height * 0.5 + + transition.updateFrame(view: self.rightButtonsBackground, frame: rightButtonsFrame) + transition.updateFrame(view: self.rightButtonsContainer, frame: CGRect(origin: CGPoint(), size: rightButtonsFrame.size)) + self.rightButtonsContainer.layer.cornerRadius = rightButtonsFrame.height * 0.5 + + self.updateBackgroundColors(transition: ComponentTransition(transition)) + } + + private func updateBackgroundColors(transition: ComponentTransition) { + guard let presentationData = self.presentationData else { + return + } + + let leftButtonsSize = self.leftButtonsBackground.bounds.size + let rightButtonsSize = self.rightButtonsBackground.bounds.size + + let tintColor: GlassBackgroundView.TintColor + let tintIsDark: Bool + if self.isOverColoredContents { + tintColor = .init(kind: .custom, color: self.backgroundContentColor) + tintIsDark = presentationData.theme.overallDarkAppearance + } else { + tintColor = .init(kind: .panel, color: UIColor(white: presentationData.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)) + tintIsDark = presentationData.theme.overallDarkAppearance + } + + self.backgroundContainer.update(size: self.backgroundContainer.bounds.size, isDark: tintIsDark, transition: transition) + + self.rightButtonsBackground.update(size: rightButtonsSize, cornerRadius: rightButtonsSize.height * 0.5, isDark: tintIsDark, tintColor: tintColor, isInteractive: true, transition: transition) + self.leftButtonsBackground.update(size: leftButtonsSize, cornerRadius: leftButtonsSize.height * 0.5, isDark: tintIsDark, tintColor: tintColor, isInteractive: true, transition: transition) } } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift index 28c5cb6c..d6911343 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift @@ -44,6 +44,7 @@ import ProfileLevelInfoScreen import PlainButtonComponent import BundleIconComponent import MarqueeComponent +import EdgeEffect final class PeerInfoHeaderNavigationTransition { let sourceNavigationBar: NavigationBar @@ -63,7 +64,7 @@ final class PeerInfoHeaderNavigationTransition { } } -final class PeerInfoHeaderRegularContentNode: ASDisplayNode { +final class PeerInfoHeaderRegularContentNode: SparseNode { } enum PeerInfoHeaderTextFieldNodeKey: Equatable { @@ -90,6 +91,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { private var state: PeerInfoState? private var peer: Peer? private var threadData: MessageHistoryThreadData? + private var isSearching: Bool = false private var avatarSize: CGFloat? private let isOpenedFromChat: Bool @@ -151,17 +153,14 @@ final class PeerInfoHeaderNode: ASDisplayNode { let usernameNode: MultiScaleTextNode var actionButtonNodes: [PeerInfoHeaderButtonKey: PeerInfoHeaderActionButtonNode] = [:] var buttonNodes: [PeerInfoHeaderButtonKey: PeerInfoHeaderButtonNode] = [:] - let backgroundNode: NavigationBackgroundNode - let expandedBackgroundNode: NavigationBackgroundNode - let separatorNode: ASDisplayNode - let navigationBackgroundNode: ASDisplayNode - let navigationBackgroundBackgroundNode: ASDisplayNode + let headerEdgeEffectContainer: UIView + let headerEdgeEffectView: EdgeEffectView var navigationTitle: String? - let navigationTitleNode: ImmediateTextNode - let navigationSeparatorNode: ASDisplayNode let navigationButtonContainer: PeerInfoHeaderNavigationButtonContainerNode - let editingNavigationBackgroundNode: NavigationBackgroundNode - let editingNavigationBackgroundSeparator: ASDisplayNode + let searchContainer: ASDisplayNode + var searchEdgeEffectView: EdgeEffectView? + let searchBarContainer: SparseNode + let editingEdgeEffectView: EdgeEffectView var musicBackground: UIView? var music: ComponentView? @@ -172,6 +171,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { var cancelUpload: (() -> Void)? var requestUpdateLayout: ((Bool) -> Void)? var animateOverlaysFadeIn: (() -> Void)? + var updateUnderHeaderContentsAlpha: ((CGFloat, ContainedViewLayoutTransition) -> Void)? var displayAvatarContextMenu: ((ASDisplayNode, ContextGesture?) -> Void)? var displayCopyContextMenu: ((ASDisplayNode, Bool, Bool) -> Void)? @@ -266,6 +266,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { self.backgroundBannerView = UIView() self.backgroundBannerView.clipsToBounds = true self.backgroundBannerView.isUserInteractionEnabled = false + self.backgroundBannerView.layer.allowsGroupOpacity = true self.buttonsContainerNode = SparseNode() self.buttonsContainerNode.clipsToBounds = true @@ -286,30 +287,18 @@ final class PeerInfoHeaderNode: ASDisplayNode { self.avatarOverlayNode = PeerInfoEditingAvatarOverlayNode(context: context) self.avatarOverlayNode.isUserInteractionEnabled = false - self.navigationBackgroundNode = ASDisplayNode() - self.navigationBackgroundNode.isHidden = true - self.navigationBackgroundNode.isUserInteractionEnabled = false - - self.navigationBackgroundBackgroundNode = ASDisplayNode() - self.navigationBackgroundBackgroundNode.isUserInteractionEnabled = false - - self.navigationTitleNode = ImmediateTextNode() - - self.navigationSeparatorNode = ASDisplayNode() - self.navigationButtonContainer = PeerInfoHeaderNavigationButtonContainerNode() - self.editingNavigationBackgroundNode = NavigationBackgroundNode(color: .clear, enableBlur: true) - self.editingNavigationBackgroundSeparator = ASDisplayNode() + self.searchBarContainer = SparseNode() + self.searchContainer = ASDisplayNode() - self.backgroundNode = NavigationBackgroundNode(color: .clear) - self.backgroundNode.isHidden = true - self.backgroundNode.isUserInteractionEnabled = false - self.expandedBackgroundNode = NavigationBackgroundNode(color: .clear) - self.expandedBackgroundNode.isHidden = false - self.expandedBackgroundNode.isUserInteractionEnabled = false + self.headerEdgeEffectView = EdgeEffectView() + self.headerEdgeEffectView.isUserInteractionEnabled = false - self.separatorNode = ASDisplayNode() - self.separatorNode.isLayerBacked = true + self.headerEdgeEffectContainer = UIView() + self.headerEdgeEffectContainer.addSubview(self.headerEdgeEffectView) + + self.editingEdgeEffectView = EdgeEffectView() + self.editingEdgeEffectView.isUserInteractionEnabled = false self.animationCache = context.animationCache self.animationRenderer = context.animationRenderer @@ -320,8 +309,6 @@ final class PeerInfoHeaderNode: ASDisplayNode { self?.requestUpdateLayout?(false) } - self.addSubnode(self.backgroundNode) - self.addSubnode(self.expandedBackgroundNode) self.view.addSubview(self.backgroundBannerView) self.titleNodeContainer.addSubnode(self.titleNode) self.subtitleNodeContainer.addSubnode(self.subtitleNode) @@ -346,14 +333,11 @@ final class PeerInfoHeaderNode: ASDisplayNode { self.addSubnode(self.editingContentNode) self.addSubnode(self.avatarOverlayNode) - self.addSubnode(self.navigationBackgroundNode) - self.navigationBackgroundNode.addSubnode(self.navigationBackgroundBackgroundNode) - self.navigationBackgroundNode.addSubnode(self.navigationTitleNode) - self.navigationBackgroundNode.addSubnode(self.navigationSeparatorNode) - self.addSubnode(self.editingNavigationBackgroundNode) - self.addSubnode(self.editingNavigationBackgroundSeparator) + self.view.addSubview(self.editingEdgeEffectView) self.addSubnode(self.navigationButtonContainer) - self.addSubnode(self.separatorNode) + + self.addSubnode(self.searchContainer) + self.addSubnode(self.searchBarContainer) self.avatarListNode.avatarContainerNode.tapped = { [weak self] in self?.initiateAvatarExpansion(gallery: false, first: false) @@ -509,20 +493,20 @@ final class PeerInfoHeaderNode: ASDisplayNode { private var currentStatusIcon: CredibilityIcon? private var currentPanelStatusData: PeerInfoStatusData? - func update(width: CGFloat, containerHeight: CGFloat, containerInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, isMediaOnly: Bool, contentOffset: CGFloat, paneContainerY: CGFloat, presentationData: PresentationData, peer: Peer?, cachedData: CachedPeerData?, threadData: MessageHistoryThreadData?, peerNotificationSettings: TelegramPeerNotificationSettings?, threadNotificationSettings: TelegramPeerNotificationSettings?, globalNotificationSettings: EngineGlobalNotificationSettings?, statusData: PeerInfoStatusData?, panelStatusData: (PeerInfoStatusData?, PeerInfoStatusData?, CGFloat?), isSecretChat: Bool, isContact: Bool, isSettings: Bool, state: PeerInfoState, profileGiftsContext: ProfileGiftsContext?, screenData: PeerInfoScreenData?, metrics: LayoutMetrics, deviceMetrics: DeviceMetrics, transition: ContainedViewLayoutTransition, additive: Bool, animateHeader: Bool) -> CGFloat { + func update(width: CGFloat, containerHeight: CGFloat, containerInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, isMediaOnly: Bool, contentOffset: CGFloat, paneContainerY: CGFloat, presentationData: PresentationData, peer: Peer?, cachedData: CachedPeerData?, threadData: MessageHistoryThreadData?, peerNotificationSettings: TelegramPeerNotificationSettings?, threadNotificationSettings: TelegramPeerNotificationSettings?, globalNotificationSettings: EngineGlobalNotificationSettings?, statusData: PeerInfoStatusData?, panelStatusData: (PeerInfoStatusData?, PeerInfoStatusData?, CGFloat?), isSecretChat: Bool, isContact: Bool, isSettings: Bool, state: PeerInfoState, profileGiftsContext: ProfileGiftsContext?, screenData: PeerInfoScreenData?, isSearching: Bool, metrics: LayoutMetrics, deviceMetrics: DeviceMetrics, transition: ContainedViewLayoutTransition, additive: Bool, animateHeader: Bool) -> CGFloat { if self.appliedCustomNavigationContentNode !== self.customNavigationContentNode { if let previous = self.appliedCustomNavigationContentNode { - transition.updateAlpha(node: previous, alpha: 0.0, completion: { [weak previous] _ in + ComponentTransition(transition).setAlpha(view: previous.view, alpha: 0.0, completion: { [weak previous] _ in previous?.removeFromSupernode() }) } self.appliedCustomNavigationContentNode = self.customNavigationContentNode if let customNavigationContentNode = self.customNavigationContentNode { - self.addSubnode(customNavigationContentNode) + self.searchBarContainer.addSubnode(customNavigationContentNode) customNavigationContentNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: navigationHeight)) customNavigationContentNode.alpha = 0.0 - transition.updateAlpha(node: customNavigationContentNode, alpha: 1.0) + ComponentTransition(transition).setAlpha(view: customNavigationContentNode.view, alpha: 1.0) } } else if let customNavigationContentNode = self.customNavigationContentNode { transition.updateFrame(node: customNavigationContentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: navigationHeight))) @@ -536,11 +520,15 @@ final class PeerInfoHeaderNode: ASDisplayNode { self.state = state self.peer = peer self.threadData = threadData + self.isSearching = isSearching self.avatarListNode.listContainerNode.peer = peer.flatMap(EnginePeer.init) let isFirstTime = self.validLayout == nil self.validLayout = (width, statusBarHeight, deviceMetrics) + self.searchBarContainer.isUserInteractionEnabled = isSearching + self.searchContainer.isUserInteractionEnabled = isSearching + let previousPanelStatusData = self.currentPanelStatusData self.currentPanelStatusData = panelStatusData.0 @@ -637,7 +625,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { transition.updateAlpha(node: self.regularContentNode, alpha: (state.isEditing || self.customNavigationContentNode != nil) ? 0.0 : 1.0) if self.navigationTransition == nil { - transition.updateAlpha(node: self.navigationButtonContainer, alpha: self.customNavigationContentNode != nil ? 0.0 : 1.0) + transition.updateAlpha(node: self.navigationButtonContainer, alpha: (self.customNavigationContentNode != nil || isSearching) ? 0.0 : 1.0) } self.editingContentNode.alpha = state.isEditing ? 1.0 : 0.0 @@ -648,18 +636,14 @@ final class PeerInfoHeaderNode: ASDisplayNode { let avatarOverlayFarme = self.editingContentNode.convert(self.editingContentNode.avatarNode.frame, to: self) transition.updateFrame(node: self.avatarOverlayNode, frame: avatarOverlayFarme) - var transitionSourceHeight: CGFloat = 0.0 - var transitionFraction: CGFloat = 0.0 - var transitionSourceAvatarFrame: CGRect? - var transitionSourceTitleFrame = CGRect() - var transitionSourceSubtitleFrame = CGRect() + let transitionSourceHeight: CGFloat = 0.0 + let transitionFraction: CGFloat = 0.0 + let transitionSourceAvatarFrame: CGRect? = nil + let transitionSourceTitleFrame = CGRect() + let transitionSourceSubtitleFrame = CGRect() let avatarFrame = CGRect(origin: CGPoint(x: floor((width - avatarSize) / 2.0), y: statusBarHeight + 22.0), size: CGSize(width: avatarSize, height: avatarSize)) - self.backgroundNode.updateColor(color: presentationData.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate) - - let headerBackgroundColor: UIColor = presentationData.theme.list.blocksBackgroundColor - let regularNavigationContentsAccentColor: UIColor = peer?.effectiveProfileColor != nil ? .white : presentationData.theme.list.itemAccentColor let collapsedHeaderNavigationContentsAccentColor = presentationData.theme.list.itemAccentColor let expandedAvatarNavigationContentsAccentColor: UIColor = .white @@ -674,7 +658,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { let regularHeaderButtonBackgroundColor: UIColor let collapsedHeaderButtonBackgroundColor: UIColor = .clear - let expandedAvatarHeaderButtonBackgroundColor: UIColor = UIColor(white: 1.0, alpha: 0.1) + let expandedAvatarHeaderButtonBackgroundColor: UIColor = UIColor(white: 0.0, alpha: 0.5) let regularContentButtonForegroundColor: UIColor = peer?.effectiveProfileColor != nil ? UIColor.white : presentationData.theme.list.itemAccentColor let collapsedHeaderContentButtonForegroundColor = presentationData.theme.list.itemAccentColor @@ -745,61 +729,55 @@ final class PeerInfoHeaderNode: ASDisplayNode { navigationTransition = animateHeader ? .animated(duration: 0.2, curve: .easeInOut) : .immediate } + let editingEdgeEffectHeight: CGFloat = 40.0 + let editingEdgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: navigationHeight + 10.0)) + transition.updateFrame(view: self.editingEdgeEffectView, frame: editingEdgeEffectFrame) + self.editingEdgeEffectView.update(content: presentationData.theme.list.blocksBackgroundColor, blur: true, rect: editingEdgeEffectFrame, edge: .top, edgeSize: editingEdgeEffectHeight, transition: ComponentTransition(transition)) let editingBackgroundAlpha: CGFloat if state.isEditing { editingBackgroundAlpha = max(0.0, min(1.0, contentOffset / 20.0)) } else { editingBackgroundAlpha = 0.0 } + ComponentTransition(transition).setAlpha(view: self.editingEdgeEffectView, alpha: editingBackgroundAlpha) - self.editingNavigationBackgroundSeparator.backgroundColor = presentationData.theme.rootController.navigationBar.separatorColor - self.editingNavigationBackgroundNode.updateColor(color: presentationData.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate) - - let editingNavigationBackgroundFrame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: navigationHeight)) - transition.updateFrame(node: self.editingNavigationBackgroundNode, frame: editingNavigationBackgroundFrame) - self.editingNavigationBackgroundNode.update(size: editingNavigationBackgroundFrame.size, transition: transition) - transition.updateFrame(node: self.editingNavigationBackgroundSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: editingNavigationBackgroundFrame.maxY), size: CGSize(width: width, height: UIScreenPixel))) - - transition.updateAlpha(node: self.editingNavigationBackgroundNode, alpha: editingBackgroundAlpha) - transition.updateAlpha(node: self.editingNavigationBackgroundSeparator, alpha: editingBackgroundAlpha) + if isSearching { + let searchNavigationHeight: CGFloat + if isSettings { + searchNavigationHeight = statusBarHeight + 10.0 + } else { + searchNavigationHeight = navigationHeight + 10.0 + } + + let searchEdgeEffectHeight: CGFloat = 40.0 + let searchEdgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: searchNavigationHeight)) + + let searchEdgeEffectView: EdgeEffectView + var searchEdgeEffectTransition = ComponentTransition(transition) + if let current = self.searchEdgeEffectView { + searchEdgeEffectView = current + } else { + searchEdgeEffectTransition = .immediate + searchEdgeEffectView = EdgeEffectView() + self.searchEdgeEffectView = searchEdgeEffectView + self.searchContainer.view.superview?.insertSubview(searchEdgeEffectView, aboveSubview: self.searchContainer.view) + if transition.isAnimated { + searchEdgeEffectView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + } + + transition.updateFrame(view: searchEdgeEffectView, frame: searchEdgeEffectFrame) + searchEdgeEffectView.update(content: presentationData.theme.list.plainBackgroundColor, blur: true, rect: searchEdgeEffectFrame, edge: .top, edgeSize: searchEdgeEffectHeight, transition: searchEdgeEffectTransition) + } else if let searchEdgeEffectView = self.searchEdgeEffectView { + self.searchEdgeEffectView = nil + transition.updateAlpha(layer: searchEdgeEffectView.layer, alpha: 0.0, completion: { [weak searchEdgeEffectView] _ in + searchEdgeEffectView?.removeFromSuperview() + }) + } let backgroundBannerAlpha: CGFloat - var effectiveSeparatorAlpha: CGFloat - if let navigationTransition = self.navigationTransition { - transitionSourceHeight = navigationTransition.sourceNavigationBar.backgroundNode.bounds.height - transitionFraction = navigationTransition.fraction - - innerBackgroundTransitionFraction = 0.0 - backgroundBannerAlpha = 1.0 - - if let avatarNavigationNode = navigationTransition.sourceNavigationBar.rightButtonNode.singleCustomNode as? ChatAvatarNavigationNode { - if let statusView = avatarNavigationNode.statusView.view { - transitionSourceAvatarFrame = statusView.convert(statusView.bounds, to: navigationTransition.sourceNavigationBar.view) - } else { - transitionSourceAvatarFrame = avatarNavigationNode.avatarNode.view.convert(avatarNavigationNode.avatarNode.view.bounds, to: navigationTransition.sourceNavigationBar.view) - } - transition.updateAlpha(node: self.avatarListNode.avatarContainerNode.avatarNode, alpha: 1.0 - transitionFraction) - } else { - if deviceMetrics.hasDynamicIsland && statusBarHeight > 0.0 && !isLandscape { - transitionSourceAvatarFrame = CGRect(origin: CGPoint(x: avatarFrame.minX, y: -20.0), size: avatarFrame.size).insetBy(dx: avatarSize * 0.4, dy: avatarSize * 0.4) - } else { - transitionSourceAvatarFrame = avatarFrame.offsetBy(dx: 0.0, dy: -avatarFrame.maxY).insetBy(dx: avatarSize * 0.4, dy: avatarSize * 0.4) - } - } - transitionSourceTitleFrame = navigationTransition.sourceTitleFrame - transitionSourceSubtitleFrame = navigationTransition.sourceSubtitleFrame - - transition.updateAlpha(layer: self.backgroundBannerView.layer, alpha: 1.0 - transitionFraction) - - self.expandedBackgroundNode.updateColor(color: presentationData.theme.rootController.navigationBar.blurredBackgroundColor.mixedWith(headerBackgroundColor, alpha: 1.0 - transitionFraction), forceKeepBlur: true, transition: transition) - effectiveSeparatorAlpha = transitionFraction - - if self.isAvatarExpanded, case .animated = transition, transitionFraction == 1.0 { - self.avatarListNode.animateAvatarCollapse(transition: transition) - } - self.avatarClippingNode.clipsToBounds = false - } else { + do { let backgroundTransitionStepDistance: CGFloat = 50.0 var backgroundTransitionDistance: CGFloat = navigationHeight + panelWithAvatarHeight - backgroundTransitionStepDistance if self.isSettings || self.isMyProfile { @@ -812,9 +790,6 @@ final class PeerInfoHeaderNode: ASDisplayNode { innerBackgroundTransitionFraction = max(0.0, min(1.0, contentOffset / backgroundTransitionStepDistance)) } - self.expandedBackgroundNode.updateColor(color: presentationData.theme.rootController.navigationBar.opaqueBackgroundColor.mixedWith(headerBackgroundColor, alpha: 1.0 - innerBackgroundTransitionFraction), forceKeepBlur: true, transition: transition) - navigationTransition.updateAlpha(node: self.expandedBackgroundNode, alpha: state.isEditing ? 0.0 : 1.0) - if state.isEditing { backgroundBannerAlpha = 0.0 } else { @@ -826,8 +801,6 @@ final class PeerInfoHeaderNode: ASDisplayNode { } navigationTransition.updateAlpha(layer: self.backgroundBannerView.layer, alpha: backgroundBannerAlpha) - effectiveSeparatorAlpha = innerBackgroundTransitionFraction - self.avatarClippingNode.clipsToBounds = true } @@ -1161,7 +1134,10 @@ final class PeerInfoHeaderNode: ASDisplayNode { self.titleExpandedVerifiedIconSize = expandedIconSize } - self.navigationButtonContainer.updateContentsColor(backgroundContentColor: headerButtonBackgroundColor, contentsColor: navigationContentsAccentColor, canBeExpanded: navigationContentsCanBeExpanded, transition: navigationTransition) + var actualNavigationContentsColor = navigationContentsAccentColor + actualNavigationContentsColor = presentationData.theme.chat.inputPanel.panelControlColor + + self.navigationButtonContainer.updateContentsColor(backgroundContentColor: headerButtonBackgroundColor, contentsColor: actualNavigationContentsColor, isOverColoredContents: !navigationContentsCanBeExpanded, transition: navigationTransition) self.titleNode.updateTintColor(color: navigationContentsPrimaryColor, transition: navigationTransition) self.subtitleNode.updateTintColor(color: navigationContentsSecondaryColor, transition: navigationTransition) @@ -1183,31 +1159,16 @@ final class PeerInfoHeaderNode: ASDisplayNode { var titleBrightness: CGFloat = 0.0 navigationContentsPrimaryColor.getHue(nil, saturation: nil, brightness: &titleBrightness, alpha: nil) - self.controller?.setStatusBarStyle(titleBrightness > 0.5 ? .White : .Black, animated: !isFirstTime && animateHeader) + if isSearching { + self.controller?.setStatusBarStyle(presentationData.theme.overallDarkAppearance ? .White : .Black, animated: !isFirstTime && animateHeader) + } else { + self.controller?.setStatusBarStyle(titleBrightness > 0.5 ? .White : .Black, animated: !isFirstTime && animateHeader) + } self.avatarListNode.avatarContainerNode.updateTransitionFraction(transitionFraction, transition: transition) self.avatarListNode.listContainerNode.currentItemNode?.updateTransitionFraction(transitionFraction, transition: transition) self.avatarOverlayNode.updateTransitionFraction(transitionFraction, transition: transition) - if self.navigationTitle != presentationData.strings.EditProfile_Title || themeUpdated { - self.navigationTitleNode.attributedText = NSAttributedString(string: presentationData.strings.EditProfile_Title, font: Font.semibold(17.0), textColor: .white) - } - - let navigationTitleSize = self.navigationTitleNode.updateLayout(CGSize(width: width, height: navigationHeight)) - self.navigationTitleNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((width - navigationTitleSize.width) / 2.0), y: navigationHeight - 44.0 + floorToScreenPixels((44.0 - navigationTitleSize.height) / 2.0)), size: navigationTitleSize) - - self.navigationBackgroundNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: navigationHeight)) - self.navigationBackgroundBackgroundNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: navigationHeight)) - self.navigationSeparatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: navigationHeight), size: CGSize(width: width, height: UIScreenPixel)) - self.navigationBackgroundBackgroundNode.backgroundColor = presentationData.theme.rootController.navigationBar.opaqueBackgroundColor - self.navigationSeparatorNode.backgroundColor = presentationData.theme.rootController.navigationBar.separatorColor - - let navigationSeparatorAlpha: CGFloat = 0.0 - transition.updateAlpha(node: self.navigationBackgroundBackgroundNode, alpha: 1.0 - navigationSeparatorAlpha) - transition.updateAlpha(node: self.navigationSeparatorNode, alpha: navigationSeparatorAlpha) - - self.separatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor - let expandedAvatarControlsHeight: CGFloat = 61.0 var expandedAvatarListHeight = min(width, containerHeight - expandedAvatarControlsHeight) if self.isSettings || self.isMyProfile { @@ -1638,7 +1599,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { let singleTitleLockOffset: CGFloat = ((peer?.id == self.context.account.peerId && !self.isMyProfile) || subtitleSize.height.isZero) ? 8.0 : 0.0 - let titleLockOffset: CGFloat = 7.0 + singleTitleLockOffset + let titleLockOffset: CGFloat = 16.0 + singleTitleLockOffset let titleMaxLockOffset: CGFloat = 7.0 let titleOffset: CGFloat let titleCollapseFraction: CGFloat @@ -1697,38 +1658,37 @@ final class PeerInfoHeaderNode: ASDisplayNode { let paneAreaExpansionDistance: CGFloat = 32.0 let effectiveAreaExpansionFraction: CGFloat + let realAreaExpansionFraction: CGFloat if state.isEditing { effectiveAreaExpansionFraction = 0.0 + realAreaExpansionFraction = effectiveAreaExpansionFraction } else if isSettings || isMyProfile { var paneAreaExpansionDelta = (self.frame.maxY - navigationHeight) - contentOffset paneAreaExpansionDelta = max(0.0, min(paneAreaExpansionDelta, paneAreaExpansionDistance)) effectiveAreaExpansionFraction = 1.0 - paneAreaExpansionDelta / paneAreaExpansionDistance + + do { + var paneAreaExpansionDelta = (paneContainerY - navigationHeight) - contentOffset + paneAreaExpansionDelta = max(0.0, min(paneAreaExpansionDelta, paneAreaExpansionDistance)) + realAreaExpansionFraction = 1.0 - paneAreaExpansionDelta / paneAreaExpansionDistance + } } else { var paneAreaExpansionDelta = (paneContainerY - navigationHeight) - contentOffset paneAreaExpansionDelta = max(0.0, min(paneAreaExpansionDelta, paneAreaExpansionDistance)) effectiveAreaExpansionFraction = 1.0 - paneAreaExpansionDelta / paneAreaExpansionDistance + realAreaExpansionFraction = effectiveAreaExpansionFraction } - let secondarySeparatorAlpha = 1.0 - effectiveAreaExpansionFraction - if self.navigationTransition == nil && !self.isSettings && effectiveSeparatorAlpha == 1.0 && secondarySeparatorAlpha < 1.0 { - effectiveSeparatorAlpha = secondarySeparatorAlpha - } - if self.customNavigationContentNode != nil { - effectiveSeparatorAlpha = 0.0 - } - if state.isEditing { - effectiveSeparatorAlpha = 0.0 - } - transition.updateAlpha(node: self.separatorNode, alpha: effectiveSeparatorAlpha) - self.titleNode.update(stateFractions: [ TitleNodeStateRegular: self.isAvatarExpanded ? 0.0 : 1.0, TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0 ], transition: transition) - let subtitleAlpha: CGFloat + transition.updateAlpha(node: self.titleNode, alpha: isSearching ? 0.0 : 1.0) + + var subtitleAlpha: CGFloat var subtitleOffset: CGFloat = 0.0 - let panelSubtitleAlpha: CGFloat + var panelSubtitleAlpha: CGFloat var panelSubtitleOffset: CGFloat = 0.0 if self.isSettings { subtitleAlpha = 1.0 - titleCollapseFraction @@ -1755,6 +1715,12 @@ final class PeerInfoHeaderNode: ASDisplayNode { } } } + + if isSearching { + subtitleAlpha = 0.0 + panelSubtitleAlpha = 0.0 + } + self.subtitleNode.update(stateFractions: [ TitleNodeStateRegular: self.isAvatarExpanded ? 0.0 : 1.0, TitleNodeStateExpanded: self.isAvatarExpanded ? 1.0 : 0.0 @@ -2465,18 +2431,15 @@ final class PeerInfoHeaderNode: ASDisplayNode { } let backgroundFrame: CGRect - let separatorFrame: CGRect var resolvedHeight: CGFloat if state.isEditing { resolvedHeight = editingContentHeight backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: -2000.0 + max(navigationHeight, resolvedHeight - contentOffset)), size: CGSize(width: width, height: 2000.0)) - separatorFrame = CGRect(origin: CGPoint(x: 0.0, y: max(navigationHeight, resolvedHeight - contentOffset)), size: CGSize(width: width, height: UIScreenPixel)) } else { resolvedHeight = resolvedRegularHeight backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: -2000.0 + apparentHeight), size: CGSize(width: width, height: 2000.0)) - separatorFrame = CGRect(origin: CGPoint(x: 0.0, y: apparentHeight), size: CGSize(width: width, height: UIScreenPixel)) } transition.updateFrame(node: self.regularContentNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: resolvedHeight))) @@ -2582,20 +2545,24 @@ final class PeerInfoHeaderNode: ASDisplayNode { } } + let edgeEffectHeight: CGFloat = 60.0 + var edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: -50.0), size: CGSize(width: backgroundFrame.width, height: navigationHeight + 18.0 + 50.0)) + edgeEffectFrame.origin.y += floorToScreenPixels(realAreaExpansionFraction * 50.0) + if additive { - transition.updateFrameAdditive(node: self.backgroundNode, frame: backgroundFrame) - self.backgroundNode.update(size: self.backgroundNode.bounds.size, transition: transition) - transition.updateFrameAdditive(node: self.expandedBackgroundNode, frame: backgroundFrame) - self.expandedBackgroundNode.update(size: self.expandedBackgroundNode.bounds.size, transition: transition) - transition.updateFrameAdditive(node: self.separatorNode, frame: separatorFrame) + transition.updateFrameAdditive(layer: self.headerEdgeEffectView.layer, frame: edgeEffectFrame) } else { - transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame) - self.backgroundNode.update(size: self.backgroundNode.bounds.size, transition: transition) - transition.updateFrame(node: self.expandedBackgroundNode, frame: backgroundFrame) - self.expandedBackgroundNode.update(size: self.expandedBackgroundNode.bounds.size, transition: transition) - transition.updateFrame(node: self.separatorNode, frame: separatorFrame) + transition.updateFrame(view: self.headerEdgeEffectView, frame: edgeEffectFrame) } + if !isSettings { + self.updateUnderHeaderContentsAlpha?(1.0 - realAreaExpansionFraction, transition) + } + + self.headerEdgeEffectView.update(content: presentationData.theme.list.plainBackgroundColor, blur: true, rect: edgeEffectFrame, edge: .top, edgeSize: edgeEffectHeight, transition: ComponentTransition(transition)) + + navigationTransition.updateAlpha(layer: self.headerEdgeEffectView.layer, alpha: state.isEditing ? 0.0 : 1.0) + if !state.isEditing { if !isSettings && !isMyProfile { if self.isAvatarExpanded { @@ -2775,15 +2742,19 @@ final class PeerInfoHeaderNode: ASDisplayNode { guard let result = super.hitTest(point, with: event) else { return nil } - if !self.backgroundNode.frame.contains(point) { - return nil + + if self.isSearching { + if !result.isDescendant(of: self.searchBarContainer.view) && !result.isDescendant(of: self.searchContainer.view) { + return self.view + } + + return result } if let customNavigationContentNode = self.customNavigationContentNode { if let result = customNavigationContentNode.view.hitTest(self.view.convert(point, to: customNavigationContentNode.view), with: event) { return result } - return self.view } let setByFrame = self.avatarListNode.listContainerNode.setByYouNode.view.convert(self.avatarListNode.listContainerNode.setByYouNode.bounds, to: self.view).insetBy(dx: -44.0, dy: 0.0) @@ -2836,7 +2807,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { return result } - if self.isSettings { + if self.isSettings && self.buttonsContainerNode.alpha != 0.0 { if self.subtitleNodeRawContainer.bounds.contains(self.view.convert(point, to: self.subtitleNodeRawContainer.view)) { return self.subtitleNodeRawContainer.view } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoInteraction.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoInteraction.swift new file mode 100644 index 00000000..d92de16e --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoInteraction.swift @@ -0,0 +1,238 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import TelegramCore +import Postbox +import SwiftSignalKit +import AccountContext +import StatisticsUI + +final class PeerInfoInteraction { + let openChat: (EnginePeer.Id?) -> Void + let openUsername: (String, Bool, Promise?) -> Void + let openPhone: (String, ASDisplayNode, ContextGesture?, Promise?) -> Void + let editingOpenNotificationSettings: () -> Void + let editingOpenSoundSettings: () -> Void + let editingToggleShowMessageText: (Bool) -> Void + let requestDeleteContact: () -> Void + let suggestBirthdate: () -> Void + let suggestPhoto: () -> Void + let setCustomPhoto: () -> Void + let resetCustomPhoto: () -> Void + let openAddContact: () -> Void + let updateBlocked: (Bool) -> Void + let openReport: (PeerInfoReportType) -> Void + let openShareBot: () -> Void + let openAddBotToGroup: () -> Void + let performBotCommand: (PeerInfoBotCommand) -> Void + let editingOpenPublicLinkSetup: () -> Void + let editingOpenNameColorSetup: () -> Void + let editingOpenInviteLinksSetup: () -> Void + let editingOpenDiscussionGroupSetup: () -> Void + let editingOpenPostSuggestionsSetup: () -> Void + let editingOpenRevenue: () -> Void + let editingOpenStars: () -> Void + let openParticipantsSection: (PeerInfoParticipantsSection) -> Void + let openRecentActions: () -> Void + let openChannelMessages: () -> Void + let openStats: (ChannelStatsSection) -> Void + let editingOpenPreHistorySetup: () -> Void + let editingOpenAutoremoveMesages: () -> Void + let openPermissions: () -> Void + let openLocation: () -> Void + let editingOpenSetupLocation: () -> Void + let openPeerInfo: (Peer, Bool) -> Void + let performMemberAction: (PeerInfoMember, PeerInfoMemberAction) -> Void + let openPeerInfoContextMenu: (PeerInfoContextSubject, ASDisplayNode, CGRect?) -> Void + let performBioLinkAction: (TextLinkItemActionType, TextLinkItem) -> Void + let requestLayout: (Bool) -> Void + let openEncryptionKey: () -> Void + let openSettings: (PeerInfoSettingsSection) -> Void + let openPaymentMethod: () -> Void + let switchToAccount: (AccountRecordId) -> Void + let logoutAccount: (AccountRecordId) -> Void + let accountContextMenu: (AccountRecordId, ASDisplayNode, ContextGesture?) -> Void + let updateBio: (String) -> Void + let updateNote: (NSAttributedString) -> Void + let openDeletePeer: () -> Void + let openFaq: (String?) -> Void + let openAddMember: () -> Void + let openQrCode: () -> Void + let editingOpenReactionsSetup: () -> Void + let dismissInput: () -> Void + let openForumSettings: () -> Void + let displayTopicsLimited: (TopicsLimitedReason) -> Void + let openPeerMention: (String, ChatControllerInteractionNavigateToPeer) -> Void + let openBotApp: (AttachMenuBot) -> Void + let openEditing: () -> Void + let updateBirthdate: (TelegramBirthday??) -> Void + let updateIsEditingBirthdate: (Bool) -> Void + let openBioPrivacy: () -> Void + let openBirthdatePrivacy: () -> Void + let openPremiumGift: () -> Void + let editingOpenPersonalChannel: () -> Void + let openUsernameContextMenu: (ASDisplayNode, ContextGesture?) -> Void + let openBioContextMenu: (ASDisplayNode, ContextGesture?) -> Void + let openNoteContextMenu: (ASDisplayNode, ContextGesture?) -> Void + let openWorkingHoursContextMenu: (ASDisplayNode, ContextGesture?) -> Void + let openBusinessLocationContextMenu: (ASDisplayNode, ContextGesture?) -> Void + let openBirthdayContextMenu: (ASDisplayNode, ContextGesture?) -> Void + let editingOpenAffiliateProgram: () -> Void + let editingOpenVerifyAccounts: () -> Void + let editingToggleAutoTranslate: (Bool) -> Void + let displayAutoTranslateLocked: () -> Void + let getController: () -> ViewController? + + init( + openUsername: @escaping (String, Bool, Promise?) -> Void, + openPhone: @escaping (String, ASDisplayNode, ContextGesture?, Promise?) -> Void, + editingOpenNotificationSettings: @escaping () -> Void, + editingOpenSoundSettings: @escaping () -> Void, + editingToggleShowMessageText: @escaping (Bool) -> Void, + requestDeleteContact: @escaping () -> Void, + suggestBirthdate: @escaping () -> Void, + suggestPhoto: @escaping () -> Void, + setCustomPhoto: @escaping () -> Void, + resetCustomPhoto: @escaping () -> Void, + openChat: @escaping (EnginePeer.Id?) -> Void, + openAddContact: @escaping () -> Void, + updateBlocked: @escaping (Bool) -> Void, + openReport: @escaping (PeerInfoReportType) -> Void, + openShareBot: @escaping () -> Void, + openAddBotToGroup: @escaping () -> Void, + performBotCommand: @escaping (PeerInfoBotCommand) -> Void, + editingOpenPublicLinkSetup: @escaping () -> Void, + editingOpenNameColorSetup: @escaping () -> Void, + editingOpenInviteLinksSetup: @escaping () -> Void, + editingOpenDiscussionGroupSetup: @escaping () -> Void, + editingOpenPostSuggestionsSetup: @escaping () -> Void, + editingOpenRevenue: @escaping () -> Void, + editingOpenStars: @escaping () -> Void, + openParticipantsSection: @escaping (PeerInfoParticipantsSection) -> Void, + openRecentActions: @escaping () -> Void, + openChannelMessages: @escaping () -> Void, + openStats: @escaping (ChannelStatsSection) -> Void, + editingOpenPreHistorySetup: @escaping () -> Void, + editingOpenAutoremoveMesages: @escaping () -> Void, + openPermissions: @escaping () -> Void, + openLocation: @escaping () -> Void, + editingOpenSetupLocation: @escaping () -> Void, + openPeerInfo: @escaping (Peer, Bool) -> Void, + performMemberAction: @escaping (PeerInfoMember, PeerInfoMemberAction) -> Void, + openPeerInfoContextMenu: @escaping (PeerInfoContextSubject, ASDisplayNode, CGRect?) -> Void, + performBioLinkAction: @escaping (TextLinkItemActionType, TextLinkItem) -> Void, + requestLayout: @escaping (Bool) -> Void, + openEncryptionKey: @escaping () -> Void, + openSettings: @escaping (PeerInfoSettingsSection) -> Void, + openPaymentMethod: @escaping () -> Void, + switchToAccount: @escaping (AccountRecordId) -> Void, + logoutAccount: @escaping (AccountRecordId) -> Void, + accountContextMenu: @escaping (AccountRecordId, ASDisplayNode, ContextGesture?) -> Void, + updateBio: @escaping (String) -> Void, + updateNote: @escaping (NSAttributedString) -> Void, + openDeletePeer: @escaping () -> Void, + openFaq: @escaping (String?) -> Void, + openAddMember: @escaping () -> Void, + openQrCode: @escaping () -> Void, + editingOpenReactionsSetup: @escaping () -> Void, + dismissInput: @escaping () -> Void, + openForumSettings: @escaping () -> Void, + displayTopicsLimited: @escaping (TopicsLimitedReason) -> Void, + openPeerMention: @escaping (String, ChatControllerInteractionNavigateToPeer) -> Void, + openBotApp: @escaping (AttachMenuBot) -> Void, + openEditing: @escaping () -> Void, + updateBirthdate: @escaping (TelegramBirthday??) -> Void, + updateIsEditingBirthdate: @escaping (Bool) -> Void, + openBioPrivacy: @escaping () -> Void, + openBirthdatePrivacy: @escaping () -> Void, + openPremiumGift: @escaping () -> Void, + editingOpenPersonalChannel: @escaping () -> Void, + openUsernameContextMenu: @escaping (ASDisplayNode, ContextGesture?) -> Void, + openBioContextMenu: @escaping (ASDisplayNode, ContextGesture?) -> Void, + openNoteContextMenu: @escaping (ASDisplayNode, ContextGesture?) -> Void, + openWorkingHoursContextMenu: @escaping (ASDisplayNode, ContextGesture?) -> Void, + openBusinessLocationContextMenu: @escaping (ASDisplayNode, ContextGesture?) -> Void, + openBirthdayContextMenu: @escaping (ASDisplayNode, ContextGesture?) -> Void, + editingOpenAffiliateProgram: @escaping () -> Void, + editingOpenVerifyAccounts: @escaping () -> Void, + editingToggleAutoTranslate: @escaping (Bool) -> Void, + displayAutoTranslateLocked: @escaping () -> Void, + getController: @escaping () -> ViewController? + ) { + self.openUsername = openUsername + self.openPhone = openPhone + self.editingOpenNotificationSettings = editingOpenNotificationSettings + self.editingOpenSoundSettings = editingOpenSoundSettings + self.editingToggleShowMessageText = editingToggleShowMessageText + self.requestDeleteContact = requestDeleteContact + self.suggestBirthdate = suggestBirthdate + self.suggestPhoto = suggestPhoto + self.setCustomPhoto = setCustomPhoto + self.resetCustomPhoto = resetCustomPhoto + self.openChat = openChat + self.openAddContact = openAddContact + self.updateBlocked = updateBlocked + self.openReport = openReport + self.openShareBot = openShareBot + self.openAddBotToGroup = openAddBotToGroup + self.performBotCommand = performBotCommand + self.editingOpenPublicLinkSetup = editingOpenPublicLinkSetup + self.editingOpenNameColorSetup = editingOpenNameColorSetup + self.editingOpenInviteLinksSetup = editingOpenInviteLinksSetup + self.editingOpenDiscussionGroupSetup = editingOpenDiscussionGroupSetup + self.editingOpenPostSuggestionsSetup = editingOpenPostSuggestionsSetup + self.editingOpenRevenue = editingOpenRevenue + self.editingOpenStars = editingOpenStars + self.openParticipantsSection = openParticipantsSection + self.openRecentActions = openRecentActions + self.openChannelMessages = openChannelMessages + self.openStats = openStats + self.editingOpenPreHistorySetup = editingOpenPreHistorySetup + self.editingOpenAutoremoveMesages = editingOpenAutoremoveMesages + self.openPermissions = openPermissions + self.openLocation = openLocation + self.editingOpenSetupLocation = editingOpenSetupLocation + self.openPeerInfo = openPeerInfo + self.performMemberAction = performMemberAction + self.openPeerInfoContextMenu = openPeerInfoContextMenu + self.performBioLinkAction = performBioLinkAction + self.requestLayout = requestLayout + self.openEncryptionKey = openEncryptionKey + self.openSettings = openSettings + self.openPaymentMethod = openPaymentMethod + self.switchToAccount = switchToAccount + self.logoutAccount = logoutAccount + self.accountContextMenu = accountContextMenu + self.updateBio = updateBio + self.updateNote = updateNote + self.openDeletePeer = openDeletePeer + self.openFaq = openFaq + self.openAddMember = openAddMember + self.openQrCode = openQrCode + self.editingOpenReactionsSetup = editingOpenReactionsSetup + self.dismissInput = dismissInput + self.openForumSettings = openForumSettings + self.displayTopicsLimited = displayTopicsLimited + self.openPeerMention = openPeerMention + self.openBotApp = openBotApp + self.openEditing = openEditing + self.updateBirthdate = updateBirthdate + self.updateIsEditingBirthdate = updateIsEditingBirthdate + self.openBioPrivacy = openBioPrivacy + self.openBirthdatePrivacy = openBirthdatePrivacy + self.openPremiumGift = openPremiumGift + self.editingOpenPersonalChannel = editingOpenPersonalChannel + self.openUsernameContextMenu = openUsernameContextMenu + self.openBioContextMenu = openBioContextMenu + self.openNoteContextMenu = openNoteContextMenu + self.openWorkingHoursContextMenu = openWorkingHoursContextMenu + self.openBusinessLocationContextMenu = openBusinessLocationContextMenu + self.openBirthdayContextMenu = openBirthdayContextMenu + self.editingOpenAffiliateProgram = editingOpenAffiliateProgram + self.editingOpenVerifyAccounts = editingOpenVerifyAccounts + self.editingToggleAutoTranslate = editingToggleAutoTranslate + self.displayAutoTranslateLocked = displayAutoTranslateLocked + self.getController = getController + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift index 47d81e1b..152ed248 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoPaneContainerNode.swift @@ -16,10 +16,14 @@ import PeerInfoChatPaneNode import TextFormat import EmojiTextAttachmentView import ComponentFlow +import ComponentDisplayAdapters import TabSelectorComponent import MultilineTextComponent import BottomButtonPanelComponent import UndoUI +import HorizontalTabsComponent +import GlassBackgroundComponent +import EdgeEffect final class PeerInfoPaneWrapper { let key: PeerInfoPaneKey @@ -44,8 +48,6 @@ final class PeerInfoPaneWrapper { } private final class GiftsTabItemComponent: Component { - typealias EnvironmentType = TabSelectorComponent.ItemEnvironment - let context: AccountContext let icons: [ProfileGiftsContext.State.StarGift] let title: String @@ -83,22 +85,19 @@ private final class GiftsTabItemComponent: Component { private var component: GiftsTabItemComponent? - func update(component: GiftsTabItemComponent, availableSize: CGSize, state: State, environment: Environment, transition: ComponentTransition) -> CGSize { + func update(component: GiftsTabItemComponent, availableSize: CGSize, state: State, environment: Environment, transition: ComponentTransition) -> CGSize { self.component = component - let environment = environment[EnvironmentType.self].value - let textSpacing: CGFloat = 2.0 let iconSpacing: CGFloat = 1.0 - let normalColor = component.theme.list.itemSecondaryTextColor - let selectedColor = component.theme.list.itemAccentColor - let effectiveColor = normalColor.mixedWith(selectedColor, alpha: environment.selectionFraction) + let normalColor = component.theme.chat.inputPanel.panelControlColor + let effectiveColor = normalColor let titleSize = self.title.update( transition: .immediate, component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: component.title, font: Font.medium(14.0), textColor: effectiveColor)) + text: .plain(NSAttributedString(string: component.title, font: Font.medium(15.0), textColor: effectiveColor)) )), environment: {}, containerSize: CGSize(width: availableSize.width, height: 100.0) @@ -399,292 +398,6 @@ private func interpolateFrame(from fromValue: CGRect, to toValue: CGRect, t: CGF return CGRect(x: floorToScreenPixels(toValue.origin.x * t + fromValue.origin.x * (1.0 - t)), y: floorToScreenPixels(toValue.origin.y * t + fromValue.origin.y * (1.0 - t)), width: floorToScreenPixels(toValue.size.width * t + fromValue.size.width * (1.0 - t)), height: floorToScreenPixels(toValue.size.height * t + fromValue.size.height * (1.0 - t))) } -final class PeerInfoPaneTabsContainerNode: ASDisplayNode { - private let context: AccountContext - private let scrollNode: ASScrollNode - private var paneNodes: [PeerInfoPaneKey: PeerInfoPaneTabsContainerPaneNode] = [:] - private let selectedLineNode: ASImageNode - - private var currentParams: ([PeerInfoPaneSpecifier], PeerInfoPaneKey?, Bool, PresentationData)? - - var requestSelectPane: ((PeerInfoPaneKey) -> Void)? - - init(context: AccountContext) { - self.context = context - self.scrollNode = ASScrollNode() - - self.selectedLineNode = ASImageNode() - self.selectedLineNode.displaysAsynchronously = false - self.selectedLineNode.displayWithoutProcessing = true - - super.init() - - self.scrollNode.view.disablesInteractiveTransitionGestureRecognizerNow = { [weak self] in - guard let strongSelf = self else { - return false - } - return strongSelf.scrollNode.view.contentOffset.x > .ulpOfOne - } - self.scrollNode.view.showsHorizontalScrollIndicator = false - self.scrollNode.view.scrollsToTop = false - if #available(iOS 11.0, *) { - self.scrollNode.view.contentInsetAdjustmentBehavior = .never - } - - self.addSubnode(self.scrollNode) - self.scrollNode.addSubnode(self.selectedLineNode) - } - - func update(size: CGSize, presentationData: PresentationData, paneList: [PeerInfoPaneSpecifier], selectedPane: PeerInfoPaneKey?, disableSwitching: Bool, transitionFraction: CGFloat, transition: ContainedViewLayoutTransition) { - transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: size)) - - let focusOnSelectedPane = self.currentParams?.1 != selectedPane - - if self.currentParams?.3.theme !== presentationData.theme { - self.selectedLineNode.image = generateImage(CGSize(width: 7.0, height: 4.0), rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - context.setFillColor(presentationData.theme.list.itemAccentColor.cgColor) - context.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.width))) - })?.stretchableImage(withLeftCapWidth: 4, topCapHeight: 1) - } - - if self.currentParams?.0 != paneList || self.currentParams?.1 != selectedPane || self.currentParams?.3 !== presentationData { - for specifier in paneList { - let paneNode: PeerInfoPaneTabsContainerPaneNode - if let current = self.paneNodes[specifier.key] { - paneNode = current - } else { - paneNode = PeerInfoPaneTabsContainerPaneNode(pressed: { [weak self] in - self?.paneSelected(specifier.key) - }) - self.paneNodes[specifier.key] = paneNode - } - paneNode.updateText(context: self.context, title: specifier.title, icons: specifier.icons, isSelected: selectedPane == specifier.key, presentationData: presentationData) - } - var removeKeys: [PeerInfoPaneKey] = [] - for (key, _) in self.paneNodes { - if !paneList.contains(where: { $0.key == key }) { - removeKeys.append(key) - } - } - for key in removeKeys { - if let paneNode = self.paneNodes.removeValue(forKey: key) { - paneNode.removeFromSupernode() - } - } - } - self.currentParams = (paneList, selectedPane, disableSwitching, presentationData) - - var tabSizes: [(PeerInfoPaneKey, CGSize, PeerInfoPaneTabsContainerPaneNode, Bool)] = [] - var totalRawTabSize: CGFloat = 0.0 - var selectionFrames: [CGRect] = [] - - for specifier in paneList { - guard let paneNode = self.paneNodes[specifier.key] else { - continue - } - let wasAdded = paneNode.supernode == nil - if wasAdded { - self.scrollNode.addSubnode(paneNode) - } - let paneNodeWidth = paneNode.updateLayout(height: size.height) - let paneNodeSize = CGSize(width: paneNodeWidth, height: size.height) - tabSizes.append((specifier.key, paneNodeSize, paneNode, wasAdded)) - totalRawTabSize += paneNodeSize.width - } - - let minSpacing: CGFloat = 26.0 - if tabSizes.count <= 1 { - for i in 0 ..< tabSizes.count { - let (paneKey, paneNodeSize, paneNode, wasAdded) = tabSizes[i] - let leftOffset: CGFloat = 16.0 - - let paneFrame = CGRect(origin: CGPoint(x: leftOffset, y: floor((size.height - paneNodeSize.height) / 2.0)), size: paneNodeSize) - - let paneAlpha: CGFloat - if disableSwitching { - paneAlpha = paneKey == selectedPane ? 1.0 : 0.5 - } else { - paneAlpha = 1.0 - } - - if wasAdded { - paneNode.frame = paneFrame - paneNode.alpha = 0.0 - } else { - transition.updateFrameAdditiveToCenter(node: paneNode, frame: paneFrame) - } - transition.updateAlpha(node: paneNode, alpha: paneAlpha) - - let areaSideInset: CGFloat = 16.0 - paneNode.updateArea(size: paneFrame.size, sideInset: areaSideInset) - paneNode.hitTestSlop = UIEdgeInsets(top: 0.0, left: -areaSideInset, bottom: 0.0, right: -areaSideInset) - - selectionFrames.append(paneFrame) - } - self.scrollNode.view.contentSize = CGSize(width: size.width, height: size.height) - } else if totalRawTabSize + CGFloat(tabSizes.count + 1) * minSpacing <= size.width { - let availableSpace = size.width - let availableSpacing = availableSpace - totalRawTabSize - let perTabSpacing = floor(availableSpacing / CGFloat(tabSizes.count + 1)) - - let normalizedPerTabWidth = floor(availableSpace / CGFloat(tabSizes.count)) - var maxSpacing: CGFloat = 0.0 - var minSpacing: CGFloat = .greatestFiniteMagnitude - for i in 0 ..< tabSizes.count - 1 { - let distanceToNextBoundary = (normalizedPerTabWidth - tabSizes[i].1.width) / 2.0 - let nextDistanceToBoundary = (normalizedPerTabWidth - tabSizes[i + 1].1.width) / 2.0 - let distance = nextDistanceToBoundary + distanceToNextBoundary - maxSpacing = max(distance, maxSpacing) - minSpacing = min(distance, minSpacing) - } - - if minSpacing >= 100.0 || (maxSpacing / minSpacing) < 0.2 { - for i in 0 ..< tabSizes.count { - let (paneKey, paneNodeSize, paneNode, wasAdded) = tabSizes[i] - - let paneFrame = CGRect(origin: CGPoint(x: CGFloat(i) * normalizedPerTabWidth + floor((normalizedPerTabWidth - paneNodeSize.width) / 2.0), y: floor((size.height - paneNodeSize.height) / 2.0)), size: paneNodeSize) - - let paneAlpha: CGFloat - if disableSwitching { - paneAlpha = paneKey == selectedPane ? 1.0 : 0.5 - } else { - paneAlpha = 1.0 - } - - if wasAdded { - paneNode.frame = paneFrame - paneNode.alpha = 0.0 - } else { - transition.updateFrameAdditiveToCenter(node: paneNode, frame: paneFrame) - } - - transition.updateAlpha(node: paneNode, alpha: paneAlpha) - - let areaSideInset = floor((normalizedPerTabWidth - paneNodeSize.width) / 2.0) - paneNode.updateArea(size: paneFrame.size, sideInset: areaSideInset) - paneNode.hitTestSlop = UIEdgeInsets(top: 0.0, left: -areaSideInset, bottom: 0.0, right: -areaSideInset) - - selectionFrames.append(paneFrame) - } - } else { - var leftOffset = perTabSpacing - for i in 0 ..< tabSizes.count { - let (paneKey, paneNodeSize, paneNode, wasAdded) = tabSizes[i] - - let paneFrame = CGRect(origin: CGPoint(x: leftOffset, y: floor((size.height - paneNodeSize.height) / 2.0)), size: paneNodeSize) - - let paneAlpha: CGFloat - if disableSwitching { - paneAlpha = paneKey == selectedPane ? 1.0 : 0.5 - } else { - paneAlpha = 1.0 - } - - if wasAdded { - paneNode.frame = paneFrame - paneNode.alpha = 0.0 - } else { - transition.updateFrameAdditiveToCenter(node: paneNode, frame: paneFrame) - } - - transition.updateAlpha(node: paneNode, alpha: paneAlpha) - - let areaSideInset = floor(perTabSpacing / 2.0) - paneNode.updateArea(size: paneFrame.size, sideInset: areaSideInset) - paneNode.hitTestSlop = UIEdgeInsets(top: 0.0, left: -areaSideInset, bottom: 0.0, right: -areaSideInset) - - leftOffset += paneNodeSize.width + perTabSpacing - - selectionFrames.append(paneFrame) - } - } - self.scrollNode.view.contentSize = CGSize(width: size.width, height: size.height) - } else { - let sideInset: CGFloat = 16.0 - var leftOffset: CGFloat = sideInset - for i in 0 ..< tabSizes.count { - let (paneKey, paneNodeSize, paneNode, wasAdded) = tabSizes[i] - let paneFrame = CGRect(origin: CGPoint(x: leftOffset, y: floor((size.height - paneNodeSize.height) / 2.0)), size: paneNodeSize) - - let paneAlpha: CGFloat - if disableSwitching { - paneAlpha = paneKey == selectedPane ? 1.0 : 0.5 - } else { - paneAlpha = 1.0 - } - - if wasAdded { - paneNode.frame = paneFrame - paneNode.alpha = 0.0 - } else { - transition.updateFrameAdditiveToCenter(node: paneNode, frame: paneFrame) - } - - transition.updateAlpha(node: paneNode, alpha: paneAlpha) - - paneNode.updateArea(size: paneFrame.size, sideInset: minSpacing) - paneNode.hitTestSlop = UIEdgeInsets(top: 0.0, left: -minSpacing, bottom: 0.0, right: -minSpacing) - - selectionFrames.append(paneFrame) - - leftOffset += paneNodeSize.width + minSpacing - } - self.scrollNode.view.contentSize = CGSize(width: leftOffset - minSpacing + sideInset, height: size.height) - } - - var selectedFrame: CGRect? - if let selectedPane = selectedPane, let currentIndex = paneList.firstIndex(where: { $0.key == selectedPane }) { - if currentIndex != 0 && transitionFraction > 0.0 { - let currentFrame = selectionFrames[currentIndex] - let previousFrame = selectionFrames[currentIndex - 1] - selectedFrame = interpolateFrame(from: currentFrame, to: previousFrame, t: abs(transitionFraction)) - } else if currentIndex != paneList.count - 1 && transitionFraction < 0.0 { - let currentFrame = selectionFrames[currentIndex] - let previousFrame = selectionFrames[currentIndex + 1] - selectedFrame = interpolateFrame(from: currentFrame, to: previousFrame, t: abs(transitionFraction)) - } else { - selectedFrame = selectionFrames[currentIndex] - } - } - - if let selectedFrame = selectedFrame { - let wasAdded = self.selectedLineNode.isHidden - self.selectedLineNode.isHidden = false - let lineFrame = CGRect(origin: CGPoint(x: selectedFrame.minX, y: size.height - 4.0), size: CGSize(width: selectedFrame.width, height: 4.0)) - if wasAdded { - self.selectedLineNode.frame = lineFrame - self.selectedLineNode.alpha = 0.0 - transition.updateAlpha(node: self.selectedLineNode, alpha: 1.0) - } else { - transition.updateFrame(node: self.selectedLineNode, frame: lineFrame) - } - if focusOnSelectedPane { - if selectedPane == paneList.first?.key { - transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(), size: self.scrollNode.bounds.size)) - } else if selectedPane == paneList.last?.key { - transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(x: max(0.0, self.scrollNode.view.contentSize.width - self.scrollNode.bounds.width), y: 0.0), size: self.scrollNode.bounds.size)) - } else { - let contentOffsetX = max(0.0, min(self.scrollNode.view.contentSize.width - self.scrollNode.bounds.width, floor(selectedFrame.midX - self.scrollNode.bounds.width / 2.0))) - transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(x: contentOffsetX, y: 0.0), size: self.scrollNode.bounds.size)) - } - } - } else { - self.selectedLineNode.isHidden = true - } - } - - private func paneSelected(_ key: PeerInfoPaneKey) { - guard let currentParams = self.currentParams else { - return - } - if currentParams.2 { - return - } - self.requestSelectPane?(key) - } -} - private final class PeerInfoPendingPane { let pane: PeerInfoPaneWrapper private var disposable: Disposable? @@ -874,11 +587,11 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat weak var parentController: ViewController? - private let coveringBackgroundNode: NavigationBackgroundNode - private let additionalBackgroundNode: ASDisplayNode - private let separatorNode: ASDisplayNode + let headerContainer: UIView + + private let tabsBackgroundContainer: GlassBackgroundContainerView + private let tabsBackgroundView: GlassBackgroundView private let tabsContainer = ComponentView() - private let tabsSeparatorNode: ASDisplayNode private var didJustReorderTabs = false private var actionPanel: ComponentView? @@ -886,7 +599,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat let isReady = Promise() var didSetIsReady = false - private var currentParams: (size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, expansionFraction: CGFloat, presentationData: PresentationData, data: PeerInfoScreenData?, areTabsHidden: Bool, disableTabSwitching: Bool, navigationHeight: CGFloat)? + private var currentParams: (size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, topInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, expansionFraction: CGFloat, presentationData: PresentationData, data: PeerInfoScreenData?, areTabsHidden: Bool, disableTabSwitching: Bool, navigationHeight: CGFloat)? private(set) var currentPaneKey: PeerInfoPaneKey? var pendingSwitchToPaneKey: PeerInfoPaneKey? @@ -914,6 +627,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat private var initialStoryFolderId: Int64? private var initialGiftCollectionId: Int64? + private var isDraggingTabs: Bool = false private var transitionFraction: CGFloat = 0.0 var selectionPanelNode: PeerInfoSelectionPanelNode? @@ -951,22 +665,15 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat self.initialStoryFolderId = initialStoryFolderId self.initialGiftCollectionId = initialGiftCollectionId - self.additionalBackgroundNode = ASDisplayNode() + self.tabsBackgroundContainer = GlassBackgroundContainerView() + self.tabsBackgroundView = GlassBackgroundView() - self.separatorNode = ASDisplayNode() - self.separatorNode.isLayerBacked = true - - self.coveringBackgroundNode = NavigationBackgroundNode(color: .clear) - self.coveringBackgroundNode.isUserInteractionEnabled = false - - self.tabsSeparatorNode = ASDisplayNode() + self.headerContainer = SparseContainerView() super.init() -// self.addSubnode(self.separatorNode) - self.addSubnode(self.additionalBackgroundNode) - self.addSubnode(self.coveringBackgroundNode) - self.addSubnode(self.tabsSeparatorNode) + self.tabsBackgroundContainer.contentView.addSubview(self.tabsBackgroundView) + self.headerContainer.addSubview(self.tabsBackgroundContainer) } override func didLoad() { @@ -1031,6 +738,8 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat @objc private func panGesture(_ recognizer: UIPanGestureRecognizer) { switch recognizer.state { case .began: + self.isDraggingTabs = true + func cancelContextGestures(view: UIView) { if let gestureRecognizers = view.gestureRecognizers { for gesture in gestureRecognizers { @@ -1046,7 +755,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat cancelContextGestures(view: self.view) case .changed: - if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden, disableTabSwitching, navigationHeight) = self.currentParams, let availablePanes = data?.availablePanes, availablePanes.count > 1, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) { + if let (size, sideInset, topInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden, disableTabSwitching, navigationHeight) = self.currentParams, let availablePanes = data?.availablePanes, availablePanes.count > 1, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) { let translation = recognizer.translation(in: self.view) var transitionFraction = translation.x / size.width if currentIndex <= 0 { @@ -1061,11 +770,13 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat // print(transitionFraction) self.paneTransitionPromise.set(transitionFraction) - self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, areTabsHidden: areTabsHidden, disableTabSwitching: disableTabSwitching, navigationHeight: navigationHeight, transition: .immediate) + self.update(size: size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, areTabsHidden: areTabsHidden, disableTabSwitching: disableTabSwitching, navigationHeight: navigationHeight, transition: .immediate) self.currentPaneUpdated?(false) } case .cancelled, .ended: - if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden, disableTabSwitching, navigationHeight) = self.currentParams, let availablePanes = data?.availablePanes, availablePanes.count > 1, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) { + self.isDraggingTabs = false + + if let (size, sideInset, topInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden, disableTabSwitching, navigationHeight) = self.currentParams, let availablePanes = data?.availablePanes, availablePanes.count > 1, let currentPaneKey = self.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey) { let translation = recognizer.translation(in: self.view) let velocity = recognizer.velocity(in: self.view) var directionIsToRight: Bool? @@ -1089,7 +800,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat } } self.transitionFraction = 0.0 - self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, areTabsHidden: areTabsHidden, disableTabSwitching: disableTabSwitching, navigationHeight: navigationHeight, transition: .animated(duration: 0.35, curve: .spring)) + self.update(size: size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, areTabsHidden: areTabsHidden, disableTabSwitching: disableTabSwitching, navigationHeight: navigationHeight, transition: .animated(duration: 0.35, curve: .spring)) self.currentPaneUpdated?(false) self.currentPaneStatusPromise.set(self.currentPane?.node.status ?? .single(nil)) @@ -1158,8 +869,8 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat } } - func openTabContextMenu(key: PeerInfoPaneKey, sourceNode: ASDisplayNode, gesture: ContextGesture?) { - guard let params = self.currentParams, let sourceNode = sourceNode as? ContextExtractedContentContainingNode else { + func openTabContextMenu(key: PeerInfoPaneKey, sourceView: UIView, gesture: ContextGesture?) { + guard let params = self.currentParams, let sourceView = sourceView as? ContextExtractedContentContainingView else { return } @@ -1190,7 +901,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat let contextController = ContextController( presentationData: params.presentationData, - source: .extracted(TabsExtractedContentSource(sourceNode: sourceNode)), + source: .reference(TabsReferenceContentSource(sourceView: sourceView)), items: .single(ContextController.Items(content: .list(items))), recognizer: nil, gesture: gesture @@ -1198,7 +909,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat self.parentController?.presentInGlobalOverlay(contextController) } - func update(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, expansionFraction: CGFloat, presentationData: PresentationData, data: PeerInfoScreenData?, areTabsHidden: Bool, disableTabSwitching: Bool, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) { + func update(size: CGSize, sideInset: CGFloat, topInset: CGFloat, bottomInset: CGFloat, deviceMetrics: DeviceMetrics, visibleHeight: CGFloat, expansionFraction: CGFloat, presentationData: PresentationData, data: PeerInfoScreenData?, areTabsHidden: Bool, disableTabSwitching: Bool, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) { let previousAvailablePanes = self.currentAvailablePanes let availablePanes = data?.availablePanes ?? [] self.currentAvailablePanes = data?.availablePanes @@ -1242,24 +953,23 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat currentIndex = nil } - self.currentParams = (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden, disableTabSwitching, navigationHeight) + self.currentParams = (size, sideInset, topInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden, disableTabSwitching, navigationHeight) - transition.updateAlpha(node: self.coveringBackgroundNode, alpha: expansionFraction) + let backgroundColor: UIColor + if self.currentPaneKey == .gifts { + backgroundColor = presentationData.theme.list.blocksBackgroundColor + } else { + backgroundColor = presentationData.theme.list.blocksBackgroundColor.mixedWith(presentationData.theme.list.plainBackgroundColor, alpha: expansionFraction) + } -// transition.updateAlpha(node: self.additionalBackgroundNode, alpha: 1.0 - expansionFraction) - - self.backgroundColor = presentationData.theme.list.plainBackgroundColor - self.coveringBackgroundNode.updateColor(color: presentationData.theme.rootController.navigationBar.opaqueBackgroundColor, transition: .immediate) - self.separatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor - self.additionalBackgroundNode.backgroundColor = presentationData.theme.list.itemBlocksBackgroundColor - self.tabsSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor + self.backgroundColor = backgroundColor let isScrollingLockedAtTop = expansionFraction < 1.0 - CGFloat.ulpOfOne - let tabsHeight: CGFloat = 48.0 - let effectiveTabsHeight: CGFloat = areTabsHidden ? 0.0 : tabsHeight + let tabsHeight: CGFloat = 40.0 + let effectiveTabsHeight: CGFloat = areTabsHidden ? 0.0 : (10.0 + tabsHeight + 10.0 + 6.0) - let paneFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)) + let paneFrame = CGRect(origin: CGPoint(x: 0.0, y: -topInset), size: CGSize(width: size.width, height: topInset + size.height)) var visiblePaneIndices: [Int] = [] var requiredPendingKeys: [PeerInfoPaneKey] = [] @@ -1330,12 +1040,12 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat guard let strongSelf = self else { return } - if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden, disableTabSwitching, navigationHeight) = strongSelf.currentParams { + if let (size, sideInset, topInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden, disableTabSwitching, navigationHeight) = strongSelf.currentParams { var transition: ContainedViewLayoutTransition = .immediate if strongSelf.pendingSwitchToPaneKey == key && strongSelf.currentPaneKey != nil { transition = .animated(duration: 0.4, curve: .spring) } - strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, areTabsHidden: areTabsHidden, disableTabSwitching: disableTabSwitching, navigationHeight: navigationHeight, transition: transition) + strongSelf.update(size: size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, areTabsHidden: areTabsHidden, disableTabSwitching: disableTabSwitching, navigationHeight: navigationHeight, transition: transition) } } if leftScope { @@ -1376,14 +1086,14 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat ) self.pendingPanes[key] = pane pane.pane.node.frame = paneFrame - pane.pane.update(size: paneFrame.size, topInset: effectiveTabsHeight, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expansionFraction, navigationHeight: navigationHeight, presentationData: presentationData, synchronous: true, transition: .immediate) + pane.pane.update(size: paneFrame.size, topInset: topInset + effectiveTabsHeight, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expansionFraction, navigationHeight: navigationHeight, presentationData: presentationData, synchronous: true, transition: .immediate) let paneNode = pane.pane.node pane.pane.node.tabBarOffsetUpdated = { [weak self, weak paneNode] transition in guard let strongSelf = self, let paneNode = paneNode, let currentPane = strongSelf.currentPane, paneNode === currentPane.node else { return } - if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden, disableTabSwitching, navigationHeight) = strongSelf.currentParams { - strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, areTabsHidden: areTabsHidden, disableTabSwitching: disableTabSwitching, navigationHeight: navigationHeight, transition: transition) + if let (size, sideInset, topInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden, disableTabSwitching, navigationHeight) = strongSelf.currentParams { + strongSelf.update(size: size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, areTabsHidden: areTabsHidden, disableTabSwitching: disableTabSwitching, navigationHeight: navigationHeight, transition: transition) } } leftScope = true @@ -1392,7 +1102,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat for (key, pane) in self.pendingPanes { pane.pane.node.frame = paneFrame - pane.pane.update(size: paneFrame.size, topInset: effectiveTabsHeight, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expansionFraction, navigationHeight: navigationHeight, presentationData: presentationData, synchronous: self.currentPaneKey == nil, transition: .immediate) + pane.pane.update(size: paneFrame.size, topInset: effectiveTabsHeight + topInset, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expansionFraction, navigationHeight: navigationHeight, presentationData: presentationData, synchronous: self.currentPaneKey == nil, transition: .immediate) if pane.isReady { self.pendingPanes.removeValue(forKey: key) @@ -1445,7 +1155,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat if let index = availablePanes.firstIndex(of: key), let updatedCurrentIndex = updatedCurrentIndex { var paneWasAdded = false if pane.node.supernode == nil { - self.insertSubnode(pane.node, belowSubnode: self.coveringBackgroundNode) + self.insertSubnode(pane.node, at: 0) paneWasAdded = true } let indexOffset = CGFloat(index - updatedCurrentIndex) @@ -1458,7 +1168,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat return } pane.isAnimatingOut = false - if let (_, _, _, _, _, _, _, data, _, _, _) = strongSelf.currentParams { + if let (_, _, _, _, _, _, _, _, data, _, _, _) = strongSelf.currentParams { if let availablePanes = data?.availablePanes, let currentPaneKey = strongSelf.currentPaneKey, let currentIndex = availablePanes.firstIndex(of: currentPaneKey), let paneIndex = availablePanes.firstIndex(of: key), abs(paneIndex - currentIndex) <= 1 { } else { if let pane = strongSelf.currentPanes.removeValue(forKey: key) { @@ -1484,21 +1194,23 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat } else { let isAnimatingOut = pane.isAnimatingOut pane.isAnimatingOut = true - paneTransition.updateFrame(node: pane.node, frame: adjustedFrame, completion: isAnimatingOut ? nil : { _ in + paneTransition.updateFrame(node: pane.node, frame: adjustedFrame, completion: isAnimatingOut ? nil : { _ in paneCompletion() }) } - pane.update(size: paneFrame.size, topInset: effectiveTabsHeight, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expansionFraction, navigationHeight: navigationHeight, presentationData: presentationData, synchronous: paneWasAdded, transition: paneTransition) + pane.update(size: paneFrame.size, topInset: effectiveTabsHeight + topInset, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expansionFraction, navigationHeight: navigationHeight, presentationData: presentationData, synchronous: paneWasAdded, transition: paneTransition) } } var tabsOffset: CGFloat = 0.0 - if let currentPane = self.currentPane { - tabsOffset = currentPane.node.tabBarOffset - } - tabsOffset = max(0.0, min(tabsHeight, tabsOffset)) - if isScrollingLockedAtTop || self.isMediaOnly { - tabsOffset = 0.0 + if !"".isEmpty { + if let currentPane = self.currentPane { + tabsOffset = currentPane.node.tabBarOffset + } + tabsOffset = max(0.0, min(tabsHeight, tabsOffset)) + if isScrollingLockedAtTop || self.isMediaOnly { + tabsOffset = 0.0 + } } var tabsAlpha: CGFloat @@ -1510,13 +1222,6 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat } tabsAlpha *= tabsAlpha - transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel - tabsOffset), size: CGSize(width: size.width, height: UIScreenPixel))) - transition.updateFrame(node: self.coveringBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel - tabsOffset), size: CGSize(width: size.width, height: tabsHeight + UIScreenPixel))) - transition.updateFrame(node: self.additionalBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel - tabsOffset), size: CGSize(width: size.width, height: tabsHeight + UIScreenPixel))) - self.coveringBackgroundNode.update(size: self.coveringBackgroundNode.bounds.size, transition: transition) - - transition.updateFrame(node: self.tabsSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: tabsHeight - tabsOffset), size: CGSize(width: size.width, height: UIScreenPixel))) - var canManageTabs = false if let peer = data?.peer { if peer.id == self.context.account.peerId { @@ -1528,130 +1233,145 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat } } - let items: [TabSelectorComponent.Item] = availablePanes.map { key in - let content: TabSelectorComponent.Item.Content - var canReorder = false - switch key { - case .stories: - content = .text(presentationData.strings.PeerInfo_PaneStories) - canReorder = true - case .storyArchive: - content = .text(presentationData.strings.PeerInfo_PaneArchivedStories) - case .botPreview: - content = .text(presentationData.strings.PeerInfo_PaneBotPreviews) - case .media: - content = .text(presentationData.strings.PeerInfo_PaneMedia) - canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel - case .files: - content = .text(presentationData.strings.PeerInfo_PaneFiles) - canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel - case .links: - content = .text(presentationData.strings.PeerInfo_PaneLinks) - canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel - case .voice: - content = .text(presentationData.strings.PeerInfo_PaneVoiceAndVideo) - canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel - case .gifs: - content = .text(presentationData.strings.PeerInfo_PaneGifs) - canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel - case .music: - content = .text(presentationData.strings.PeerInfo_PaneAudio) - canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel - case .groupsInCommon: - content = .text(presentationData.strings.PeerInfo_PaneGroups) - case .members: - content = .text(presentationData.strings.PeerInfo_PaneMembers) - case .similarChannels: - content = .text(presentationData.strings.PeerInfo_PaneRecommended) - case .similarBots: - content = .text(presentationData.strings.PeerInfo_PaneRecommendedBots) - case .savedMessagesChats: - content = .text(presentationData.strings.DialogList_TabTitle) - case .savedMessages: - content = .text(presentationData.strings.PeerInfo_SavedMessagesTabTitle) - case .gifts: - var icons: [ProfileGiftsContext.State.StarGift] = [] - if let gifts = data?.profileGiftsContext?.currentState?.gifts.prefix(3) { - icons = Array(gifts) - } - content = .component(AnyComponent( - GiftsTabItemComponent(context: self.context, icons: icons, title: presentationData.strings.PeerInfo_PaneGifts, theme: presentationData.theme) - )) - canReorder = true - } - return TabSelectorComponent.Item(id: key, content: content, isReorderable: false, contextAction: key != availablePanes.first && canManageTabs && canReorder ? { [weak self] node, gesture in - self?.openTabContextMenu(key: key, sourceNode: node, gesture: gesture) - } : nil) - } + let tabsSideInset: CGFloat = sideInset + 16.0 - let tabsContainerSize = CGSize(width: size.width - sideInset * 2.0, height: tabsHeight) + let tabsContainerSize = CGSize(width: size.width - tabsSideInset * 2.0, height: tabsHeight) let tabsContainerEffectiveSize = self.tabsContainer.update( transition: ComponentTransition(transition), - component: AnyComponent(TabSelectorComponent( - colors: TabSelectorComponent.Colors( - foreground: presentationData.theme.list.itemSecondaryTextColor, - selection: presentationData.theme.list.itemAccentColor - ), + component: AnyComponent(HorizontalTabsComponent( + context: self.context, theme: presentationData.theme, - customLayout: TabSelectorComponent.CustomLayout( - font: Font.medium(14.0), - spacing: 6.0, - fillWidth: true, - lineSelection: true - ), - items: items, - selectedId: self.currentPaneKey, - setSelectedId: { [weak self] id in - guard let strongSelf = self, let key = id.base as? PeerInfoPaneKey else { - return - } - if strongSelf.currentPaneKey == key { - if let requestExpandTabs = strongSelf.requestExpandTabs, requestExpandTabs() { - } else { - let _ = strongSelf.currentPane?.node.scrollToTop() - } - return - } - if strongSelf.currentPanes[key] != nil { - strongSelf.currentPaneKey = key - - if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden, disableTabSwitching, navigationHeight) = strongSelf.currentParams { - strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, areTabsHidden: areTabsHidden, disableTabSwitching: disableTabSwitching, navigationHeight: navigationHeight, transition: .animated(duration: 0.4, curve: .spring)) - - strongSelf.currentPaneUpdated?(true) - - strongSelf.currentPaneStatusPromise.set(strongSelf.currentPane?.node.status ?? .single(nil)) - strongSelf.nextPaneStatusPromise.set(.single(nil)) - strongSelf.paneTransitionPromise.set(nil) - } - } else if strongSelf.pendingSwitchToPaneKey != key { - strongSelf.pendingSwitchToPaneKey = key - strongSelf.expandOnSwitch = true - - if let (size, sideInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden, disableTabSwitching, navigationHeight) = strongSelf.currentParams { - strongSelf.update(size: size, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, areTabsHidden: areTabsHidden, disableTabSwitching: disableTabSwitching, navigationHeight: navigationHeight, transition: .animated(duration: 0.4, curve: .spring)) + tabs: availablePanes.map { paneKey -> HorizontalTabsComponent.Tab in + var canReorder = false + let content: HorizontalTabsComponent.Tab.Content + switch paneKey { + case .stories: + content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_PaneStories, entities: [], enableAnimations: false)) + canReorder = true + case .storyArchive: + content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_PaneArchivedStories, entities: [], enableAnimations: false)) + case .botPreview: + content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_PaneBotPreviews, entities: [], enableAnimations: false)) + case .media: + content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_PaneMedia, entities: [], enableAnimations: false)) + canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel + case .files: + content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_PaneFiles, entities: [], enableAnimations: false)) + canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel + case .links: + content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_PaneLinks, entities: [], enableAnimations: false)) + canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel + case .voice: + content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_PaneVoiceAndVideo, entities: [], enableAnimations: false)) + canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel + case .gifs: + content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_PaneGifs, entities: [], enableAnimations: false)) + canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel + case .music: + content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_PaneAudio, entities: [], enableAnimations: false)) + canReorder = self.peerId.namespace == Namespaces.Peer.CloudChannel + case .groupsInCommon: + content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_PaneGroups, entities: [], enableAnimations: false)) + case .members: + content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_PaneMembers, entities: [], enableAnimations: false)) + case .similarChannels: + content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_PaneRecommended, entities: [], enableAnimations: false)) + case .similarBots: + content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_PaneRecommendedBots, entities: [], enableAnimations: false)) + case .savedMessagesChats: + content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.DialogList_TabTitle, entities: [], enableAnimations: false)) + case .savedMessages: + content = .title(HorizontalTabsComponent.Tab.Title(text: presentationData.strings.PeerInfo_SavedMessagesTabTitle, entities: [], enableAnimations: false)) + case .gifts: + var icons: [ProfileGiftsContext.State.StarGift] = [] + if let gifts = data?.profileGiftsContext?.currentState?.gifts.prefix(3) { + icons = Array(gifts) } + content = .custom(AnyComponent( + GiftsTabItemComponent(context: self.context, icons: icons, title: presentationData.strings.PeerInfo_PaneGifts, theme: presentationData.theme) + )) + canReorder = true } + + return HorizontalTabsComponent.Tab( + id: AnyHashable(paneKey), + content: content, + badge: nil, + action: { [weak self] in + guard let self else { + return + } + if self.currentPaneKey == paneKey { + if let requestExpandTabs = self.requestExpandTabs, requestExpandTabs() { + } else { + let _ = self.currentPane?.node.scrollToTop() + } + return + } + if self.currentPanes[paneKey] != nil { + self.currentPaneKey = paneKey + + if let (size, sideInset, topInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden, disableTabSwitching, navigationHeight) = self.currentParams { + self.update(size: size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, areTabsHidden: areTabsHidden, disableTabSwitching: disableTabSwitching, navigationHeight: navigationHeight, transition: .animated(duration: 0.4, curve: .spring)) + + self.currentPaneUpdated?(true) + + self.currentPaneStatusPromise.set(self.currentPane?.node.status ?? .single(nil)) + self.nextPaneStatusPromise.set(.single(nil)) + self.paneTransitionPromise.set(nil) + } + } else if self.pendingSwitchToPaneKey != paneKey { + self.pendingSwitchToPaneKey = paneKey + self.expandOnSwitch = true + + if let (size, sideInset, topInset, bottomInset, deviceMetrics, visibleHeight, expansionFraction, presentationData, data, areTabsHidden, disableTabSwitching, navigationHeight) = self.currentParams { + self.update(size: size, sideInset: sideInset, topInset: topInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, expansionFraction: expansionFraction, presentationData: presentationData, data: data, areTabsHidden: areTabsHidden, disableTabSwitching: disableTabSwitching, navigationHeight: navigationHeight, transition: .animated(duration: 0.4, curve: .spring)) + } + } + }, + contextAction: paneKey != availablePanes.first && canManageTabs && canReorder ? { [weak self] sourceView, gesture in + guard let self else { + return + } + self.openTabContextMenu(key: paneKey, sourceView: sourceView, gesture: gesture) + } : nil, + deleteAction: nil + ) }, - transitionFraction: -self.transitionFraction + selectedTab: self.currentPaneKey.flatMap { HorizontalTabsComponent.Tab.Id($0) }, + isEditing: false, + layout: .fit, + liftWhileSwitching: deviceMetrics.type != .tablet )), environment: {}, containerSize: tabsContainerSize ) - let tabContainerFrameOriginX = items.count == 1 ? sideInset : floorToScreenPixels((size.width - tabsContainerEffectiveSize.width) / 2.0) - let tabContainerFrame = CGRect(origin: CGPoint(x: tabContainerFrameOriginX, y: 10.0 - tabsOffset), size: tabsContainerSize) - if let tabsContainerView = self.tabsContainer.view { + + let tabContainerFrameOriginX = floorToScreenPixels((size.width - tabsContainerEffectiveSize.width) / 2.0) + let tabContainerFrame = CGRect(origin: CGPoint(x: tabContainerFrameOriginX, y: 10.0), size: tabsContainerEffectiveSize) + + transition.updateFrame(view: self.tabsBackgroundContainer, frame: tabContainerFrame) + self.tabsBackgroundContainer.update(size: tabContainerFrame.size, isDark: presentationData.theme.overallDarkAppearance, transition: ComponentTransition(transition)) + + transition.updateFrame(view: self.tabsBackgroundView, frame: CGRect(origin: CGPoint(), size: tabContainerFrame.size)) + self.tabsBackgroundView.update(size: tabContainerFrame.size, cornerRadius: tabContainerFrame.height * 0.5, isDark: presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: presentationData.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), transition: ComponentTransition(transition)) + + ComponentTransition(transition).setAlpha(view: self.tabsBackgroundContainer, alpha: tabsAlpha) + + if let tabsContainerView = self.tabsContainer.view as? HorizontalTabsComponent.View { if tabsContainerView.superview == nil { - self.view.insertSubview(tabsContainerView, belowSubview: self.tabsSeparatorNode.view) + self.tabsBackgroundView.contentView.addSubview(tabsContainerView) + tabsContainerView.setOverlayContainerView(overlayContainerView: self.headerContainer) } - transition.updateFrame(view: tabsContainerView, frame: tabContainerFrame) - transition.updateAlpha(layer: tabsContainerView.layer, alpha: tabsAlpha) + transition.updateFrame(view: tabsContainerView, frame: CGRect(origin: CGPoint(), size: tabContainerFrame.size)) + + tabsContainerView.updateTabSwitchFraction(fraction: self.transitionFraction, isDragging: self.isDraggingTabs, transition: ComponentTransition(transition)) } for (_, pane) in self.pendingPanes { let paneTransition: ContainedViewLayoutTransition = .immediate paneTransition.updateFrame(node: pane.pane.node, frame: paneFrame) - pane.pane.update(size: paneFrame.size, topInset: effectiveTabsHeight, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expansionFraction, navigationHeight: navigationHeight, presentationData: presentationData, synchronous: true, transition: paneTransition) + pane.pane.update(size: paneFrame.size, topInset: effectiveTabsHeight + topInset, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expansionFraction, navigationHeight: navigationHeight, presentationData: presentationData, synchronous: true, transition: paneTransition) } var removeKeys: [PeerInfoPaneKey] = [] @@ -1717,3 +1437,22 @@ private final class TabsExtractedContentSource: ContextExtractedContentSource { return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds) } } + +private final class TabsReferenceContentSource: ContextReferenceContentSource { + let keepInPlace: Bool = true + let actionsHorizontalAlignment: ContextActionsHorizontalAlignment = .center + + private let sourceView: ContextExtractedContentContainingView + + init(sourceView: ContextExtractedContentContainingView) { + self.sourceView = sourceView + } + + func transitionInfo() -> ContextControllerReferenceViewInfo? { + return ContextControllerReferenceViewInfo( + referenceView: self.sourceView.contentView, + contentAreaInScreenSpace: UIScreen.main.bounds, + actionsPosition: .bottom + ) + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoProfileItems.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoProfileItems.swift new file mode 100644 index 00000000..abde525b --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoProfileItems.swift @@ -0,0 +1,1730 @@ +import Foundation +import UIKit +import Display +import AccountContext +import TelegramPresentationData +import TelegramCore +import Postbox +import PeerInfoUI +import TextFormat +import PhoneNumberFormat +import SwiftSignalKit +import TelegramStringFormatting +import AsyncDisplayKit +import LocationResources +import AttachmentUI +import WebUI +import AvatarNode +import PeerNameColorItem +import BoostLevelIconComponent + +private let enabledPublicBioEntities: EnabledEntityTypes = [.allUrl, .mention, .hashtag] +private let enabledPrivateBioEntities: EnabledEntityTypes = [.internalUrl, .mention, .hashtag] + +enum InfoSection: Int, CaseIterable { + case groupLocation + case calls + case personalChannel + case peerInfo + case balances + case permissions + case peerInfoTrailing + case peerSettings + case peerMembers + case channelMonoforum + case botAffiliateProgram +} + +func infoItems(data: PeerInfoScreenData?, context: AccountContext, presentationData: PresentationData, interaction: PeerInfoInteraction, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], chatLocation: ChatLocation, isOpenedFromChat: Bool, isMyProfile: Bool) -> [(AnyHashable, [PeerInfoScreenItem])] { + guard let data = data else { + return [] + } + + var currentPeerInfoSection: InfoSection = .peerInfo + + var items: [InfoSection: [PeerInfoScreenItem]] = [:] + for section in InfoSection.allCases { + items[section] = [] + } + + let bioContextAction: (ASDisplayNode, ContextGesture?, CGPoint?) -> Void = { node, gesture, _ in + interaction.openBioContextMenu(node, gesture) + } + let noteContextAction: (ASDisplayNode, ContextGesture?, CGPoint?) -> Void = { node, gesture, _ in + interaction.openNoteContextMenu(node, gesture) + } + let bioLinkAction: (TextLinkItemActionType, TextLinkItem, ASDisplayNode, CGRect?, Promise?) -> Void = { action, item, _, _, _ in + interaction.performBioLinkAction(action, item) + } + let workingHoursContextAction: (ASDisplayNode, ContextGesture?, CGPoint?) -> Void = { node, gesture, _ in + interaction.openWorkingHoursContextMenu(node, gesture) + } + let businessLocationContextAction: (ASDisplayNode, ContextGesture?, CGPoint?) -> Void = { node, gesture, _ in + interaction.openBusinessLocationContextMenu(node, gesture) + } + let birthdayContextAction: (ASDisplayNode, ContextGesture?, CGPoint?) -> Void = { node, gesture, _ in + interaction.openBirthdayContextMenu(node, gesture) + } + + if let user = data.peer as? TelegramUser { + let ItemCallList = 1000 + let ItemPersonalChannelHeader = 2000 + let ItemPersonalChannel = 2001 + let ItemPhoneNumber = 3000 + let ItemUsername = 3001 + let ItemBirthdate = 3002 + let ItemAbout = 3003 + let ItemNote = 3004 + let ItemAppFooter = 3005 + let ItemAffiliate = 4000 + let ItemAffiliateInfo = 4001 + let ItemBusinessHours = 5000 + let ItemLocation = 5001 + let ItemSendMessage = 6000 + let ItemReport = 6001 + let ItemAddToContacts = 6002 + let ItemBlock = 6003 + let ItemEncryptionKey = 6004 + let ItemBalanceHeader = 7000 + let ItemBalanceTon = 7001 + let ItemBalanceStars = 7002 + let ItemBotPermissionsHeader = 8000 + let ItemBotPermissionsEmojiStatus = 8001 + let ItemBotPermissionsLocation = 8002 + let ItemBotPermissionsBiometry = 8003 + let ItemBotSettings = 9000 + let ItemBotReport = 9001 + let ItemBotAddToChat = 9002 + let ItemBotAddToChatInfo = 9003 + let ItemVerification = 9004 + + if !callMessages.isEmpty { + items[.calls]!.append(PeerInfoScreenCallListItem(id: ItemCallList, messages: callMessages)) + } + + if let personalChannel = data.personalChannel { + let peerId = personalChannel.peer.peerId + var label: String? + if let subscriberCount = personalChannel.subscriberCount { + label = presentationData.strings.Conversation_StatusSubscribers(Int32(subscriberCount)) + } + items[.personalChannel]?.append(PeerInfoScreenHeaderItem(id: ItemPersonalChannelHeader, text: presentationData.strings.Profile_PersonalChannelSectionTitle, label: label)) + items[.personalChannel]?.append(PeerInfoScreenPersonalChannelItem(id: ItemPersonalChannel, context: context, data: personalChannel, controller: { [weak interaction] in + guard let interaction else { + return nil + } + return interaction.getController() + }, action: { [weak interaction] in + guard let interaction else { + return + } + interaction.openChat(peerId) + })) + } + + if let phone = user.phone { + let formattedPhone = formatPhoneNumber(context: context, number: phone) + let label: String + if formattedPhone.hasPrefix("+888 ") { + label = presentationData.strings.UserInfo_AnonymousNumberLabel + } else { + label = presentationData.strings.ContactInfo_PhoneLabelMobile + } + items[currentPeerInfoSection]!.append(PeerInfoScreenLabeledValueItem(id: ItemPhoneNumber, label: label, text: formattedPhone, textColor: .accent, action: { node, progress in + interaction.openPhone(phone, node, nil, progress) + }, longTapAction: nil, contextAction: { node, gesture, _ in + interaction.openPhone(phone, node, gesture, nil) + }, requestLayout: { animated in + interaction.requestLayout(animated) + })) + } + if let mainUsername = user.addressName { + var additionalUsernames: String? + let usernames = user.usernames.filter { $0.isActive && $0.username != mainUsername } + if !usernames.isEmpty { + additionalUsernames = presentationData.strings.Profile_AdditionalUsernames(String(usernames.map { "@\($0.username)" }.joined(separator: ", "))).string + } + + items[currentPeerInfoSection]!.append( + PeerInfoScreenLabeledValueItem( + id: ItemUsername, + label: presentationData.strings.Profile_Username, + text: "@\(mainUsername)", + additionalText: additionalUsernames, + textColor: .accent, + icon: .qrCode, + action: { _, progress in + interaction.openUsername(mainUsername, true, progress) + }, linkItemAction: { type, item, _, _, progress in + if case .tap = type { + if case let .mention(username) = item { + interaction.openUsername(String(username[username.index(username.startIndex, offsetBy: 1)...]), false, progress) + } + } + }, iconAction: { + interaction.openQrCode() + }, contextAction: { node, gesture, _ in + interaction.openUsernameContextMenu(node, gesture) + }, requestLayout: { animated in + interaction.requestLayout(animated) + } + ) + ) + } + + if let cachedData = data.cachedData as? CachedUserData { + if let birthday = cachedData.birthday { + var hasBirthdayToday = false + let today = Calendar.current.dateComponents(Set([.day, .month]), from: Date()) + if today.day == Int(birthday.day) && today.month == Int(birthday.month) { + hasBirthdayToday = true + } + + var birthdayAction: ((ASDisplayNode, Promise?) -> Void)? + if isMyProfile { + birthdayAction = { node, _ in + birthdayContextAction(node, nil, nil) + } + } else if hasBirthdayToday && cachedData.disallowedGifts != TelegramDisallowedGifts.All { + birthdayAction = { _, _ in + interaction.openPremiumGift() + } + } + + items[currentPeerInfoSection]!.append(PeerInfoScreenLabeledValueItem(id: ItemBirthdate, context: context, label: hasBirthdayToday ? presentationData.strings.UserInfo_BirthdayToday : presentationData.strings.UserInfo_Birthday, text: stringForCompactBirthday(birthday, strings: presentationData.strings, showAge: true), textColor: .primary, leftIcon: hasBirthdayToday ? .birthday : nil, icon: hasBirthdayToday ? .premiumGift : nil, action: birthdayAction, longTapAction: nil, iconAction: { + interaction.openPremiumGift() + }, contextAction: birthdayContextAction, requestLayout: { _ in + })) + } + + var hasAbout = false + if let about = cachedData.about, !about.isEmpty { + hasAbout = true + } + var hasNote = false + if let note = cachedData.note, !note.text.isEmpty { + hasNote = true + } + + var hasWebApp = false + if let botInfo = user.botInfo, botInfo.flags.contains(.hasWebApp) { + hasWebApp = true + } + + if user.isFake { + items[currentPeerInfoSection]!.append(PeerInfoScreenLabeledValueItem(id: ItemAbout, label: "", text: user.botInfo != nil ? presentationData.strings.UserInfo_FakeBotWarning : presentationData.strings.UserInfo_FakeUserWarning, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: user.botInfo != nil ? enabledPrivateBioEntities : []), action: nil, requestLayout: { animated in + interaction.requestLayout(animated) + })) + } else if user.isScam { + items[currentPeerInfoSection]!.append(PeerInfoScreenLabeledValueItem(id: ItemAbout, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: user.botInfo != nil ? presentationData.strings.UserInfo_ScamBotWarning : presentationData.strings.UserInfo_ScamUserWarning, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: user.botInfo != nil ? enabledPrivateBioEntities : []), action: nil, requestLayout: { animated in + interaction.requestLayout(animated) + })) + } else if hasAbout || hasNote || hasWebApp { + var actionButton: PeerInfoScreenLabeledValueItem.Button? + if hasWebApp { + actionButton = PeerInfoScreenLabeledValueItem.Button(title: presentationData.strings.PeerInfo_OpenAppButton, action: { + guard let parentController = interaction.getController() else { + return + } + + if let navigationController = parentController.navigationController as? NavigationController, let minimizedContainer = navigationController.minimizedContainer { + for controller in minimizedContainer.controllers { + if let controller = controller as? AttachmentController, let mainController = controller.mainController as? WebAppController, mainController.botId == user.id && mainController.source == .generic { + navigationController.maximizeViewController(controller, animated: true) + return + } + } + } + + context.sharedContext.openWebApp( + context: context, + parentController: parentController, + updatedPresentationData: nil, + botPeer: .user(user), + chatPeer: nil, + threadId: nil, + buttonText: "", + url: "", + simple: true, + source: .generic, + skipTermsOfService: true, + payload: nil, + verifyAgeCompletion: nil + ) + }) + } + + if hasAbout || hasWebApp { + var label: String = "" + if let about = cachedData.about, !about.isEmpty { + label = user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo + } + items[currentPeerInfoSection]!.append(PeerInfoScreenLabeledValueItem(id: ItemAbout, label: label, text: cachedData.about ?? "", textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: user.isPremium ? enabledPublicBioEntities : enabledPrivateBioEntities), action: isMyProfile ? { node, _ in + bioContextAction(node, nil, nil) + } : nil, linkItemAction: bioLinkAction, button: actionButton, contextAction: bioContextAction, requestLayout: { animated in + interaction.requestLayout(animated) + })) + } + + if let note = cachedData.note, !note.text.isEmpty { + var entities = note.entities + if context.isPremium { + entities = generateTextEntities(note.text, enabledTypes: [.mention, .hashtag, .allUrl], currentEntities: entities) + } + items[currentPeerInfoSection]!.append(PeerInfoScreenLabeledValueItem(id: ItemNote, label: presentationData.strings.PeerInfo_Notes, rightLabel: presentationData.strings.PeerInfo_NotesInfo, text: note.text, entities: entities, handleSpoilers: true, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: []), action: nil, linkItemAction: bioLinkAction, button: nil, contextAction: noteContextAction, requestLayout: { animated in + interaction.requestLayout(animated) + })) + } + + if let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) { + items[currentPeerInfoSection]!.append(PeerInfoScreenCommentItem(id: ItemAppFooter, text: presentationData.strings.PeerInfo_AppFooterAdmin, linkAction: { action in + if case let .tap(url) = action { + context.sharedContext.applicationBindings.openUrl(url) + } + })) + + currentPeerInfoSection = .peerInfoTrailing + } else if actionButton != nil { + items[currentPeerInfoSection]!.append(PeerInfoScreenCommentItem(id: ItemAppFooter, text: presentationData.strings.PeerInfo_AppFooter, linkAction: { action in + if case let .tap(url) = action { + context.sharedContext.applicationBindings.openUrl(url) + } + })) + + currentPeerInfoSection = .peerInfoTrailing + } + + if let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) { + } else { + if let starRefProgram = cachedData.starRefProgram, starRefProgram.endDate == nil { + var canJoinRefProgram = false + if let data = context.currentAppConfiguration.with({ $0 }).data, let value = data["starref_connect_allowed"] { + if let value = value as? Double { + canJoinRefProgram = value != 0.0 + } else if let value = value as? Bool { + canJoinRefProgram = value + } + } + + if canJoinRefProgram { + if items[.botAffiliateProgram] == nil { + items[.botAffiliateProgram] = [] + } + let programTitleValue: String + programTitleValue = "\(formatPermille(starRefProgram.commissionPermille))%" + items[.botAffiliateProgram]!.append(PeerInfoScreenDisclosureItem(id: ItemAffiliate, label: .labelBadge(programTitleValue), additionalBadgeLabel: nil, text: presentationData.strings.PeerInfo_ItemAffiliateProgram_Title, icon: PresentationResourcesSettings.affiliateProgram, action: { + interaction.editingOpenAffiliateProgram() + })) + items[.botAffiliateProgram]!.append(PeerInfoScreenCommentItem(id: ItemAffiliateInfo, text: presentationData.strings.PeerInfo_ItemAffiliateProgram_Footer(EnginePeer.user(user).compactDisplayTitle, formatPermille(starRefProgram.commissionPermille)).string)) + } + } + } + } + + if let businessHours = cachedData.businessHours { + items[currentPeerInfoSection]!.append(PeerInfoScreenBusinessHoursItem(id: ItemBusinessHours, label: presentationData.strings.PeerInfo_BusinessHours_Label, businessHours: businessHours, requestLayout: { animated in + interaction.requestLayout(animated) + }, longTapAction: nil, contextAction: workingHoursContextAction)) + } + + if let businessLocation = cachedData.businessLocation { + if let coordinates = businessLocation.coordinates { + let imageSignal = chatMapSnapshotImage(engine: context.engine, resource: MapSnapshotMediaResource(latitude: coordinates.latitude, longitude: coordinates.longitude, width: 90, height: 90)) + items[currentPeerInfoSection]!.append(PeerInfoScreenAddressItem( + id: ItemLocation, + label: presentationData.strings.PeerInfo_Location_Label, + text: businessLocation.address, + imageSignal: imageSignal, + action: { + interaction.openLocation() + }, + contextAction: businessLocationContextAction + )) + } else { + items[currentPeerInfoSection]!.append(PeerInfoScreenAddressItem( + id: ItemLocation, + label: presentationData.strings.PeerInfo_Location_Label, + text: businessLocation.address, + imageSignal: nil, + action: nil, + contextAction: businessLocationContextAction + )) + } + } + } + + if !isMyProfile { + if let reactionSourceMessageId = reactionSourceMessageId, !data.isContact { + items[currentPeerInfoSection]!.append(PeerInfoScreenActionItem(id: ItemSendMessage, text: presentationData.strings.UserInfo_SendMessage, action: { + interaction.openChat(nil) + })) + + items[currentPeerInfoSection]!.append(PeerInfoScreenActionItem(id: ItemReport, text: presentationData.strings.ReportPeer_BanAndReport, color: .destructive, action: { + interaction.openReport(.reaction(reactionSourceMessageId)) + })) + } else if let _ = nearbyPeerDistance { + items[currentPeerInfoSection]!.append(PeerInfoScreenActionItem(id: ItemSendMessage, text: presentationData.strings.UserInfo_SendMessage, action: { + interaction.openChat(nil) + })) + + items[currentPeerInfoSection]!.append(PeerInfoScreenActionItem(id: ItemReport, text: presentationData.strings.ReportPeer_Report, color: .destructive, action: { + interaction.openReport(.user) + })) + } else { + if !data.isContact { + if user.botInfo == nil { + items[currentPeerInfoSection]!.append(PeerInfoScreenActionItem(id: ItemAddToContacts, text: presentationData.strings.PeerInfo_AddToContacts, action: { + interaction.openAddContact() + })) + } + } + + var isBlocked = false + if let cachedData = data.cachedData as? CachedUserData, cachedData.isBlocked { + isBlocked = true + } + + if isBlocked { + items[currentPeerInfoSection]!.append(PeerInfoScreenActionItem(id: ItemBlock, text: user.botInfo != nil ? presentationData.strings.Bot_Unblock : presentationData.strings.Conversation_Unblock, action: { + interaction.updateBlocked(false) + })) + } else { + if user.flags.contains(.isSupport) || data.isContact { + } else { + if user.botInfo == nil { + items[currentPeerInfoSection]!.append(PeerInfoScreenActionItem(id: ItemBlock, text: presentationData.strings.Conversation_BlockUser, color: .destructive, action: { + interaction.updateBlocked(true) + })) + } + } + } + + if let encryptionKeyFingerprint = data.encryptionKeyFingerprint { + items[currentPeerInfoSection]!.append(PeerInfoScreenDisclosureEncryptionKeyItem(id: ItemEncryptionKey, text: presentationData.strings.Profile_EncryptionKey, fingerprint: encryptionKeyFingerprint, action: { + interaction.openEncryptionKey() + })) + } + + let revenueBalance = data.revenueStatsState?.balances.currentBalance.amount.value ?? 0 + let overallRevenueBalance = data.revenueStatsState?.balances.overallRevenue.amount.value ?? 0 + + let starsBalance = data.starsRevenueStatsState?.balances.currentBalance.amount ?? StarsAmount.zero + let overallStarsBalance = data.starsRevenueStatsState?.balances.overallRevenue.amount ?? StarsAmount.zero + + if overallRevenueBalance > 0 || overallStarsBalance > StarsAmount.zero { + items[.balances]!.append(PeerInfoScreenHeaderItem(id: ItemBalanceHeader, text: presentationData.strings.PeerInfo_BotBalance_Title)) + if overallRevenueBalance > 0 { + let string = "*\(formatTonAmountText(revenueBalance, dateTimeFormat: presentationData.dateTimeFormat))" + let attributedString = NSMutableAttributedString(string: string, font: Font.regular(presentationData.listsFontSize.itemListBaseFontSize), textColor: presentationData.theme.list.itemSecondaryTextColor) + if let range = attributedString.string.range(of: "*") { + attributedString.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .ton(tinted: false)), range: NSRange(range, in: attributedString.string)) + attributedString.addAttribute(.baselineOffset, value: 1.5, range: NSRange(range, in: attributedString.string)) + } + items[.balances]!.append(PeerInfoScreenDisclosureItem(id: ItemBalanceTon, label: .attributedText(attributedString), text: presentationData.strings.PeerInfo_BotBalance_Ton, icon: PresentationResourcesSettings.ton, action: { + interaction.editingOpenRevenue() + })) + } + + if overallStarsBalance > StarsAmount.zero { + let formattedLabel = formatStarsAmountText(starsBalance, dateTimeFormat: presentationData.dateTimeFormat) + let smallLabelFont = Font.regular(floor(presentationData.listsFontSize.itemListBaseFontSize / 17.0 * 13.0)) + let labelFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize) + let labelColor = presentationData.theme.list.itemSecondaryTextColor + let attributedString = tonAmountAttributedString(formattedLabel, integralFont: labelFont, fractionalFont: smallLabelFont, color: labelColor, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator).mutableCopy() as! NSMutableAttributedString + attributedString.insert(NSAttributedString(string: "*", font: labelFont, textColor: labelColor), at: 0) + + if let range = attributedString.string.range(of: "*") { + attributedString.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: false)), range: NSRange(range, in: attributedString.string)) + attributedString.addAttribute(.baselineOffset, value: 1.5, range: NSRange(range, in: attributedString.string)) + } + items[.balances]!.append(PeerInfoScreenDisclosureItem(id: ItemBalanceStars, label: .attributedText(attributedString), text: presentationData.strings.PeerInfo_BotBalance_Stars, icon: PresentationResourcesSettings.stars, action: { + interaction.editingOpenStars() + })) + } + } + + if let _ = user.botInfo { + var canManageEmojiStatus = false + if let cachedData = data.cachedData as? CachedUserData, cachedData.flags.contains(.botCanManageEmojiStatus) { + canManageEmojiStatus = true + } + if canManageEmojiStatus || data.webAppPermissions?.emojiStatus?.isRequested == true { + items[.permissions]!.append(PeerInfoScreenSwitchItem(id: ItemBotPermissionsEmojiStatus, text: presentationData.strings.PeerInfo_Permissions_EmojiStatus, value: canManageEmojiStatus, icon: UIImage(bundleImageName: "Chat/Info/Status"), isLocked: false, toggled: { value in + let _ = (context.engine.peers.toggleBotEmojiStatusAccess(peerId: user.id, enabled: value) + |> deliverOnMainQueue).startStandalone() + + let _ = updateWebAppPermissionsStateInteractively(context: context, peerId: user.id) { current in + return WebAppPermissionsState(location: current?.location, emojiStatus: WebAppPermissionsState.EmojiStatus(isRequested: true)) + }.startStandalone() + })) + } + if data.webAppPermissions?.location?.isRequested == true || data.webAppPermissions?.location?.isAllowed == true { + items[.permissions]!.append(PeerInfoScreenSwitchItem(id: ItemBotPermissionsLocation, text: presentationData.strings.PeerInfo_Permissions_Geolocation, value: data.webAppPermissions?.location?.isAllowed ?? false, icon: UIImage(bundleImageName: "Chat/Info/Location"), isLocked: false, toggled: { value in + let _ = updateWebAppPermissionsStateInteractively(context: context, peerId: user.id) { current in + return WebAppPermissionsState(location: WebAppPermissionsState.Location(isRequested: true, isAllowed: value), emojiStatus: current?.emojiStatus) + }.startStandalone() + })) + } + if !"".isEmpty { + items[.permissions]!.append(PeerInfoScreenSwitchItem(id: ItemBotPermissionsBiometry, text: presentationData.strings.PeerInfo_Permissions_Biometry, value: true, icon: UIImage(bundleImageName: "Settings/Menu/TouchId"), isLocked: false, toggled: { value in + + })) + } + + if !items[.permissions]!.isEmpty { + items[.permissions]!.insert(PeerInfoScreenHeaderItem(id: ItemBotPermissionsHeader, text: presentationData.strings.PeerInfo_Permissions_Title), at: 0) + } + } + + if let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) { + items[currentPeerInfoSection]!.append(PeerInfoScreenDisclosureItem(id: ItemBotSettings, label: .none, text: presentationData.strings.Bot_Settings, icon: UIImage(bundleImageName: "Chat/Info/SettingsIcon"), action: { + interaction.openEditing() + })) + } + + if let botInfo = user.botInfo, !botInfo.flags.contains(.canEdit) { + items[currentPeerInfoSection]!.append(PeerInfoScreenActionItem(id: ItemBotReport, text: presentationData.strings.ReportPeer_Report, action: { + interaction.openReport(.default) + })) + } + + if let verification = (data.cachedData as? CachedUserData)?.verification { + let description: String + let descriptionString = verification.description + let entities = generateTextEntities(descriptionString, enabledTypes: [.allUrl]) + if let entity = entities.first { + let range = NSRange(location: entity.range.lowerBound, length: entity.range.upperBound - entity.range.lowerBound) + let url = (descriptionString as NSString).substring(with: range) + description = descriptionString.replacingOccurrences(of: url, with: "[\(url)](\(url))") + } else { + description = descriptionString + } + let attributedPrefix = NSMutableAttributedString(string: " ") + attributedPrefix.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: verification.iconFileId, file: nil), range: NSMakeRange(0, 1)) + + items[currentPeerInfoSection]!.append(PeerInfoScreenCommentItem(id: ItemVerification, text: description, attributedPrefix: attributedPrefix, useAccentLinkColor: false, linkAction: { action in + if case let .tap(url) = action, let navigationController = interaction.getController()?.navigationController as? NavigationController { + context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: url, forceExternal: false, presentationData: presentationData, navigationController: navigationController, dismissInput: {}) + } + })) + } else if let botInfo = user.botInfo, botInfo.flags.contains(.worksWithGroups) { + items[currentPeerInfoSection]!.append(PeerInfoScreenActionItem(id: ItemBotAddToChat, text: presentationData.strings.Bot_AddToChat, color: .accent, action: { + interaction.openAddBotToGroup() + })) + items[currentPeerInfoSection]!.append(PeerInfoScreenCommentItem(id: ItemBotAddToChatInfo, text: presentationData.strings.Bot_AddToChatInfo)) + } + } + } + } else if let channel = data.peer as? TelegramChannel { + let ItemUsername = 1 + let ItemUsernameInfo = 2 + let ItemAbout = 3 + let ItemLocationHeader = 4 + let ItemLocation = 5 + let ItemAdmins = 6 + let ItemMembers = 7 + let ItemMemberRequests = 8 + let ItemBalance = 9 + let ItemEdit = 10 + let ItemPeerPersonalChannel = 11 + + if let _ = data.threadData { + let mainUsername: String + if let addressName = channel.addressName { + mainUsername = addressName + } else { + mainUsername = "c/\(channel.id.id._internalGetInt64Value())" + } + + var threadId: Int64 = 0 + if case let .replyThread(message) = chatLocation { + threadId = message.threadId + } + + let linkText = "https://t.me/\(mainUsername)/\(threadId)" + + items[currentPeerInfoSection]!.append( + PeerInfoScreenLabeledValueItem( + id: ItemUsername, + label: presentationData.strings.Channel_LinkItem, + text: linkText, + textColor: .accent, + icon: .qrCode, + action: { _, progress in + interaction.openUsername(linkText, true, progress) + }, longTapAction: { sourceNode in + interaction.openPeerInfoContextMenu(.link(customLink: linkText), sourceNode, nil) + }, linkItemAction: { type, item, _, _, progress in + if case .tap = type { + if case let .mention(username) = item { + interaction.openUsername(String(username.suffix(from: username.index(username.startIndex, offsetBy: 1))), false, progress) + } + } + }, iconAction: { + interaction.openQrCode() + }, requestLayout: { animated in + interaction.requestLayout(animated) + } + ) + ) + if let _ = channel.addressName { + + } else { + items[currentPeerInfoSection]!.append(PeerInfoScreenCommentItem(id: ItemUsernameInfo, text: presentationData.strings.PeerInfo_PrivateShareLinkInfo)) + } + } else { + if let location = (data.cachedData as? CachedChannelData)?.peerGeoLocation { + items[.groupLocation]!.append(PeerInfoScreenHeaderItem(id: ItemLocationHeader, text: presentationData.strings.GroupInfo_Location.uppercased())) + + let imageSignal = chatMapSnapshotImage(engine: context.engine, resource: MapSnapshotMediaResource(latitude: location.latitude, longitude: location.longitude, width: 90, height: 90)) + items[.groupLocation]!.append(PeerInfoScreenAddressItem( + id: ItemLocation, + label: "", + text: location.address.replacingOccurrences(of: ", ", with: "\n"), + imageSignal: imageSignal, + action: { + interaction.openLocation() + } + )) + } + + if let mainUsername = channel.addressName { + var additionalUsernames: String? + let usernames = channel.usernames.filter { $0.isActive && $0.username != mainUsername } + if !usernames.isEmpty { + additionalUsernames = presentationData.strings.Profile_AdditionalUsernames(String(usernames.map { "@\($0.username)" }.joined(separator: ", "))).string + } + + items[currentPeerInfoSection]!.append( + PeerInfoScreenLabeledValueItem( + id: ItemUsername, + label: presentationData.strings.Channel_LinkItem, + text: "https://t.me/\(mainUsername)", + additionalText: additionalUsernames, + textColor: .accent, + icon: .qrCode, + action: { _, progress in + interaction.openUsername(mainUsername, true, progress) + }, longTapAction: { sourceNode in + interaction.openPeerInfoContextMenu(.link(customLink: nil), sourceNode, nil) + }, linkItemAction: { type, item, sourceNode, sourceRect, progress in + if case .tap = type { + if case let .mention(username) = item { + interaction.openUsername(String(username.suffix(from: username.index(username.startIndex, offsetBy: 1))), false, progress) + } + } else if case .longTap = type { + if case let .mention(username) = item { + interaction.openPeerInfoContextMenu(.link(customLink: username), sourceNode, sourceRect) + } + } + }, iconAction: { + interaction.openQrCode() + }, requestLayout: { animated in + interaction.requestLayout(animated) + } + ) + ) + } + if let cachedData = data.cachedData as? CachedChannelData { + let aboutText: String? + if channel.isFake { + if case .broadcast = channel.info { + aboutText = presentationData.strings.ChannelInfo_FakeChannelWarning + } else { + aboutText = presentationData.strings.GroupInfo_FakeGroupWarning + } + } else if channel.isScam { + if case .broadcast = channel.info { + aboutText = presentationData.strings.ChannelInfo_ScamChannelWarning + } else { + aboutText = presentationData.strings.GroupInfo_ScamGroupWarning + } + } else if let about = cachedData.about, !about.isEmpty { + aboutText = about + } else { + aboutText = nil + } + + if let aboutText = aboutText { + var enabledEntities = enabledPublicBioEntities + if case .group = channel.info { + enabledEntities = enabledPrivateBioEntities + } + items[currentPeerInfoSection]!.append(PeerInfoScreenLabeledValueItem(id: ItemAbout, label: presentationData.strings.Channel_Info_Description, text: aboutText, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledEntities), action: isMyProfile ? { node, _ in + bioContextAction(node, nil, nil) + } : nil, linkItemAction: bioLinkAction, contextAction: bioContextAction, requestLayout: { animated in + interaction.requestLayout(animated) + })) + } + + if let verification = (data.cachedData as? CachedChannelData)?.verification { + let description: String + let descriptionString = verification.description + let entities = generateTextEntities(descriptionString, enabledTypes: [.allUrl]) + if let entity = entities.first { + let range = NSRange(location: entity.range.lowerBound, length: entity.range.upperBound - entity.range.lowerBound) + let url = (descriptionString as NSString).substring(with: range) + description = descriptionString.replacingOccurrences(of: url, with: "[\(url)](\(url))") + } else { + description = descriptionString + } + + let attributedPrefix = NSMutableAttributedString(string: " ") + attributedPrefix.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: verification.iconFileId, file: nil), range: NSMakeRange(0, 1)) + + items[currentPeerInfoSection]!.append(PeerInfoScreenCommentItem(id: 800, text: description, attributedPrefix: attributedPrefix, useAccentLinkColor: false, linkAction: { action in + if case let .tap(url) = action, let navigationController = interaction.getController()?.navigationController as? NavigationController { + context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: url, forceExternal: false, presentationData: presentationData, navigationController: navigationController, dismissInput: {}) + } + })) + } + + if case .broadcast = channel.info { + var canEditMembers = false + if channel.hasPermission(.banMembers) { + canEditMembers = true + } + if canEditMembers { + if channel.adminRights != nil || channel.flags.contains(.isCreator) { + let adminCount = cachedData.participantsSummary.adminCount ?? 0 + let memberCount = cachedData.participantsSummary.memberCount ?? 0 + + items[.peerMembers]!.append(PeerInfoScreenDisclosureItem(id: ItemAdmins, label: .text("\(adminCount == 0 ? "" : "\(presentationStringsFormattedNumber(adminCount, presentationData.dateTimeFormat.groupingSeparator))")"), text: presentationData.strings.GroupInfo_Administrators, icon: UIImage(bundleImageName: "Chat/Info/GroupAdminsIcon"), action: { + interaction.openParticipantsSection(.admins) + })) + items[.peerMembers]!.append(PeerInfoScreenDisclosureItem(id: ItemMembers, label: .text("\(memberCount == 0 ? "" : "\(presentationStringsFormattedNumber(memberCount, presentationData.dateTimeFormat.groupingSeparator))")"), text: presentationData.strings.Channel_Info_Subscribers, icon: UIImage(bundleImageName: "Chat/Info/GroupMembersIcon"), action: { + interaction.openParticipantsSection(.members) + })) + + if let count = data.requests?.count, count > 0 { + items[.peerMembers]!.append(PeerInfoScreenDisclosureItem(id: ItemMemberRequests, label: .badge(presentationStringsFormattedNumber(count, presentationData.dateTimeFormat.groupingSeparator), presentationData.theme.list.itemAccentColor), text: presentationData.strings.GroupInfo_MemberRequests, icon: UIImage(bundleImageName: "Chat/Info/GroupRequestsIcon"), action: { + interaction.openParticipantsSection(.memberRequests) + })) + } + } + } + } + + if channel.adminRights != nil || channel.flags.contains(.isCreator) { + let section: InfoSection + if case .group = channel.info { + section = .peerSettings + } else { + section = .peerMembers + } + if cachedData.flags.contains(.canViewRevenue) || cachedData.flags.contains(.canViewStarsRevenue) { + let revenueBalance = data.revenueStatsState?.balances.currentBalance.amount.value ?? 0 + let starsBalance = data.starsRevenueStatsState?.balances.currentBalance.amount ?? StarsAmount.zero + + let overallRevenueBalance = data.revenueStatsState?.balances.overallRevenue.amount.value ?? 0 + let overallStarsBalance = data.starsRevenueStatsState?.balances.overallRevenue.amount ?? StarsAmount.zero + + if overallRevenueBalance > 0 || overallStarsBalance > StarsAmount.zero { + let smallLabelFont = Font.regular(floor(presentationData.listsFontSize.itemListBaseFontSize / 17.0 * 13.0)) + let labelFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize) + let labelColor = presentationData.theme.list.itemSecondaryTextColor + + let attributedString = NSMutableAttributedString() + if overallRevenueBalance > 0 { + attributedString.append(NSAttributedString(string: "#\(formatTonAmountText(revenueBalance, dateTimeFormat: presentationData.dateTimeFormat))", font: labelFont, textColor: labelColor)) + } + if overallStarsBalance > StarsAmount.zero { + if !attributedString.string.isEmpty { + attributedString.append(NSAttributedString(string: " ", font: labelFont, textColor: labelColor)) + } + attributedString.append(NSAttributedString(string: "*", font: labelFont, textColor: labelColor)) + + let formattedLabel = formatStarsAmountText(starsBalance, dateTimeFormat: presentationData.dateTimeFormat) + let starsAttributedString = tonAmountAttributedString(formattedLabel, integralFont: labelFont, fractionalFont: smallLabelFont, color: labelColor, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator).mutableCopy() as! NSMutableAttributedString + attributedString.append(starsAttributedString) + } + if let range = attributedString.string.range(of: "#") { + attributedString.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .ton(tinted: false)), range: NSRange(range, in: attributedString.string)) + attributedString.addAttribute(.baselineOffset, value: 1.5, range: NSRange(range, in: attributedString.string)) + } + if let range = attributedString.string.range(of: "*") { + attributedString.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 1, file: nil, custom: .stars(tinted: false)), range: NSRange(range, in: attributedString.string)) + attributedString.addAttribute(.baselineOffset, value: 1.5, range: NSRange(range, in: attributedString.string)) + } + + items[section]!.append(PeerInfoScreenDisclosureItem(id: ItemBalance, label: .attributedText(attributedString), text: presentationData.strings.PeerInfo_Bot_Balance, icon: PresentationResourcesSettings.balance, action: { + interaction.openStats(.monetization) + })) + } + } + + let settingsTitle: String + switch channel.info { + case .broadcast: + settingsTitle = presentationData.strings.Channel_Info_Settings + case .group: + settingsTitle = presentationData.strings.Group_Info_Settings + } + items[section]!.append(PeerInfoScreenDisclosureItem(id: ItemEdit, label: .none, text: settingsTitle, icon: UIImage(bundleImageName: "Chat/Info/SettingsIcon"), action: { + interaction.openEditing() + })) + } + + if channel.hasPermission(.manageDirect), let personalChannel = data.personalChannel { + let peerId = personalChannel.peer.peerId + items[.channelMonoforum]?.append(PeerInfoScreenPersonalChannelItem(id: ItemPeerPersonalChannel, context: context, data: personalChannel, controller: { [weak interaction] in + guard let interaction else { + return nil + } + return interaction.getController() + }, action: { [weak interaction] in + guard let interaction else { + return + } + interaction.openChat(peerId) + })) + } + } + } + } else if let group = data.peer as? TelegramGroup { + if let cachedData = data.cachedData as? CachedGroupData { + let aboutText: String? + if group.isFake { + aboutText = presentationData.strings.GroupInfo_FakeGroupWarning + } else if group.isScam { + aboutText = presentationData.strings.GroupInfo_ScamGroupWarning + } else if let about = cachedData.about, !about.isEmpty { + aboutText = about + } else { + aboutText = nil + } + + if let aboutText = aboutText { + items[currentPeerInfoSection]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: presentationData.strings.Channel_Info_Description, text: aboutText, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledPrivateBioEntities), action: isMyProfile ? { node, _ in + bioContextAction(node, nil, nil) + } : nil, linkItemAction: bioLinkAction, contextAction: bioContextAction, requestLayout: { animated in + interaction.requestLayout(animated) + })) + } + } + } + + if let peer = data.peer, let members = data.members, case let .shortList(_, memberList) = members { + var canAddMembers = false + if let group = data.peer as? TelegramGroup { + switch group.role { + case .admin, .creator: + canAddMembers = true + case .member: + break + } + if !group.hasBannedPermission(.banAddMembers) { + canAddMembers = true + } + } else if let channel = data.peer as? TelegramChannel { + switch channel.info { + case .broadcast: + break + case .group: + if channel.flags.contains(.isCreator) || channel.hasPermission(.inviteMembers) { + canAddMembers = true + } + } + } + + if canAddMembers { + items[.peerMembers]!.append(PeerInfoScreenActionItem(id: 0, text: presentationData.strings.GroupInfo_AddParticipant, color: .accent, icon: UIImage(bundleImageName: "Contact List/AddMemberIcon"), alignment: .peerList, action: { + interaction.openAddMember() + })) + } + + for member in memberList { + let isAccountPeer = member.id == context.account.peerId + items[.peerMembers]!.append(PeerInfoScreenMemberItem(id: member.id, context: .account(context), enclosingPeer: peer, member: member, isAccount: false, action: isAccountPeer ? nil : { action in + switch action { + case .open: + interaction.openPeerInfo(member.peer, true) + case .promote: + interaction.performMemberAction(member, .promote) + case .restrict: + interaction.performMemberAction(member, .restrict) + case .remove: + interaction.performMemberAction(member, .remove) + } + }, openStories: { sourceView in + interaction.performMemberAction(member, .openStories(sourceView: sourceView)) + })) + } + } + + var result: [(AnyHashable, [PeerInfoScreenItem])] = [] + for section in InfoSection.allCases { + if let sectionItems = items[section], !sectionItems.isEmpty { + result.append((section, sectionItems)) + } + } + return result +} + +func editingItems(data: PeerInfoScreenData?, boostStatus: ChannelBoostStatus?, state: PeerInfoState, chatLocation: ChatLocation, context: AccountContext, presentationData: PresentationData, interaction: PeerInfoInteraction) -> [(AnyHashable, [PeerInfoScreenItem])] { + enum Section: Int, CaseIterable { + case notifications + case groupLocation + case peerPublicSettings + case peerNote + case peerDataSettings + case peerVerifySettings + case peerSettings + case linkedMonoforum + case peerAdditionalSettings + case peerActions + } + + var items: [Section: [PeerInfoScreenItem]] = [:] + for section in Section.allCases { + items[section] = [] + } + + if let data = data { + if let user = data.peer as? TelegramUser { + let ItemNote: AnyHashable = AnyHashable("note_edit") + let ItemNoteInfo = 1 + + let ItemSuggestBirthdate = 2 + let ItemSuggestPhoto = 3 + let ItemCustomPhoto = 4 + let ItemReset = 5 + let ItemInfo = 6 + let ItemDelete = 7 + let ItemUsername = 8 + let ItemAffiliateProgram = 9 + + let ItemVerify = 10 + + let ItemIntro = 11 + let ItemCommands = 12 + let ItemBotSettings = 13 + let ItemBotInfo = 14 + + if let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) { + items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemUsername, label: .text("@\(user.addressName ?? "")"), text: presentationData.strings.PeerInfo_Bot_Username, icon: PresentationResourcesSettings.bot, action: { + interaction.editingOpenPublicLinkSetup() + })) + + var canSetupRefProgram = false + if let data = context.currentAppConfiguration.with({ $0 }).data, let value = data["starref_program_allowed"] { + if let value = value as? Double { + canSetupRefProgram = value != 0.0 + } else if let value = value as? Bool { + canSetupRefProgram = value + } + } + + if canSetupRefProgram { + let programTitleValue: PeerInfoScreenDisclosureItem.Label + if let cachedData = data.cachedData as? CachedUserData, let starRefProgram = cachedData.starRefProgram, starRefProgram.endDate == nil { + programTitleValue = .labelBadge("\(formatPermille(starRefProgram.commissionPermille))%") + } else { + programTitleValue = .text(presentationData.strings.PeerInfo_ItemAffiliateProgram_ValueOff) + } + items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAffiliateProgram, label: programTitleValue, additionalBadgeLabel: presentationData.strings.Settings_New, text: presentationData.strings.PeerInfo_ItemAffiliateProgram_Title, icon: PresentationResourcesSettings.affiliateProgram, action: { + interaction.editingOpenAffiliateProgram() + })) + } + + if let cachedUserData = data.cachedData as? CachedUserData, let _ = cachedUserData.botInfo?.verifierSettings { + items[.peerVerifySettings]!.append(PeerInfoScreenActionItem(id: ItemVerify, text: presentationData.strings.PeerInfo_VerifyAccounts, icon: UIImage(bundleImageName: "Peer Info/BotVerify"), action: { + interaction.editingOpenVerifyAccounts() + })) + } + + items[.peerSettings]!.append(PeerInfoScreenActionItem(id: ItemIntro, text: presentationData.strings.PeerInfo_Bot_EditIntro, icon: UIImage(bundleImageName: "Peer Info/BotIntro"), action: { + interaction.openPeerMention("botfather", .withBotStartPayload(ChatControllerInitialBotStart(payload: "\(user.addressName ?? "")-intro", behavior: .interactive))) + })) + items[.peerSettings]!.append(PeerInfoScreenActionItem(id: ItemCommands, text: presentationData.strings.PeerInfo_Bot_EditCommands, icon: UIImage(bundleImageName: "Peer Info/BotCommands"), action: { + interaction.openPeerMention("botfather", .withBotStartPayload(ChatControllerInitialBotStart(payload: "\(user.addressName ?? "")-commands", behavior: .interactive))) + })) + items[.peerSettings]!.append(PeerInfoScreenActionItem(id: ItemBotSettings, text: presentationData.strings.PeerInfo_Bot_ChangeSettings, icon: UIImage(bundleImageName: "Peer Info/BotSettings"), action: { + interaction.openPeerMention("botfather", .withBotStartPayload(ChatControllerInitialBotStart(payload: user.addressName ?? "", behavior: .interactive))) + })) + items[.peerSettings]!.append(PeerInfoScreenCommentItem(id: ItemBotInfo, text: presentationData.strings.PeerInfo_Bot_BotFatherInfo, linkAction: { _ in + interaction.openPeerMention("botfather", .default) + })) + } else if !user.flags.contains(.isSupport) { + let compactName = EnginePeer(user).compactDisplayTitle + + if let cachedData = data.cachedData as? CachedUserData { + items[.peerNote]!.append(PeerInfoScreenNoteListItem( + id: ItemNote, + initialValue: chatInputStateStringWithAppliedEntities(cachedData.note?.text ?? "", entities: cachedData.note?.entities ?? []), + valueUpdated: { value in + interaction.updateNote(value) + }, + requestLayout: { animated in + interaction.requestLayout(animated) + } + )) + + items[.peerNote]!.append(PeerInfoScreenCommentItem(id: ItemNoteInfo, text: presentationData.strings.PeerInfo_AddNotesInfo)) + + if let _ = cachedData.sendPaidMessageStars { + + } else { + if cachedData.birthday == nil { + items[.peerDataSettings]!.append(PeerInfoScreenActionItem(id: ItemSuggestBirthdate, text: presentationData.strings.UserInfo_SuggestBirthdate, color: .accent, icon: UIImage(bundleImageName: "Contact List/AddBirthdayIcon"), action: { + interaction.suggestBirthdate() + })) + } + + items[.peerDataSettings]!.append(PeerInfoScreenActionItem(id: ItemSuggestPhoto, text: presentationData.strings.UserInfo_SuggestPhoto(compactName).string, color: .accent, icon: UIImage(bundleImageName: "Peer Info/SuggestAvatar"), action: { + interaction.suggestPhoto() + })) + } + } + + let setText: String + if user.photo.first?.isPersonal == true || state.updatingAvatar != nil { + setText = presentationData.strings.UserInfo_ChangeCustomPhoto(compactName).string + } else { + setText = presentationData.strings.UserInfo_SetCustomPhoto(compactName).string + } + + items[.peerDataSettings]!.append(PeerInfoScreenActionItem(id: ItemCustomPhoto, text: setText, color: .accent, icon: UIImage(bundleImageName: "Settings/SetAvatar"), action: { + interaction.setCustomPhoto() + })) + + if user.photo.first?.isPersonal == true || state.updatingAvatar != nil { + var representation: TelegramMediaImageRepresentation? + var originalIsVideo: Bool? + if let cachedData = data.cachedData as? CachedUserData, case let .known(photo) = cachedData.photo { + representation = photo?.representationForDisplayAtSize(PixelDimensions(width: 28, height: 28)) + originalIsVideo = !(photo?.videoRepresentations.isEmpty ?? true) + } + + let removeText: String + if let originalIsVideo { + removeText = originalIsVideo ? presentationData.strings.UserInfo_ResetCustomVideo : presentationData.strings.UserInfo_ResetCustomPhoto + } else { + removeText = user.photo.first?.hasVideo == true ? presentationData.strings.UserInfo_RemoveCustomVideo : presentationData.strings.UserInfo_RemoveCustomPhoto + } + + let imageSignal: Signal + if let representation, let signal = peerAvatarImage(account: context.account, peerReference: PeerReference(user), authorOfMessage: nil, representation: representation, displayDimensions: CGSize(width: 28.0, height: 28.0)) { + imageSignal = signal + |> map { data -> UIImage? in + return data?.0 + } + } else { + imageSignal = peerAvatarCompleteImage(account: context.account, peer: EnginePeer(user), forceProvidedRepresentation: true, representation: representation, size: CGSize(width: 28.0, height: 28.0)) + } + + items[.peerDataSettings]!.append(PeerInfoScreenActionItem(id: ItemReset, text: removeText, color: .accent, icon: nil, iconSignal: imageSignal, action: { + interaction.resetCustomPhoto() + })) + } + items[.peerDataSettings]!.append(PeerInfoScreenCommentItem(id: ItemInfo, text: presentationData.strings.UserInfo_CustomPhotoInfo(compactName).string)) + } + + if data.isContact { + items[.peerSettings]!.append(PeerInfoScreenActionItem(id: ItemDelete, text: presentationData.strings.UserInfo_DeleteContact, color: .destructive, action: { + interaction.requestDeleteContact() + })) + } + } else if let channel = data.peer as? TelegramChannel { + switch channel.info { + case .broadcast: + let ItemUsername = 1 + let ItemPeerColor = 2 + let ItemInviteLinks = 3 + let ItemDiscussionGroup = 4 + let ItemDeleteChannel = 5 + let ItemReactions = 6 + let ItemAdmins = 7 + let ItemMembers = 8 + let ItemMemberRequests = 9 + let ItemStats = 10 + let ItemBanned = 11 + let ItemRecentActions = 12 + let ItemAffiliatePrograms = 13 + let ItemPostSuggestionsSettings = 14 + let ItemPeerAutoTranslate = 15 + + let isCreator = channel.flags.contains(.isCreator) + + if isCreator { + let linkText: String + if let _ = channel.addressName { + linkText = presentationData.strings.Channel_Setup_TypePublic + } else { + linkText = presentationData.strings.Channel_Setup_TypePrivate + } + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemUsername, label: .text(linkText), text: presentationData.strings.Channel_TypeSetup_Title, icon: UIImage(bundleImageName: "Chat/Info/GroupChannelIcon"), action: { + interaction.editingOpenPublicLinkSetup() + })) + } + + if (isCreator && (channel.addressName?.isEmpty ?? true)) || (!channel.flags.contains(.isCreator) && channel.adminRights?.rights.contains(.canInviteUsers) == true) { + let invitesText: String + if let count = data.invitations?.count, count > 0 { + invitesText = "\(count)" + } else { + invitesText = "" + } + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, icon: UIImage(bundleImageName: "Chat/Info/GroupLinksIcon"), action: { + interaction.editingOpenInviteLinksSetup() + })) + } + + if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) { + let discussionGroupTitle: String + if let _ = data.cachedData as? CachedChannelData { + if let peer = data.linkedDiscussionPeer { + if let addressName = peer.addressName, !addressName.isEmpty { + discussionGroupTitle = "@\(addressName)" + } else { + discussionGroupTitle = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + } + } else { + discussionGroupTitle = presentationData.strings.Channel_DiscussionGroupAdd + } + } else { + discussionGroupTitle = "..." + } + + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemDiscussionGroup, label: .text(discussionGroupTitle), text: presentationData.strings.Channel_DiscussionGroup, icon: UIImage(bundleImageName: "Chat/Info/GroupDiscussionIcon"), action: { + interaction.editingOpenDiscussionGroupSetup() + })) + } + + if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) { + let label: String + if let cachedData = data.cachedData as? CachedChannelData, case let .known(reactionSettings) = cachedData.reactionSettings { + switch reactionSettings.allowedReactions { + case .all: + label = presentationData.strings.PeerInfo_LabelAllReactions + case .empty: + if let starsAllowed = reactionSettings.starsAllowed, starsAllowed { + label = "1" + } else { + label = presentationData.strings.PeerInfo_ReactionsDisabled + } + case let .limited(reactions): + var countValue = reactions.count + if let starsAllowed = reactionSettings.starsAllowed, starsAllowed { + countValue += 1 + } + label = "\(countValue)" + } + } else { + label = "" + } + let additionalBadgeLabel: String? = nil + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemReactions, label: .text(label), additionalBadgeLabel: additionalBadgeLabel, text: presentationData.strings.PeerInfo_Reactions, icon: UIImage(bundleImageName: "Settings/Menu/Reactions"), action: { + interaction.editingOpenReactionsSetup() + })) + } + + if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) { + var colors: [PeerNameColors.Colors] = [] + if let nameColor = channel.nameColor.flatMap({ context.peerNameColors.get($0, dark: presentationData.theme.overallDarkAppearance) }) { + colors.append(nameColor) + } + if let profileColor = channel.profileColor.flatMap({ context.peerNameColors.getProfile($0, dark: presentationData.theme.overallDarkAppearance, subject: .palette) }) { + colors.append(profileColor) + } + let colorImage = generateSettingsMenuPeerColorsLabelIcon(colors: colors) + + var boostIcon: UIImage? + if let approximateBoostLevel = channel.approximateBoostLevel, approximateBoostLevel < 1 { + boostIcon = generateDisclosureActionBoostLevelBadgeImage(text: presentationData.strings.Channel_Info_BoostLevelPlusBadge("1").string) + } else { + /*let labelText = NSAttributedString(string: presentationData.strings.Settings_New, font: Font.medium(11.0), textColor: presentationData.theme.list.itemCheckColors.foregroundColor) + let labelBounds = labelText.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: [.usesLineFragmentOrigin], context: nil) + let labelSize = CGSize(width: ceil(labelBounds.width), height: ceil(labelBounds.height)) + let badgeSize = CGSize(width: labelSize.width + 8.0, height: labelSize.height + 2.0 + 1.0) + boostIcon = generateImage(badgeSize, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + let rect = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height - UIScreenPixel * 2.0)) + + context.addPath(UIBezierPath(roundedRect: rect, cornerRadius: 5.0).cgPath) + context.setFillColor(presentationData.theme.list.itemCheckColors.fillColor.cgColor) + context.fillPath() + + UIGraphicsPushContext(context) + labelText.draw(at: CGPoint(x: 4.0, y: 1.0 + UIScreenPixel)) + UIGraphicsPopContext() + })*/ + } + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPeerColor, label: .image(colorImage, colorImage.size), additionalBadgeIcon: boostIcon, text: presentationData.strings.Channel_Info_AppearanceItem, icon: UIImage(bundleImageName: "Chat/Info/NameColorIcon"), action: { + interaction.editingOpenNameColorSetup() + })) + + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) + var isLocked = true + if let boostLevel = boostStatus?.level, boostLevel >= BoostSubject.autoTranslate.requiredLevel(group: false, context: context, configuration: premiumConfiguration) { + isLocked = false + } + items[.peerSettings]!.append(PeerInfoScreenSwitchItem(id: ItemPeerAutoTranslate, text: presentationData.strings.Channel_Info_AutoTranslate, value: channel.flags.contains(.autoTranslateEnabled), icon: UIImage(bundleImageName: "Settings/Menu/AutoTranslate"), isLocked: isLocked, toggled: { value in + if isLocked { + interaction.displayAutoTranslateLocked() + } else { + interaction.editingToggleAutoTranslate(value) + } + })) + } + + if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) { + let labelString: NSAttributedString + if channel.linkedMonoforumId != nil { + if let monoforumPeer = data.linkedMonoforumPeer as? TelegramChannel { + if let sendPaidMessageStars = monoforumPeer.sendPaidMessageStars { + let formattedLabel = formatStarsAmountText(sendPaidMessageStars, dateTimeFormat: presentationData.dateTimeFormat) + let smallLabelFont = Font.regular(floor(presentationData.listsFontSize.itemListBaseFontSize / 17.0 * 13.0)) + let labelFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize) + let labelColor = presentationData.theme.list.itemSecondaryTextColor + let attributedString = tonAmountAttributedString(formattedLabel, integralFont: labelFont, fractionalFont: smallLabelFont, color: labelColor, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator).mutableCopy() as! NSMutableAttributedString + attributedString.insert(NSAttributedString(string: "*", font: labelFont, textColor: labelColor), at: 0) + + if let range = attributedString.string.range(of: "*") { + attributedString.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: false)), range: NSRange(range, in: attributedString.string)) + attributedString.addAttribute(.baselineOffset, value: 1.5, range: NSRange(range, in: attributedString.string)) + } + labelString = attributedString + } else { + let labelFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize) + let labelColor = presentationData.theme.list.itemSecondaryTextColor + + labelString = NSAttributedString(string: presentationData.strings.PeerInfo_AllowChannelMessages_Free, font: labelFont, textColor: labelColor) + } + } else { + let labelFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize) + let labelColor = presentationData.theme.list.itemSecondaryTextColor + + labelString = NSAttributedString(string: " ", font: labelFont, textColor: labelColor) + } + } else { + let labelFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize) + let labelColor = presentationData.theme.list.itemSecondaryTextColor + + labelString = NSAttributedString(string: presentationData.strings.PeerInfo_AllowChannelMessages_Off, font: labelFont, textColor: labelColor) + } + + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPostSuggestionsSettings, label: .attributedText(labelString), additionalBadgeLabel: presentationData.strings.Settings_New, text: presentationData.strings.PeerInfo_AllowChannelMessages, icon: PresentationResourcesSettings.channelMessages, action: { + interaction.editingOpenPostSuggestionsSetup() + })) + + if let personalChannel = data.personalChannel { + let peerId = personalChannel.peer.peerId + items[.linkedMonoforum]?.append(PeerInfoScreenPersonalChannelItem(id: 1, context: context, data: personalChannel, controller: { [weak interaction] in + guard let interaction else { + return nil + } + return interaction.getController() + }, action: { [weak interaction] in + guard let interaction else { + return + } + interaction.openChat(peerId) + })) + } + } + + var canEditMembers = false + if channel.hasPermission(.banMembers) && (channel.adminRights != nil || channel.flags.contains(.isCreator)) { + canEditMembers = true + } + if canEditMembers { + let adminCount: Int32 + let memberCount: Int32 + if let cachedData = data.cachedData as? CachedChannelData { + adminCount = cachedData.participantsSummary.adminCount ?? 0 + memberCount = cachedData.participantsSummary.memberCount ?? 0 + } else { + adminCount = 0 + memberCount = 0 + } + + items[.peerAdditionalSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAdmins, label: .text("\(adminCount == 0 ? "" : "\(presentationStringsFormattedNumber(adminCount, presentationData.dateTimeFormat.groupingSeparator))")"), text: presentationData.strings.GroupInfo_Administrators, icon: UIImage(bundleImageName: "Chat/Info/GroupAdminsIcon"), action: { + interaction.openParticipantsSection(.admins) + })) + items[.peerAdditionalSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemMembers, label: .text("\(memberCount == 0 ? "" : "\(presentationStringsFormattedNumber(memberCount, presentationData.dateTimeFormat.groupingSeparator))")"), text: presentationData.strings.Channel_Info_Subscribers, icon: UIImage(bundleImageName: "Chat/Info/GroupMembersIcon"), action: { + interaction.openParticipantsSection(.members) + })) + + if let count = data.requests?.count, count > 0 { + items[.peerAdditionalSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemMemberRequests, label: .badge(presentationStringsFormattedNumber(count, presentationData.dateTimeFormat.groupingSeparator), presentationData.theme.list.itemAccentColor), text: presentationData.strings.GroupInfo_MemberRequests, icon: UIImage(bundleImageName: "Chat/Info/GroupRequestsIcon"), action: { + interaction.openParticipantsSection(.memberRequests) + })) + } + } + + if let cachedData = data.cachedData as? CachedChannelData, cachedData.flags.contains(.canViewStats) { + items[.peerAdditionalSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemStats, label: .none, text: presentationData.strings.Channel_Info_Stats, icon: UIImage(bundleImageName: "Chat/Info/StatsIcon"), action: { + interaction.openStats(.stats) + })) + } + + if canEditMembers { + let bannedCount: Int32 + if let cachedData = data.cachedData as? CachedChannelData { + bannedCount = cachedData.participantsSummary.kickedCount ?? 0 + } else { + bannedCount = 0 + } + items[.peerAdditionalSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemBanned, label: .text("\(bannedCount == 0 ? "" : "\(presentationStringsFormattedNumber(bannedCount, presentationData.dateTimeFormat.groupingSeparator))")"), text: presentationData.strings.GroupInfo_Permissions_Removed, icon: UIImage(bundleImageName: "Chat/Info/GroupRemovedIcon"), action: { + interaction.openParticipantsSection(.banned) + })) + + items[.peerAdditionalSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemRecentActions, label: .none, text: presentationData.strings.Group_Info_AdminLog, icon: UIImage(bundleImageName: "Chat/Info/RecentActionsIcon"), action: { + interaction.openRecentActions() + })) + } + + if channel.hasPermission(.changeInfo) { + var canJoinRefProgram = false + if let data = context.currentAppConfiguration.with({ $0 }).data, let value = data["starref_connect_allowed"] { + if let value = value as? Double { + canJoinRefProgram = value != 0.0 + } else if let value = value as? Bool { + canJoinRefProgram = value + } + } + + if canJoinRefProgram { + items[.peerAdditionalSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAffiliatePrograms, label: .text(""), additionalBadgeLabel: nil, text: presentationData.strings.PeerInfo_ItemAffiliatePrograms_Title, icon: PresentationResourcesSettings.affiliateProgram, action: { + interaction.editingOpenAffiliateProgram() + })) + } + } + + if isCreator { //if let cachedData = data.cachedData as? CachedChannelData, cachedData.flags.contains(.canDeleteHistory) { + items[.peerActions]!.append(PeerInfoScreenActionItem(id: ItemDeleteChannel, text: presentationData.strings.ChannelInfo_DeleteChannel, color: .destructive, icon: nil, alignment: .natural, action: { + interaction.openDeletePeer() + })) + } + case .group: + let ItemUsername = 101 + let ItemInviteLinks = 102 + let ItemLinkedChannel = 103 + let ItemPreHistory = 104 + let ItemMembers = 106 + let ItemPermissions = 107 + let ItemAdmins = 108 + let ItemMemberRequests = 109 + let ItemRemovedUsers = 110 + let ItemRecentActions = 111 + let ItemLocationHeader = 112 + let ItemLocation = 113 + let ItemLocationSetup = 114 + let ItemDeleteGroup = 115 + let ItemReactions = 116 + let ItemTopics = 117 + let ItemTopicsText = 118 + let ItemAppearance = 119 + + let isCreator = channel.flags.contains(.isCreator) + let isPublic = channel.addressName != nil + + if let cachedData = data.cachedData as? CachedChannelData { + if isCreator, let location = cachedData.peerGeoLocation { + items[.groupLocation]!.append(PeerInfoScreenHeaderItem(id: ItemLocationHeader, text: presentationData.strings.GroupInfo_Location.uppercased())) + + let imageSignal = chatMapSnapshotImage(engine: context.engine, resource: MapSnapshotMediaResource(latitude: location.latitude, longitude: location.longitude, width: 90, height: 90)) + items[.groupLocation]!.append(PeerInfoScreenAddressItem( + id: ItemLocation, + label: "", + text: location.address.replacingOccurrences(of: ", ", with: "\n"), + imageSignal: imageSignal, + action: { + interaction.openLocation() + } + )) + if cachedData.flags.contains(.canChangePeerGeoLocation) { + items[.groupLocation]!.append(PeerInfoScreenActionItem(id: ItemLocationSetup, text: presentationData.strings.Group_Location_ChangeLocation, action: { + interaction.editingOpenSetupLocation() + })) + } + } + + if isCreator || (channel.adminRights != nil && channel.hasPermission(.pinMessages)) { + if cachedData.peerGeoLocation != nil { + if isCreator { + let linkText: String + if let username = channel.addressName { + linkText = "@\(username)" + } else { + linkText = presentationData.strings.GroupInfo_PublicLinkAdd + } + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemUsername, label: .text(linkText), text: presentationData.strings.GroupInfo_PublicLink, icon: UIImage(bundleImageName: "Chat/Info/GroupLinksIcon"), action: { + interaction.editingOpenPublicLinkSetup() + })) + } + } else { + if cachedData.flags.contains(.canChangeUsername) { + items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemUsername, label: .text(isPublic ? presentationData.strings.Group_Setup_TypePublic : presentationData.strings.Group_Setup_TypePrivate), text: presentationData.strings.GroupInfo_GroupType, icon: UIImage(bundleImageName: "Chat/Info/GroupTypeIcon"), action: { + interaction.editingOpenPublicLinkSetup() + })) + } + } + } + + if (isCreator && (channel.addressName?.isEmpty ?? true) && cachedData.peerGeoLocation == nil) || (!isCreator && channel.adminRights?.rights.contains(.canInviteUsers) == true) { + let invitesText: String + if let count = data.invitations?.count, count > 0 { + invitesText = "\(count)" + } else { + invitesText = "" + } + + items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, icon: UIImage(bundleImageName: "Chat/Info/GroupLinksIcon"), action: { + interaction.editingOpenInviteLinksSetup() + })) + } + + if (isCreator || (channel.adminRights != nil && channel.hasPermission(.pinMessages))) && cachedData.peerGeoLocation == nil { + if let linkedDiscussionPeer = data.linkedDiscussionPeer { + let peerTitle: String + if let addressName = linkedDiscussionPeer.addressName, !addressName.isEmpty { + peerTitle = "@\(addressName)" + } else { + peerTitle = EnginePeer(linkedDiscussionPeer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + } + items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemLinkedChannel, label: .text(peerTitle), text: presentationData.strings.Group_LinkedChannel, icon: UIImage(bundleImageName: "Chat/Info/GroupLinkedChannelIcon"), action: { + interaction.editingOpenDiscussionGroupSetup() + })) + } + + if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) { + let label: String + if let cachedData = data.cachedData as? CachedChannelData, case let .known(reactionSettings) = cachedData.reactionSettings { + switch reactionSettings.allowedReactions { + case .all: + label = presentationData.strings.PeerInfo_LabelAllReactions + case .empty: + label = presentationData.strings.PeerInfo_ReactionsDisabled + case let .limited(reactions): + label = "\(reactions.count)" + } + } else { + label = "" + } + items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemReactions, label: .text(label), text: presentationData.strings.PeerInfo_Reactions, icon: UIImage(bundleImageName: "Settings/Menu/Reactions"), action: { + interaction.editingOpenReactionsSetup() + })) + } + } else { + if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) { + let label: String + if let cachedData = data.cachedData as? CachedChannelData, case let .known(reactionSettings) = cachedData.reactionSettings { + switch reactionSettings.allowedReactions { + case .all: + label = presentationData.strings.PeerInfo_LabelAllReactions + case .empty: + label = presentationData.strings.PeerInfo_ReactionsDisabled + case let .limited(reactions): + label = "\(reactions.count)" + } + } else { + label = "" + } + items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemReactions, label: .text(label), text: presentationData.strings.PeerInfo_Reactions, icon: UIImage(bundleImageName: "Settings/Menu/Reactions"), action: { + interaction.editingOpenReactionsSetup() + })) + } + } + + if isCreator || channel.adminRights?.rights.contains(.canChangeInfo) == true { + var colors: [PeerNameColors.Colors] = [] + if let nameColor = channel.nameColor.flatMap({ context.peerNameColors.get($0, dark: presentationData.theme.overallDarkAppearance) }) { + colors.append(nameColor) + } + if let profileColor = channel.profileColor.flatMap({ context.peerNameColors.getProfile($0, dark: presentationData.theme.overallDarkAppearance, subject: .palette) }) { + colors.append(profileColor) + } + let colorImage = generateSettingsMenuPeerColorsLabelIcon(colors: colors) + + var boostIcon: UIImage? + if let approximateBoostLevel = channel.approximateBoostLevel, approximateBoostLevel < 1 { + boostIcon = generateDisclosureActionBoostLevelBadgeImage(text: presentationData.strings.Channel_Info_BoostLevelPlusBadge("1").string) + } else { + boostIcon = nil + /*let labelText = NSAttributedString(string: presentationData.strings.Settings_New, font: Font.medium(11.0), textColor: presentationData.theme.list.itemCheckColors.foregroundColor) + let labelBounds = labelText.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: [.usesLineFragmentOrigin], context: nil) + let labelSize = CGSize(width: ceil(labelBounds.width), height: ceil(labelBounds.height)) + let badgeSize = CGSize(width: labelSize.width + 8.0, height: labelSize.height + 2.0 + 1.0) + boostIcon = generateImage(badgeSize, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + let rect = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height - UIScreenPixel * 2.0)) + + context.addPath(UIBezierPath(roundedRect: rect, cornerRadius: 5.0).cgPath) + context.setFillColor(presentationData.theme.list.itemCheckColors.fillColor.cgColor) + context.fillPath() + + UIGraphicsPushContext(context) + labelText.draw(at: CGPoint(x: 4.0, y: 1.0 + UIScreenPixel)) + UIGraphicsPopContext() + })*/ + } + items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAppearance, label: .image(colorImage, colorImage.size), additionalBadgeIcon: boostIcon, text: presentationData.strings.Channel_Info_AppearanceItem, icon: UIImage(bundleImageName: "Chat/Info/NameColorIcon"), action: { + interaction.editingOpenNameColorSetup() + })) + } + + if (isCreator || (channel.adminRights != nil && channel.hasPermission(.banMembers))) && cachedData.peerGeoLocation == nil, !isPublic, case .known(nil) = cachedData.linkedDiscussionPeerId, !channel.isForumOrMonoForum { + items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPreHistory, label: .text(cachedData.flags.contains(.preHistoryEnabled) ? presentationData.strings.GroupInfo_GroupHistoryVisible : presentationData.strings.GroupInfo_GroupHistoryHidden), text: presentationData.strings.GroupInfo_GroupHistoryShort, icon: UIImage(bundleImageName: "Chat/Info/GroupDiscussionIcon"), action: { + interaction.editingOpenPreHistorySetup() + })) + } + + if isCreator, let appConfiguration = data.appConfiguration { + var minParticipants = 200 + if let data = appConfiguration.data, let value = data["forum_upgrade_participants_min"] as? Double { + minParticipants = Int(value) + } + + var canSetupTopics = false + var topicsLimitedReason: TopicsLimitedReason? + if channel.flags.contains(.isForum) { + canSetupTopics = true + } else if case let .known(value) = cachedData.linkedDiscussionPeerId, value != nil { + canSetupTopics = true + topicsLimitedReason = .discussion + } else if let memberCount = cachedData.participantsSummary.memberCount { + canSetupTopics = true + if Int(memberCount) < minParticipants { + topicsLimitedReason = .participants(minParticipants) + } + } + + if canSetupTopics { + let label = channel.flags.contains(.isForum) ? presentationData.strings.PeerInfo_OptionTopics_Enabled : presentationData.strings.PeerInfo_OptionTopics_Disabled + items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemTopics, label: .text(label), text: presentationData.strings.PeerInfo_OptionTopics, icon: UIImage(bundleImageName: "Settings/Menu/Topics"), action: { + if let topicsLimitedReason = topicsLimitedReason { + interaction.displayTopicsLimited(topicsLimitedReason) + } else { + interaction.openForumSettings() + } + })) + + items[.peerDataSettings]!.append(PeerInfoScreenCommentItem(id: ItemTopicsText, text: presentationData.strings.PeerInfo_OptionTopicsText)) + } + } + + var canViewAdminsAndBanned = false + if let _ = channel.adminRights { + canViewAdminsAndBanned = true + } else if channel.flags.contains(.isCreator) { + canViewAdminsAndBanned = true + } + + if canViewAdminsAndBanned { + var activePermissionCount: Int? + if let defaultBannedRights = channel.defaultBannedRights { + var count = 0 + for (right, _) in allGroupPermissionList(peer: .channel(channel), expandMedia: true) { + if right == .banSendMedia { + if banSendMediaSubList().allSatisfy({ !defaultBannedRights.flags.contains($0.0) }) { + count += 1 + } + } else { + if !defaultBannedRights.flags.contains(right) { + count += 1 + } + } + } + activePermissionCount = count + } + + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemMembers, label: .text(cachedData.participantsSummary.memberCount.flatMap { "\(presentationStringsFormattedNumber($0, presentationData.dateTimeFormat.groupingSeparator))" } ?? ""), text: presentationData.strings.Group_Info_Members, icon: UIImage(bundleImageName: "Chat/Info/GroupMembersIcon"), action: { + interaction.openParticipantsSection(.members) + })) + if !channel.flags.contains(.isGigagroup) { + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPermissions, label: .text(activePermissionCount.flatMap({ "\($0)/\(allGroupPermissionList(peer: .channel(channel), expandMedia: true).count)" }) ?? ""), text: presentationData.strings.GroupInfo_Permissions, icon: UIImage(bundleImageName: "Settings/Menu/SetPasscode"), action: { + interaction.openPermissions() + })) + } + + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAdmins, label: .text(cachedData.participantsSummary.adminCount.flatMap { "\(presentationStringsFormattedNumber($0, presentationData.dateTimeFormat.groupingSeparator))" } ?? ""), text: presentationData.strings.GroupInfo_Administrators, icon: UIImage(bundleImageName: "Chat/Info/GroupAdminsIcon"), action: { + interaction.openParticipantsSection(.admins) + })) + + if let count = data.requests?.count, count > 0 { + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemMemberRequests, label: .badge(presentationStringsFormattedNumber(count, presentationData.dateTimeFormat.groupingSeparator), presentationData.theme.list.itemAccentColor), text: presentationData.strings.GroupInfo_MemberRequests, icon: UIImage(bundleImageName: "Chat/Info/GroupRequestsIcon"), action: { + interaction.openParticipantsSection(.memberRequests) + })) + } + + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemRemovedUsers, label: .text(cachedData.participantsSummary.kickedCount.flatMap { $0 > 0 ? "\(presentationStringsFormattedNumber($0, presentationData.dateTimeFormat.groupingSeparator))" : "" } ?? ""), text: presentationData.strings.GroupInfo_Permissions_Removed, icon: UIImage(bundleImageName: "Chat/Info/GroupRemovedIcon"), action: { + interaction.openParticipantsSection(.banned) + })) + + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemRecentActions, label: .none, text: presentationData.strings.Group_Info_AdminLog, icon: UIImage(bundleImageName: "Chat/Info/RecentActionsIcon"), action: { + interaction.openRecentActions() + })) + } + + if isCreator { + items[.peerActions]!.append(PeerInfoScreenActionItem(id: ItemDeleteGroup, text: presentationData.strings.Group_DeleteGroup, color: .destructive, icon: nil, alignment: .natural, action: { + interaction.openDeletePeer() + })) + } + } + } + } else if let group = data.peer as? TelegramGroup { + let ItemUsername = 101 + let ItemInviteLinks = 102 + let ItemPreHistory = 103 + let ItemPermissions = 104 + let ItemAdmins = 105 + let ItemMemberRequests = 106 + let ItemReactions = 107 + let ItemTopics = 108 + let ItemTopicsText = 109 + + var canViewAdminsAndBanned = false + + if case .creator = group.role { + if let cachedData = data.cachedData as? CachedGroupData { + if cachedData.flags.contains(.canChangeUsername) { + items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemUsername, label: .text(presentationData.strings.Group_Setup_TypePrivate), text: presentationData.strings.GroupInfo_GroupType, icon: UIImage(bundleImageName: "Chat/Info/GroupTypeIcon"), action: { + interaction.editingOpenPublicLinkSetup() + })) + } + } + + if (group.addressName?.isEmpty ?? true) { + let invitesText: String + if let count = data.invitations?.count, count > 0 { + invitesText = "\(count)" + } else { + invitesText = "" + } + + items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, icon: UIImage(bundleImageName: "Chat/Info/GroupLinksIcon"), action: { + interaction.editingOpenInviteLinksSetup() + })) + } + + items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPreHistory, label: .text(presentationData.strings.GroupInfo_GroupHistoryHidden), text: presentationData.strings.GroupInfo_GroupHistoryShort, icon: UIImage(bundleImageName: "Chat/Info/GroupDiscussionIcon"), action: { + interaction.editingOpenPreHistorySetup() + })) + + var canSetupTopics = false + if case .creator = group.role { + canSetupTopics = true + } + var topicsLimitedReason: TopicsLimitedReason? + if let appConfiguration = data.appConfiguration { + var minParticipants = 200 + if let data = appConfiguration.data, let value = data["forum_upgrade_participants_min"] as? Double { + minParticipants = Int(value) + } + if Int(group.participantCount) < minParticipants { + topicsLimitedReason = .participants(minParticipants) + } + } + + if canSetupTopics { + items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemTopics, label: .text(presentationData.strings.PeerInfo_OptionTopics_Disabled), text: presentationData.strings.PeerInfo_OptionTopics, icon: UIImage(bundleImageName: "Settings/Menu/Topics"), action: { + if let topicsLimitedReason = topicsLimitedReason { + interaction.displayTopicsLimited(topicsLimitedReason) + } else { + interaction.openForumSettings() + } + })) + + items[.peerPublicSettings]!.append(PeerInfoScreenCommentItem(id: ItemTopicsText, text: presentationData.strings.PeerInfo_OptionTopicsText)) + } + + let label: String + if let cachedData = data.cachedData as? CachedGroupData, case let .known(reactionSettings) = cachedData.reactionSettings { + switch reactionSettings.allowedReactions { + case .all: + label = presentationData.strings.PeerInfo_LabelAllReactions + case .empty: + label = presentationData.strings.PeerInfo_ReactionsDisabled + case let .limited(reactions): + label = "\(reactions.count)" + } + } else { + label = "" + } + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemReactions, label: .text(label), text: presentationData.strings.PeerInfo_Reactions, icon: UIImage(bundleImageName: "Settings/Menu/Reactions"), action: { + interaction.editingOpenReactionsSetup() + })) + + canViewAdminsAndBanned = true + } else if case let .admin(rights, _) = group.role { + let label: String + if let cachedData = data.cachedData as? CachedGroupData, case let .known(reactionSettings) = cachedData.reactionSettings { + switch reactionSettings.allowedReactions { + case .all: + label = presentationData.strings.PeerInfo_LabelAllReactions + case .empty: + label = presentationData.strings.PeerInfo_ReactionsDisabled + case let .limited(reactions): + label = "\(reactions.count)" + } + } else { + label = "" + } + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemReactions, label: .text(label), text: presentationData.strings.PeerInfo_Reactions, icon: UIImage(bundleImageName: "Settings/Menu/Reactions"), action: { + interaction.editingOpenReactionsSetup() + })) + + if rights.rights.contains(.canInviteUsers) { + let invitesText: String + if let count = data.invitations?.count, count > 0 { + invitesText = "\(count)" + } else { + invitesText = "" + } + + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, icon: UIImage(bundleImageName: "Chat/Info/GroupLinksIcon"), action: { + interaction.editingOpenInviteLinksSetup() + })) + } + + canViewAdminsAndBanned = true + } + + if canViewAdminsAndBanned { + var activePermissionCount: Int? + if let defaultBannedRights = group.defaultBannedRights { + var count = 0 + for (right, _) in allGroupPermissionList(peer: .legacyGroup(group), expandMedia: true) { + if right == .banSendMedia { + if banSendMediaSubList().allSatisfy({ !defaultBannedRights.flags.contains($0.0) }) { + count += 1 + } + } else { + if !defaultBannedRights.flags.contains(right) { + count += 1 + } + } + } + activePermissionCount = count + } + + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPermissions, label: .text(activePermissionCount.flatMap({ "\($0)/\(allGroupPermissionList(peer: .legacyGroup(group), expandMedia: true).count)" }) ?? ""), text: presentationData.strings.GroupInfo_Permissions, icon: UIImage(bundleImageName: "Settings/Menu/SetPasscode"), action: { + interaction.openPermissions() + })) + + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAdmins, text: presentationData.strings.GroupInfo_Administrators, icon: UIImage(bundleImageName: "Chat/Info/GroupAdminsIcon"), action: { + interaction.openParticipantsSection(.admins) + })) + + if let count = data.requests?.count, count > 0 { + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemMemberRequests, label: .badge(presentationStringsFormattedNumber(count, presentationData.dateTimeFormat.groupingSeparator), presentationData.theme.list.itemAccentColor), text: presentationData.strings.GroupInfo_MemberRequests, icon: UIImage(bundleImageName: "Chat/Info/GroupRequestsIcon"), action: { + interaction.openParticipantsSection(.memberRequests) + })) + } + } + } + } + + var result: [(AnyHashable, [PeerInfoScreenItem])] = [] + for section in Section.allCases { + if let sectionItems = items[section], !sectionItems.isEmpty { + result.append((section, sectionItems)) + } + } + return result +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index ad318df6..379ed713 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -104,7 +104,6 @@ import AttachmentUI import BoostLevelIconComponent import PeerInfoChatPaneNode import PeerInfoChatListPaneNode -import GroupStickerPackSetupController import PeerNameColorItem import PeerSelectionScreen import UIKitRuntimeUtils @@ -124,386 +123,27 @@ public enum PeerInfoAvatarEditingMode { case fallback } -protocol PeerInfoScreenItem: AnyObject { - var id: AnyHashable { get } - func node() -> PeerInfoScreenItemNode -} - -class PeerInfoScreenItemNode: ASDisplayNode, AccessibilityFocusableNode { - var bringToFrontForHighlight: (() -> Void)? - - func update(context: AccountContext, width: CGFloat, safeInsets: UIEdgeInsets, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, hasCorners: Bool, transition: ContainedViewLayoutTransition) -> CGFloat { - preconditionFailure() - } - - override open func accessibilityElementDidBecomeFocused() { -// (self.supernode as? ListView)?.ensureItemNodeVisible(self, animated: false, overflow: 22.0) - } -} - -private final class PeerInfoScreenItemSectionContainerNode: ASDisplayNode { - private let backgroundNode: ASDisplayNode - private let topSeparatorNode: ASDisplayNode - private let bottomSeparatorNode: ASDisplayNode - private let itemContainerNode: ASDisplayNode - - private var currentItems: [PeerInfoScreenItem] = [] - fileprivate var itemNodes: [AnyHashable: PeerInfoScreenItemNode] = [:] - - override init() { - self.backgroundNode = ASDisplayNode() - self.backgroundNode.isLayerBacked = true - - self.topSeparatorNode = ASDisplayNode() - self.topSeparatorNode.isLayerBacked = true - - self.bottomSeparatorNode = ASDisplayNode() - self.bottomSeparatorNode.isLayerBacked = true - - self.itemContainerNode = ASDisplayNode() - self.itemContainerNode.clipsToBounds = true - - super.init() - - self.addSubnode(self.backgroundNode) - self.addSubnode(self.itemContainerNode) - self.addSubnode(self.topSeparatorNode) - self.addSubnode(self.bottomSeparatorNode) - } - - func update(context: AccountContext, width: CGFloat, safeInsets: UIEdgeInsets, hasCorners: Bool, presentationData: PresentationData, items: [PeerInfoScreenItem], transition: ContainedViewLayoutTransition) -> CGFloat { - self.backgroundNode.backgroundColor = presentationData.theme.list.itemBlocksBackgroundColor - self.topSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor - self.bottomSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor - - self.topSeparatorNode.isHidden = hasCorners - self.bottomSeparatorNode.isHidden = hasCorners - - var contentHeight: CGFloat = 0.0 - var contentWithBackgroundHeight: CGFloat = 0.0 - var contentWithBackgroundOffset: CGFloat = 0.0 - - for i in 0 ..< items.count { - let item = items[i] - - let itemNode: PeerInfoScreenItemNode - var wasAdded = false - if let current = self.itemNodes[item.id] { - itemNode = current - } else { - wasAdded = true - itemNode = item.node() - self.itemNodes[item.id] = itemNode - self.itemContainerNode.addSubnode(itemNode) - itemNode.bringToFrontForHighlight = { [weak self, weak itemNode] in - guard let strongSelf = self, let itemNode = itemNode else { - return - } - strongSelf.view.bringSubviewToFront(itemNode.view) - } - } - - let itemTransition: ContainedViewLayoutTransition = wasAdded ? .immediate : transition - - let topItem: PeerInfoScreenItem? - if i == 0 { - topItem = nil - } else if items[i - 1] is PeerInfoScreenHeaderItem { - topItem = nil - } else { - topItem = items[i - 1] - } - - let bottomItem: PeerInfoScreenItem? - if i == items.count - 1 { - bottomItem = nil - } else if items[i + 1] is PeerInfoScreenCommentItem { - bottomItem = nil - } else { - bottomItem = items[i + 1] - } - - let itemHeight = itemNode.update(context: context, width: width, safeInsets: safeInsets, presentationData: presentationData, item: item, topItem: topItem, bottomItem: bottomItem, hasCorners: hasCorners, transition: itemTransition) - let itemFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: width, height: itemHeight)) - itemTransition.updateFrame(node: itemNode, frame: itemFrame) - if wasAdded { - itemNode.alpha = 0.0 - let alphaTransition: ContainedViewLayoutTransition = transition.isAnimated ? .animated(duration: 0.35, curve: .linear) : .immediate - alphaTransition.updateAlpha(node: itemNode, alpha: 1.0) - } - - if item is PeerInfoScreenCommentItem { - } else { - contentWithBackgroundHeight += itemHeight - } - contentHeight += itemHeight - - if item is PeerInfoScreenHeaderItem { - contentWithBackgroundOffset = contentHeight - } - } - - var removeIds: [AnyHashable] = [] - for (id, _) in self.itemNodes { - if !items.contains(where: { $0.id == id }) { - removeIds.append(id) - } - } - for id in removeIds { - if let itemNode = self.itemNodes.removeValue(forKey: id) { - itemNode.view.superview?.sendSubviewToBack(itemNode.view) - transition.updateAlpha(node: itemNode, alpha: 0.0, completion: { [weak itemNode] _ in - itemNode?.removeFromSupernode() - }) - } - } - - transition.updateFrame(node: self.itemContainerNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: contentHeight))) - transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: contentWithBackgroundOffset), size: CGSize(width: width, height: max(0.0, contentWithBackgroundHeight - contentWithBackgroundOffset)))) - transition.updateFrame(node: self.topSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: contentWithBackgroundOffset - UIScreenPixel), size: CGSize(width: width, height: UIScreenPixel))) - transition.updateFrame(node: self.bottomSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: contentWithBackgroundHeight), size: CGSize(width: width, height: UIScreenPixel))) - - if contentHeight.isZero { - transition.updateAlpha(node: self.topSeparatorNode, alpha: 0.0) - transition.updateAlpha(node: self.bottomSeparatorNode, alpha: 0.0) - } else { - transition.updateAlpha(node: self.topSeparatorNode, alpha: 1.0) - transition.updateAlpha(node: self.bottomSeparatorNode, alpha: 1.0) - } - - return contentHeight - } - - func animateErrorIfNeeded() { - for (_, itemNode) in self.itemNodes { - if let itemNode = itemNode as? PeerInfoScreenMultilineInputItemNode { - itemNode.animateErrorIfNeeded() - } - } - } -} - -final class PeerInfoSelectionPanelNode: ASDisplayNode { - private let context: AccountContext - private let peerId: PeerId - - private let deleteMessages: () -> Void - private let shareMessages: () -> Void - private let forwardMessages: () -> Void - private let reportMessages: () -> Void - private let displayCopyProtectionTip: (UIView, Bool) -> Void - - let selectionPanel: ChatMessageSelectionInputPanelNode - let separatorNode: ASDisplayNode - let backgroundNode: NavigationBackgroundNode - - var viewForOverlayContent: UIView? { - return self.selectionPanel.viewForOverlayContent - } - - init(context: AccountContext, presentationData: PresentationData, peerId: PeerId, deleteMessages: @escaping () -> Void, shareMessages: @escaping () -> Void, forwardMessages: @escaping () -> Void, reportMessages: @escaping () -> Void, displayCopyProtectionTip: @escaping (UIView, Bool) -> Void) { - self.context = context - self.peerId = peerId - self.deleteMessages = deleteMessages - self.shareMessages = shareMessages - self.forwardMessages = forwardMessages - self.reportMessages = reportMessages - self.displayCopyProtectionTip = displayCopyProtectionTip - - let presentationData = presentationData - - self.separatorNode = ASDisplayNode() - self.backgroundNode = NavigationBackgroundNode(color: presentationData.theme.rootController.navigationBar.blurredBackgroundColor) - - self.selectionPanel = ChatMessageSelectionInputPanelNode(theme: presentationData.theme, strings: presentationData.strings, peerMedia: true) - self.selectionPanel.context = context - - let interfaceInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { _, _, _ in - }, setupEditMessage: { _, _ in - }, beginMessageSelection: { _, _ in - }, cancelMessageSelection: { _ in - }, deleteSelectedMessages: { - deleteMessages() - }, reportSelectedMessages: { - reportMessages() - }, reportMessages: { _, _ in - }, blockMessageAuthor: { _, _ in - }, deleteMessages: { _, _, f in - f(.default) - }, forwardSelectedMessages: { - forwardMessages() - }, forwardCurrentForwardMessages: { - }, forwardMessages: { _ in - }, updateForwardOptionsState: { _ in - }, presentForwardOptions: { _ in - }, presentReplyOptions: { _ in - }, presentLinkOptions: { _ in - }, presentSuggestPostOptions: { - }, shareSelectedMessages: { - shareMessages() - }, updateTextInputStateAndMode: { _ in - }, updateInputModeAndDismissedButtonKeyboardMessageId: { _ in - }, openStickers: { - }, editMessage: { - }, beginMessageSearch: { _, _ in - }, dismissMessageSearch: { - }, updateMessageSearch: { _ in - }, openSearchResults: { - }, navigateMessageSearch: { _ in - }, openCalendarSearch: { - }, toggleMembersSearch: { _ in - }, navigateToMessage: { _, _, _, _ in - }, navigateToChat: { _ in - }, navigateToProfile: { _ in - }, openPeerInfo: { - }, togglePeerNotifications: { - }, sendContextResult: { _, _, _, _ in - return false - }, sendBotCommand: { _, _ in - }, sendShortcut: { _ in - }, openEditShortcuts: { - }, sendBotStart: { _ in - }, botSwitchChatWithPayload: { _, _ in - }, beginMediaRecording: { _ in - }, finishMediaRecording: { _ in - }, stopMediaRecording: { - }, lockMediaRecording: { - }, resumeMediaRecording: { - }, deleteRecordedMedia: { - }, sendRecordedMedia: { _, _ in - }, displayRestrictedInfo: { _, _ in - }, displayVideoUnmuteTip: { _ in - }, switchMediaRecordingMode: { - }, setupMessageAutoremoveTimeout: { - }, sendSticker: { _, _, _, _, _, _ in - return false - }, unblockPeer: { - }, pinMessage: { _, _ in - }, unpinMessage: { _, _, _ in - }, unpinAllMessages: { - }, openPinnedList: { _ in - }, shareAccountContact: { - }, reportPeer: { - }, presentPeerContact: { - }, dismissReportPeer: { - }, deleteChat: { - }, beginCall: { _ in - }, toggleMessageStickerStarred: { _ in - }, presentController: { _, _ in - }, presentControllerInCurrent: { _, _ in - }, getNavigationController: { - return nil - }, presentGlobalOverlayController: { _, _ in - }, navigateFeed: { - }, openGrouping: { - }, toggleSilentPost: { - }, requestUnvoteInMessage: { _ in - }, requestStopPollInMessage: { _ in - }, updateInputLanguage: { _ in - }, unarchiveChat: { - }, openLinkEditing: { - }, displaySlowmodeTooltip: { _, _ in - }, displaySendMessageOptions: { _, _ in - }, openScheduledMessages: { - }, openPeersNearby: { - }, displaySearchResultsTooltip: { _, _ in - }, unarchivePeer: { - }, scrollToTop: { - }, viewReplies: { _, _ in - }, activatePinnedListPreview: { _, _ in - }, joinGroupCall: { _ in - }, presentInviteMembers: { - }, presentGigagroupHelp: { - }, openMonoforum: { - }, editMessageMedia: { _, _ in - }, updateShowCommands: { _ in - }, updateShowSendAsPeers: { _ in - }, openInviteRequests: { - }, openSendAsPeer: { _, _ in - }, presentChatRequestAdminInfo: { - }, displayCopyProtectionTip: { view, save in - displayCopyProtectionTip(view, save) - }, openWebView: { _, _, _, _ in - }, updateShowWebView: { _ in - }, insertText: { _ in - }, backwardsDeleteText: { - }, restartTopic: { - }, toggleTranslation: { _ in - }, changeTranslationLanguage: { _ in - }, addDoNotTranslateLanguage: { _ in - }, hideTranslationPanel: { - }, openPremiumGift: { - }, openSuggestPost: { _, _ in - }, openPremiumRequiredForMessaging: { - }, openStarsPurchase: { _ in - }, openMessagePayment: { - }, openBoostToUnrestrict: { - }, updateRecordingTrimRange: { _, _, _, _ in - }, dismissAllTooltips: { - }, editTodoMessage: { _, _, _ in - }, dismissUrlPreview: { - }, dismissForwardMessages: { - }, dismissSuggestPost: { - }, displayUndo: { _ in - }, sendEmoji: { _, _, _ in - }, updateHistoryFilter: { _ in - }, updateChatLocationThread: { _, _ in - }, toggleChatSidebarMode: { - }, updateDisplayHistoryFilterAsList: { _ in - }, requestLayout: { _ in - }, chatController: { - return nil - }, statuses: nil) - - self.selectionPanel.interfaceInteraction = interfaceInteraction - - super.init() - - self.addSubnode(self.backgroundNode) - self.addSubnode(self.separatorNode) - self.addSubnode(self.selectionPanel) - } - - func update(layout: ContainerViewLayout, presentationData: PresentationData, transition: ContainedViewLayoutTransition) -> CGFloat { - self.backgroundNode.updateColor(color: presentationData.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate) - self.separatorNode.backgroundColor = presentationData.theme.rootController.navigationBar.separatorColor - - let interfaceState = ChatPresentationInterfaceState(chatWallpaper: .color(0), theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, limitsConfiguration: .defaultValue, fontSize: .regular, bubbleCorners: PresentationChatBubbleCorners(mainRadius: 16.0, auxiliaryRadius: 8.0, mergeBubbleCorners: true), accountPeerId: self.context.account.peerId, mode: .standard(.default), chatLocation: .peer(id: self.peerId), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil, businessIntro: nil) - let panelHeight = self.selectionPanel.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: layout.intrinsicInsets.bottom, additionalSideInsets: UIEdgeInsets(), maxHeight: layout.size.height, maxOverlayHeight: layout.size.height, isSecondary: false, transition: transition, interfaceState: interfaceState, metrics: layout.metrics, isMediaInputExpanded: false) - - transition.updateFrame(node: self.selectionPanel, frame: CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: panelHeight))) - - let panelHeightWithInset = panelHeight + layout.intrinsicInsets.bottom - - transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: panelHeightWithInset))) - self.backgroundNode.update(size: self.backgroundNode.bounds.size, transition: transition) - transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: layout.size.width, height: UIScreenPixel))) - - return panelHeightWithInset - } -} - -private enum PeerInfoBotCommand { +enum PeerInfoBotCommand { case settings case help case privacy } -private enum PeerInfoParticipantsSection { +enum PeerInfoParticipantsSection { case members case admins case banned case memberRequests } -private enum PeerInfoMemberAction { +enum PeerInfoMemberAction { case promote case restrict case remove case openStories(sourceView: UIView) } -private enum PeerInfoContextSubject { +enum PeerInfoContextSubject { case bio case phone(String) case link(customLink: String?) @@ -512,7 +152,7 @@ private enum PeerInfoContextSubject { case birthday } -private enum PeerInfoSettingsSection { +enum PeerInfoSettingsSection { case avatar case edit case proxy @@ -548,2514 +188,82 @@ private enum PeerInfoSettingsSection { case premiumManagement case stars case ton + case ghostgram } -private enum PeerInfoReportType { +enum PeerInfoReportType { case `default` case user case reaction(MessageId) } -private enum TopicsLimitedReason { +enum TopicsLimitedReason { case participants(Int) case discussion } -private final class PeerInfoInteraction { - let openChat: (EnginePeer.Id?) -> Void - let openUsername: (String, Bool, Promise?) -> Void - let openPhone: (String, ASDisplayNode, ContextGesture?, Promise?) -> Void - let editingOpenNotificationSettings: () -> Void - let editingOpenSoundSettings: () -> Void - let editingToggleShowMessageText: (Bool) -> Void - let requestDeleteContact: () -> Void - let suggestBirthdate: () -> Void - let suggestPhoto: () -> Void - let setCustomPhoto: () -> Void - let resetCustomPhoto: () -> Void - let openAddContact: () -> Void - let updateBlocked: (Bool) -> Void - let openReport: (PeerInfoReportType) -> Void - let openShareBot: () -> Void - let openAddBotToGroup: () -> Void - let performBotCommand: (PeerInfoBotCommand) -> Void - let editingOpenPublicLinkSetup: () -> Void - let editingOpenNameColorSetup: () -> Void - let editingOpenInviteLinksSetup: () -> Void - let editingOpenDiscussionGroupSetup: () -> Void - let editingOpenPostSuggestionsSetup: () -> Void - let editingOpenRevenue: () -> Void - let editingOpenStars: () -> Void - let openParticipantsSection: (PeerInfoParticipantsSection) -> Void - let openRecentActions: () -> Void - let openChannelMessages: () -> Void - let openStats: (ChannelStatsSection) -> Void - let editingOpenPreHistorySetup: () -> Void - let editingOpenAutoremoveMesages: () -> Void - let openPermissions: () -> Void - let editingOpenStickerPackSetup: () -> Void - let openLocation: () -> Void - let editingOpenSetupLocation: () -> Void - let openPeerInfo: (Peer, Bool) -> Void - let performMemberAction: (PeerInfoMember, PeerInfoMemberAction) -> Void - let openPeerInfoContextMenu: (PeerInfoContextSubject, ASDisplayNode, CGRect?) -> Void - let performBioLinkAction: (TextLinkItemActionType, TextLinkItem) -> Void - let requestLayout: (Bool) -> Void - let openEncryptionKey: () -> Void - let openSettings: (PeerInfoSettingsSection) -> Void - let openPaymentMethod: () -> Void - let switchToAccount: (AccountRecordId) -> Void - let logoutAccount: (AccountRecordId) -> Void - let accountContextMenu: (AccountRecordId, ASDisplayNode, ContextGesture?) -> Void - let updateBio: (String) -> Void - let updateNote: (NSAttributedString) -> Void - let openDeletePeer: () -> Void - let openFaq: (String?) -> Void - let openAddMember: () -> Void - let openQrCode: () -> Void - let editingOpenReactionsSetup: () -> Void - let dismissInput: () -> Void - let openForumSettings: () -> Void - let displayTopicsLimited: (TopicsLimitedReason) -> Void - let openPeerMention: (String, ChatControllerInteractionNavigateToPeer) -> Void - let openBotApp: (AttachMenuBot) -> Void - let openEditing: () -> Void - let updateBirthdate: (TelegramBirthday??) -> Void - let updateIsEditingBirthdate: (Bool) -> Void - let openBioPrivacy: () -> Void - let openBirthdatePrivacy: () -> Void - let openPremiumGift: () -> Void - let editingOpenPersonalChannel: () -> Void - let openUsernameContextMenu: (ASDisplayNode, ContextGesture?) -> Void - let openBioContextMenu: (ASDisplayNode, ContextGesture?) -> Void - let openNoteContextMenu: (ASDisplayNode, ContextGesture?) -> Void - let openWorkingHoursContextMenu: (ASDisplayNode, ContextGesture?) -> Void - let openBusinessLocationContextMenu: (ASDisplayNode, ContextGesture?) -> Void - let openBirthdayContextMenu: (ASDisplayNode, ContextGesture?) -> Void - let editingOpenAffiliateProgram: () -> Void - let editingOpenVerifyAccounts: () -> Void - let editingToggleAutoTranslate: (Bool) -> Void - let displayAutoTranslateLocked: () -> Void - let getController: () -> ViewController? - - init( - openUsername: @escaping (String, Bool, Promise?) -> Void, - openPhone: @escaping (String, ASDisplayNode, ContextGesture?, Promise?) -> Void, - editingOpenNotificationSettings: @escaping () -> Void, - editingOpenSoundSettings: @escaping () -> Void, - editingToggleShowMessageText: @escaping (Bool) -> Void, - requestDeleteContact: @escaping () -> Void, - suggestBirthdate: @escaping () -> Void, - suggestPhoto: @escaping () -> Void, - setCustomPhoto: @escaping () -> Void, - resetCustomPhoto: @escaping () -> Void, - openChat: @escaping (EnginePeer.Id?) -> Void, - openAddContact: @escaping () -> Void, - updateBlocked: @escaping (Bool) -> Void, - openReport: @escaping (PeerInfoReportType) -> Void, - openShareBot: @escaping () -> Void, - openAddBotToGroup: @escaping () -> Void, - performBotCommand: @escaping (PeerInfoBotCommand) -> Void, - editingOpenPublicLinkSetup: @escaping () -> Void, - editingOpenNameColorSetup: @escaping () -> Void, - editingOpenInviteLinksSetup: @escaping () -> Void, - editingOpenDiscussionGroupSetup: @escaping () -> Void, - editingOpenPostSuggestionsSetup: @escaping () -> Void, - editingOpenRevenue: @escaping () -> Void, - editingOpenStars: @escaping () -> Void, - openParticipantsSection: @escaping (PeerInfoParticipantsSection) -> Void, - openRecentActions: @escaping () -> Void, - openChannelMessages: @escaping () -> Void, - openStats: @escaping (ChannelStatsSection) -> Void, - editingOpenPreHistorySetup: @escaping () -> Void, - editingOpenAutoremoveMesages: @escaping () -> Void, - openPermissions: @escaping () -> Void, - editingOpenStickerPackSetup: @escaping () -> Void, - openLocation: @escaping () -> Void, - editingOpenSetupLocation: @escaping () -> Void, - openPeerInfo: @escaping (Peer, Bool) -> Void, - performMemberAction: @escaping (PeerInfoMember, PeerInfoMemberAction) -> Void, - openPeerInfoContextMenu: @escaping (PeerInfoContextSubject, ASDisplayNode, CGRect?) -> Void, - performBioLinkAction: @escaping (TextLinkItemActionType, TextLinkItem) -> Void, - requestLayout: @escaping (Bool) -> Void, - openEncryptionKey: @escaping () -> Void, - openSettings: @escaping (PeerInfoSettingsSection) -> Void, - openPaymentMethod: @escaping () -> Void, - switchToAccount: @escaping (AccountRecordId) -> Void, - logoutAccount: @escaping (AccountRecordId) -> Void, - accountContextMenu: @escaping (AccountRecordId, ASDisplayNode, ContextGesture?) -> Void, - updateBio: @escaping (String) -> Void, - updateNote: @escaping (NSAttributedString) -> Void, - openDeletePeer: @escaping () -> Void, - openFaq: @escaping (String?) -> Void, - openAddMember: @escaping () -> Void, - openQrCode: @escaping () -> Void, - editingOpenReactionsSetup: @escaping () -> Void, - dismissInput: @escaping () -> Void, - openForumSettings: @escaping () -> Void, - displayTopicsLimited: @escaping (TopicsLimitedReason) -> Void, - openPeerMention: @escaping (String, ChatControllerInteractionNavigateToPeer) -> Void, - openBotApp: @escaping (AttachMenuBot) -> Void, - openEditing: @escaping () -> Void, - updateBirthdate: @escaping (TelegramBirthday??) -> Void, - updateIsEditingBirthdate: @escaping (Bool) -> Void, - openBioPrivacy: @escaping () -> Void, - openBirthdatePrivacy: @escaping () -> Void, - openPremiumGift: @escaping () -> Void, - editingOpenPersonalChannel: @escaping () -> Void, - openUsernameContextMenu: @escaping (ASDisplayNode, ContextGesture?) -> Void, - openBioContextMenu: @escaping (ASDisplayNode, ContextGesture?) -> Void, - openNoteContextMenu: @escaping (ASDisplayNode, ContextGesture?) -> Void, - openWorkingHoursContextMenu: @escaping (ASDisplayNode, ContextGesture?) -> Void, - openBusinessLocationContextMenu: @escaping (ASDisplayNode, ContextGesture?) -> Void, - openBirthdayContextMenu: @escaping (ASDisplayNode, ContextGesture?) -> Void, - editingOpenAffiliateProgram: @escaping () -> Void, - editingOpenVerifyAccounts: @escaping () -> Void, - editingToggleAutoTranslate: @escaping (Bool) -> Void, - displayAutoTranslateLocked: @escaping () -> Void, - getController: @escaping () -> ViewController? - ) { - self.openUsername = openUsername - self.openPhone = openPhone - self.editingOpenNotificationSettings = editingOpenNotificationSettings - self.editingOpenSoundSettings = editingOpenSoundSettings - self.editingToggleShowMessageText = editingToggleShowMessageText - self.requestDeleteContact = requestDeleteContact - self.suggestBirthdate = suggestBirthdate - self.suggestPhoto = suggestPhoto - self.setCustomPhoto = setCustomPhoto - self.resetCustomPhoto = resetCustomPhoto - self.openChat = openChat - self.openAddContact = openAddContact - self.updateBlocked = updateBlocked - self.openReport = openReport - self.openShareBot = openShareBot - self.openAddBotToGroup = openAddBotToGroup - self.performBotCommand = performBotCommand - self.editingOpenPublicLinkSetup = editingOpenPublicLinkSetup - self.editingOpenNameColorSetup = editingOpenNameColorSetup - self.editingOpenInviteLinksSetup = editingOpenInviteLinksSetup - self.editingOpenDiscussionGroupSetup = editingOpenDiscussionGroupSetup - self.editingOpenPostSuggestionsSetup = editingOpenPostSuggestionsSetup - self.editingOpenRevenue = editingOpenRevenue - self.editingOpenStars = editingOpenStars - self.openParticipantsSection = openParticipantsSection - self.openRecentActions = openRecentActions - self.openChannelMessages = openChannelMessages - self.openStats = openStats - self.editingOpenPreHistorySetup = editingOpenPreHistorySetup - self.editingOpenAutoremoveMesages = editingOpenAutoremoveMesages - self.openPermissions = openPermissions - self.editingOpenStickerPackSetup = editingOpenStickerPackSetup - self.openLocation = openLocation - self.editingOpenSetupLocation = editingOpenSetupLocation - self.openPeerInfo = openPeerInfo - self.performMemberAction = performMemberAction - self.openPeerInfoContextMenu = openPeerInfoContextMenu - self.performBioLinkAction = performBioLinkAction - self.requestLayout = requestLayout - self.openEncryptionKey = openEncryptionKey - self.openSettings = openSettings - self.openPaymentMethod = openPaymentMethod - self.switchToAccount = switchToAccount - self.logoutAccount = logoutAccount - self.accountContextMenu = accountContextMenu - self.updateBio = updateBio - self.updateNote = updateNote - self.openDeletePeer = openDeletePeer - self.openFaq = openFaq - self.openAddMember = openAddMember - self.openQrCode = openQrCode - self.editingOpenReactionsSetup = editingOpenReactionsSetup - self.dismissInput = dismissInput - self.openForumSettings = openForumSettings - self.displayTopicsLimited = displayTopicsLimited - self.openPeerMention = openPeerMention - self.openBotApp = openBotApp - self.openEditing = openEditing - self.updateBirthdate = updateBirthdate - self.updateIsEditingBirthdate = updateIsEditingBirthdate - self.openBioPrivacy = openBioPrivacy - self.openBirthdatePrivacy = openBirthdatePrivacy - self.openPremiumGift = openPremiumGift - self.editingOpenPersonalChannel = editingOpenPersonalChannel - self.openUsernameContextMenu = openUsernameContextMenu - self.openBioContextMenu = openBioContextMenu - self.openNoteContextMenu = openNoteContextMenu - self.openWorkingHoursContextMenu = openWorkingHoursContextMenu - self.openBusinessLocationContextMenu = openBusinessLocationContextMenu - self.openBirthdayContextMenu = openBirthdayContextMenu - self.editingOpenAffiliateProgram = editingOpenAffiliateProgram - self.editingOpenVerifyAccounts = editingOpenVerifyAccounts - self.editingToggleAutoTranslate = editingToggleAutoTranslate - self.displayAutoTranslateLocked = displayAutoTranslateLocked - self.getController = getController - } -} - -private let enabledPublicBioEntities: EnabledEntityTypes = [.allUrl, .mention, .hashtag] -private let enabledPrivateBioEntities: EnabledEntityTypes = [.internalUrl, .mention, .hashtag] - -private enum SettingsSection: Int, CaseIterable { - case edit - case phone - case accounts - case myProfile - case proxy - case apps - case shortcuts - case advanced - case payment - case extra - case support -} - -private func settingsItems(data: PeerInfoScreenData?, context: AccountContext, presentationData: PresentationData, interaction: PeerInfoInteraction, isExpanded: Bool) -> [(AnyHashable, [PeerInfoScreenItem])] { - guard let data = data else { - return [] - } - - var items: [SettingsSection: [PeerInfoScreenItem]] = [:] - for section in SettingsSection.allCases { - items[section] = [] - } - - let setPhotoTitle: String - if let peer = data.peer, !peer.profileImageRepresentations.isEmpty { - setPhotoTitle = presentationData.strings.Settings_ChangeProfilePhoto - } else { - setPhotoTitle = presentationData.strings.Settings_SetProfilePhotoOrVideo - } - - var setStatusTitle: String = "" - let displaySetStatus: Bool - var hasEmojiStatus = false - if let peer = data.peer as? TelegramUser, peer.isPremium { - if peer.emojiStatus != nil { - hasEmojiStatus = true - setStatusTitle = presentationData.strings.PeerInfo_ChangeEmojiStatus - } else { - setStatusTitle = presentationData.strings.PeerInfo_SetEmojiStatus - } - displaySetStatus = true - } else { - displaySetStatus = false - } - - if displaySetStatus { - items[.edit]!.append(PeerInfoScreenActionItem(id: 0, text: setStatusTitle, icon: UIImage(bundleImageName: hasEmojiStatus ? "Settings/EditEmojiStatus" : "Settings/SetEmojiStatus"), action: { - interaction.openSettings(.emojiStatus) - })) - - items[.edit]!.append(PeerInfoScreenActionItem(id: 1, text: presentationData.strings.PeerInfo_ChangeProfileColor, icon: UIImage(bundleImageName: "Premium/BoostPerk/CoverColor"), action: { - interaction.openSettings(.profileColor) - })) - } - - items[.edit]!.append(PeerInfoScreenActionItem(id: 2, text: setPhotoTitle, icon: UIImage(bundleImageName: "Settings/SetAvatar"), action: { - interaction.openSettings(.avatar) - })) - - if let peer = data.peer, (peer.addressName ?? "").isEmpty { - items[.edit]!.append(PeerInfoScreenActionItem(id: 3, text: presentationData.strings.Settings_SetUsername, icon: UIImage(bundleImageName: "Settings/SetUsername"), action: { - interaction.openSettings(.username) - })) - } - - if let settings = data.globalSettings { - if settings.premiumGracePeriod { - items[.phone]!.append(PeerInfoScreenInfoItem(id: 0, title: "Your access to Telegram Premium will expire soon!", text: .markdown("Unfortunately, your latest payment didn't come through. To keep your access to exclusive features, please renew the subscription."), isWarning: true, linkAction: nil)) - items[.phone]!.append(PeerInfoScreenActionItem(id: 1, text: "Restore Subscription", action: { - interaction.openSettings(.premiumManagement) - })) - } else if settings.suggestPhoneNumberConfirmation, let peer = data.peer as? TelegramUser { - let phoneNumber = formatPhoneNumber(context: context, number: peer.phone ?? "") - items[.phone]!.append(PeerInfoScreenInfoItem(id: 0, title: presentationData.strings.Settings_CheckPhoneNumberTitle(phoneNumber).string, text: .markdown(presentationData.strings.Settings_CheckPhoneNumberText), linkAction: { link in - if case .tap = link { - interaction.openFaq(presentationData.strings.Settings_CheckPhoneNumberFAQAnchor) - } - })) - items[.phone]!.append(PeerInfoScreenActionItem(id: 1, text: presentationData.strings.Settings_KeepPhoneNumber(phoneNumber).string, action: { - let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.validatePhoneNumber.id).startStandalone() - })) - items[.phone]!.append(PeerInfoScreenActionItem(id: 2, text: presentationData.strings.Settings_ChangePhoneNumber, action: { - interaction.openSettings(.phoneNumber) - })) - } else if settings.suggestPasswordConfirmation { - items[.phone]!.append(PeerInfoScreenInfoItem(id: 0, title: presentationData.strings.Settings_CheckPasswordTitle, text: .markdown(presentationData.strings.Settings_CheckPasswordText), linkAction: { _ in - })) - items[.phone]!.append(PeerInfoScreenActionItem(id: 1, text: presentationData.strings.Settings_KeepPassword, action: { - let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.validatePassword.id).startStandalone() - })) - items[.phone]!.append(PeerInfoScreenActionItem(id: 2, text: presentationData.strings.Settings_TryEnterPassword, action: { - interaction.openSettings(.rememberPassword) - })) - } else if settings.suggestPasswordSetup { - items[.phone]!.append(PeerInfoScreenInfoItem(id: 0, title: presentationData.strings.Settings_SuggestSetupPasswordTitle, text: .markdown(presentationData.strings.Settings_SuggestSetupPasswordText), linkAction: { _ in - })) - items[.phone]!.append(PeerInfoScreenActionItem(id: 2, text: presentationData.strings.Settings_SuggestSetupPasswordAction, action: { - interaction.openSettings(.passwordSetup) - })) - } - - if !settings.accountsAndPeers.isEmpty { - for (peerAccountContext, peer, badgeCount) in settings.accountsAndPeers { - let mappedContext = ItemListPeerItem.Context.custom(ItemListPeerItem.Context.Custom( - accountPeerId: peerAccountContext.account.peerId, - postbox: peerAccountContext.account.postbox, - network: peerAccountContext.account.network, - animationCache: context.animationCache, - animationRenderer: context.animationRenderer, - isPremiumDisabled: false, - resolveInlineStickers: { fileIds in - return context.engine.stickers.resolveInlineStickers(fileIds: fileIds) - } - )) - let member: PeerInfoMember = .account(peer: RenderedPeer(peer: peer._asPeer())) - items[.accounts]!.append(PeerInfoScreenMemberItem(id: member.id, context: mappedContext, enclosingPeer: nil, member: member, badge: badgeCount > 0 ? "\(compactNumericCountString(Int(badgeCount), decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))" : nil, isAccount: true, action: { action in - switch action { - case .open: - interaction.switchToAccount(peerAccountContext.account.id) - case .remove: - interaction.logoutAccount(peerAccountContext.account.id) - default: - break - } - }, contextAction: { node, gesture in - interaction.accountContextMenu(peerAccountContext.account.id, node, gesture) - })) - } - - items[.accounts]!.append(PeerInfoScreenActionItem(id: 100, text: presentationData.strings.Settings_AddAccount, icon: PresentationResourcesItemList.plusIconImage(presentationData.theme), action: { - interaction.openSettings(.addAccount) - })) - } - - items[.myProfile]!.append(PeerInfoScreenDisclosureItem(id: 0, text: presentationData.strings.Settings_MyProfile, icon: PresentationResourcesSettings.myProfile, action: { - interaction.openSettings(.profile) - })) - - if !settings.proxySettings.servers.isEmpty { - let proxyType: String - if settings.proxySettings.enabled, let activeServer = settings.proxySettings.activeServer { - switch activeServer.connection { - case .mtp: - proxyType = presentationData.strings.SocksProxySetup_ProxyTelegram - case .socks5: - proxyType = presentationData.strings.SocksProxySetup_ProxySocks5 - } - } else { - proxyType = presentationData.strings.Settings_ProxyDisabled - } - items[.proxy]!.append(PeerInfoScreenDisclosureItem(id: 0, label: .text(proxyType), text: presentationData.strings.Settings_Proxy, icon: PresentationResourcesSettings.proxy, action: { - interaction.openSettings(.proxy) - })) - } - } - - var appIndex = 1000 - if let settings = data.globalSettings { - for bot in settings.bots { - let iconSignal: Signal - if let peer = PeerReference(bot.peer._asPeer()), let icon = bot.icons[.iOSSettingsStatic] { - let fileReference: FileMediaReference = .attachBot(peer: peer, media: icon) - iconSignal = instantPageImageFile(account: context.account, userLocation: .other, fileReference: fileReference, fetched: true) - |> map { generator -> UIImage? in - let size = CGSize(width: 29.0, height: 29.0) - let context = generator(TransformImageArguments(corners: ImageCorners(), imageSize: size, boundingSize: size, intrinsicInsets: .zero)) - return context?.generateImage() - } - let _ = freeMediaFileInteractiveFetched(account: context.account, userLocation: .other, fileReference: fileReference).startStandalone() - } else { - iconSignal = .single(UIImage()) - } - let label: PeerInfoScreenDisclosureItem.Label = bot.flags.contains(.notActivated) || bot.flags.contains(.showInSettingsDisclaimer) ? .titleBadge(presentationData.strings.Settings_New, presentationData.theme.list.itemAccentColor) : .none - items[.apps]!.append(PeerInfoScreenDisclosureItem(id: bot.peer.id.id._internalGetInt64Value(), label: label, text: bot.shortName, icon: nil, iconSignal: iconSignal, action: { - interaction.openBotApp(bot) - })) - appIndex += 1 - } - } - - items[.shortcuts]!.append(PeerInfoScreenDisclosureItem(id: 1, text: presentationData.strings.Settings_SavedMessages, icon: PresentationResourcesSettings.savedMessages, action: { - interaction.openSettings(.savedMessages) - })) - items[.shortcuts]!.append(PeerInfoScreenDisclosureItem(id: 2, text: presentationData.strings.CallSettings_RecentCalls, icon: PresentationResourcesSettings.recentCalls, action: { - interaction.openSettings(.recentCalls) - })) - - let devicesLabel: String - if let settings = data.globalSettings, let otherSessionsCount = settings.otherSessionsCount { - if settings.enableQRLogin { - devicesLabel = otherSessionsCount == 0 ? presentationData.strings.Settings_AddDevice : "\(otherSessionsCount + 1)" - } else { - devicesLabel = otherSessionsCount == 0 ? "" : "\(otherSessionsCount + 1)" - } - } else { - devicesLabel = "" - } - - items[.shortcuts]!.append(PeerInfoScreenDisclosureItem(id: 3, label: .text(devicesLabel), text: presentationData.strings.Settings_Devices, icon: PresentationResourcesSettings.devices, action: { - interaction.openSettings(.devices) - })) - items[.shortcuts]!.append(PeerInfoScreenDisclosureItem(id: 4, text: presentationData.strings.Settings_ChatFolders, icon: PresentationResourcesSettings.chatFolders, action: { - interaction.openSettings(.chatFolders) - })) - - let notificationsWarning: Bool - if let settings = data.globalSettings { - notificationsWarning = shouldDisplayNotificationsPermissionWarning(status: settings.notificationAuthorizationStatus, suppressed: settings.notificationWarningSuppressed) - } else { - notificationsWarning = false - } - items[.advanced]!.append(PeerInfoScreenDisclosureItem(id: 0, label: notificationsWarning ? .badge("!", presentationData.theme.list.itemDestructiveColor) : .none, text: presentationData.strings.Settings_NotificationsAndSounds, icon: PresentationResourcesSettings.notifications, action: { - interaction.openSettings(.notificationsAndSounds) - })) - items[.advanced]!.append(PeerInfoScreenDisclosureItem(id: 1, text: presentationData.strings.Settings_PrivacySettings, icon: PresentationResourcesSettings.security, action: { - interaction.openSettings(.privacyAndSecurity) - })) - items[.advanced]!.append(PeerInfoScreenDisclosureItem(id: 2, text: presentationData.strings.Settings_ChatSettings, icon: PresentationResourcesSettings.dataAndStorage, action: { - interaction.openSettings(.dataAndStorage) - })) - items[.advanced]!.append(PeerInfoScreenDisclosureItem(id: 3, text: presentationData.strings.Settings_Appearance, icon: PresentationResourcesSettings.appearance, action: { - interaction.openSettings(.appearance) - })) - - items[.advanced]!.append(PeerInfoScreenDisclosureItem(id: 6, label: .text(data.isPowerSavingEnabled == true ? presentationData.strings.Settings_PowerSavingOn : presentationData.strings.Settings_PowerSavingOff), text: presentationData.strings.Settings_PowerSaving, icon: PresentationResourcesSettings.powerSaving, action: { - interaction.openSettings(.powerSaving) - })) - - let languageName = presentationData.strings.primaryComponent.localizedName - items[.advanced]!.append(PeerInfoScreenDisclosureItem(id: 4, label: .text(languageName.isEmpty ? presentationData.strings.Localization_LanguageName : languageName), text: presentationData.strings.Settings_AppLanguage, icon: PresentationResourcesSettings.language, action: { - interaction.openSettings(.language) - })) - - let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) - let isPremiumDisabled = premiumConfiguration.isPremiumDisabled - if !isPremiumDisabled || context.isPremium { - items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 100, label: .text(""), text: presentationData.strings.Settings_Premium, icon: PresentationResourcesSettings.premium, action: { - interaction.openSettings(.premium) - })) - } - if let starsState = data.starsState { - if !isPremiumDisabled || abs(starsState.balance.value) > 0 { - let balanceText: NSAttributedString - if abs(starsState.balance.value) > 0 { - let formattedLabel = formatStarsAmountText(starsState.balance, dateTimeFormat: presentationData.dateTimeFormat) - let smallLabelFont = Font.regular(floor(presentationData.listsFontSize.itemListBaseFontSize / 17.0 * 13.0)) - let labelFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize) - let labelColor = presentationData.theme.list.itemSecondaryTextColor - balanceText = tonAmountAttributedString(formattedLabel, integralFont: labelFont, fractionalFont: smallLabelFont, color: labelColor, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator) - } else { - balanceText = NSAttributedString() - } - items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 102, label: .attributedText(balanceText), text: presentationData.strings.Settings_Stars, icon: PresentationResourcesSettings.stars, action: { - interaction.openSettings(.stars) - })) - } - } - if let tonState = data.tonState { - if abs(tonState.balance.value) > 0 { - let balanceText: NSAttributedString - if abs(tonState.balance.value) > 0 { - let formattedLabel = formatTonAmountText(tonState.balance.value, dateTimeFormat: presentationData.dateTimeFormat) - let smallLabelFont = Font.regular(floor(presentationData.listsFontSize.itemListBaseFontSize / 17.0 * 13.0)) - let labelFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize) - let labelColor = presentationData.theme.list.itemSecondaryTextColor - balanceText = tonAmountAttributedString(formattedLabel, integralFont: labelFont, fractionalFont: smallLabelFont, color: labelColor, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator) - } else { - balanceText = NSAttributedString() - } - items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 103, label: .attributedText(balanceText), text: presentationData.strings.Settings_MyTon, icon: PresentationResourcesSettings.ton, action: { - interaction.openSettings(.ton) - })) - } - } - if !isPremiumDisabled || context.isPremium { - items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 104, label: .text(""), additionalBadgeLabel: nil, text: presentationData.strings.Settings_Business, icon: PresentationResourcesSettings.business, action: { - interaction.openSettings(.businessSetup) - })) - } - if let starsState = data.starsState { - if !isPremiumDisabled || starsState.balance > StarsAmount.zero { - items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 105, label: .text(""), text: presentationData.strings.Settings_SendGift, icon: PresentationResourcesSettings.premiumGift, action: { - interaction.openSettings(.premiumGift) - })) - } - } - - if let settings = data.globalSettings { - if settings.hasPassport { - items[.extra]!.append(PeerInfoScreenDisclosureItem(id: 0, text: presentationData.strings.Settings_Passport, icon: PresentationResourcesSettings.passport, action: { - interaction.openSettings(.passport) - })) - } - if settings.hasWatchApp { - items[.extra]!.append(PeerInfoScreenDisclosureItem(id: 1, text: presentationData.strings.Settings_AppleWatch, icon: PresentationResourcesSettings.watch, action: { - interaction.openSettings(.watch) - })) - } - } - - items[.support]!.append(PeerInfoScreenDisclosureItem(id: 0, text: presentationData.strings.Settings_Support, icon: PresentationResourcesSettings.support, action: { - interaction.openSettings(.support) - })) - items[.support]!.append(PeerInfoScreenDisclosureItem(id: 1, text: presentationData.strings.Settings_FAQ, icon: PresentationResourcesSettings.faq, action: { - interaction.openSettings(.faq) - })) - items[.support]!.append(PeerInfoScreenDisclosureItem(id: 2, text: presentationData.strings.Settings_Tips, icon: PresentationResourcesSettings.tips, action: { - interaction.openSettings(.tips) - })) - - var result: [(AnyHashable, [PeerInfoScreenItem])] = [] - for section in SettingsSection.allCases { - if let sectionItems = items[section], !sectionItems.isEmpty { - result.append((section, sectionItems)) - } - } - return result -} - -private func settingsEditingItems(data: PeerInfoScreenData?, state: PeerInfoState, context: AccountContext, presentationData: PresentationData, interaction: PeerInfoInteraction, isMyProfile: Bool) -> [(AnyHashable, [PeerInfoScreenItem])] { - guard let data = data else { - return [] - } - - enum Section: Int, CaseIterable { - case help - case bio - case birthday - case info - case account - case logout - } - - var items: [Section: [PeerInfoScreenItem]] = [:] - for section in Section.allCases { - items[section] = [] - } - - let ItemNameHelp = 0 - let ItemBio: AnyHashable = AnyHashable("bio_edit") - let ItemBioHelp = 2 - let ItemPhoneNumber = 3 - let ItemUsername = 4 - let ItemAddAccount = 5 - let ItemAddAccountHelp = 6 - let ItemLogout = 7 - let ItemPeerColor = 8 - let ItemBirthday = 9 - let ItemBirthdayPicker = 10 - let ItemBirthdayRemove = 11 - let ItemBirthdayHelp = 12 - let ItemPeerPersonalChannel = 13 - - items[.help]!.append(PeerInfoScreenCommentItem(id: ItemNameHelp, text: presentationData.strings.EditProfile_NameAndPhotoOrVideoHelp)) - - if let cachedData = data.cachedData as? CachedUserData { - items[.bio]!.append(PeerInfoScreenMultilineInputItem(id: ItemBio, text: state.updatingBio ?? (cachedData.about ?? ""), placeholder: presentationData.strings.UserInfo_About_Placeholder, textUpdated: { updatedText in - interaction.updateBio(updatedText) - }, action: { - interaction.dismissInput() - }, maxLength: Int(data.globalSettings?.userLimits.maxAboutLength ?? 70))) - items[.bio]!.append(PeerInfoScreenCommentItem(id: ItemBioHelp, text: presentationData.strings.Settings_About_PrivacyHelp, linkAction: { _ in - interaction.openBioPrivacy() - })) - } - - - var birthday: TelegramBirthday? - if let updatingBirthDate = state.updatingBirthDate { - birthday = updatingBirthDate - } else { - birthday = (data.cachedData as? CachedUserData)?.birthday - } - - var birthDateString: String - if let birthday { - birthDateString = stringForCompactBirthday(birthday, strings: presentationData.strings) - } else { - birthDateString = presentationData.strings.Settings_Birthday_Add - } - - let isEditingBirthDate = state.isEditingBirthDate - items[.birthday]!.append(PeerInfoScreenDisclosureItem(id: ItemBirthday, label: .coloredText(birthDateString, isEditingBirthDate ? .accent : .generic), text: presentationData.strings.Settings_Birthday, icon: nil, hasArrow: false, action: { - interaction.updateIsEditingBirthdate(!isEditingBirthDate) - })) - if isEditingBirthDate, let birthday { - items[.birthday]!.append(PeerInfoScreenBirthdatePickerItem(id: ItemBirthdayPicker, value: birthday, valueUpdated: { value in - interaction.updateBirthdate(value) - })) - items[.birthday]!.append(PeerInfoScreenActionItem(id: ItemBirthdayRemove, text: presentationData.strings.Settings_Birthday_Remove, alignment: .natural, action: { - interaction.updateBirthdate(.some(nil)) - interaction.updateIsEditingBirthdate(false) - })) - } - - - var birthdayIsForContactsOnly = false - if let birthdayPrivacy = data.globalSettings?.privacySettings?.birthday, case .enableContacts = birthdayPrivacy { - birthdayIsForContactsOnly = true - } - items[.birthday]!.append(PeerInfoScreenCommentItem(id: ItemBirthdayHelp, text: birthdayIsForContactsOnly ? presentationData.strings.Settings_Birthday_ContactsHelp : presentationData.strings.Settings_Birthday_Help, linkAction: { _ in - interaction.openBirthdatePrivacy() - })) - - if let user = data.peer as? TelegramUser { - items[.info]!.append(PeerInfoScreenDisclosureItem(id: ItemPhoneNumber, label: .text(user.phone.flatMap({ formatPhoneNumber(context: context, number: $0) }) ?? ""), text: presentationData.strings.Settings_PhoneNumber, action: { - interaction.openSettings(.phoneNumber) - })) - } - var username = "" - if let addressName = data.peer?.addressName, !addressName.isEmpty { - username = "@\(addressName)" - } - items[.info]!.append(PeerInfoScreenDisclosureItem(id: ItemUsername, label: .text(username), text: presentationData.strings.Settings_Username, action: { - interaction.openSettings(.username) - })) - - if let peer = data.peer as? TelegramUser { - var colors: [PeerNameColors.Colors] = [] - if let nameColor = peer.nameColor { - let nameColors: PeerNameColors.Colors - switch nameColor { - case let .preset(nameColor): - nameColors = context.peerNameColors.get(nameColor, dark: presentationData.theme.overallDarkAppearance) - case let .collectible(collectibleColor): - nameColors = collectibleColor.peerNameColors(dark: presentationData.theme.overallDarkAppearance) - } - colors.append(nameColors) - } - if let profileColor = peer.effectiveProfileColor.flatMap({ context.peerNameColors.getProfile($0, dark: presentationData.theme.overallDarkAppearance, subject: .palette) }) { - colors.append(profileColor) - } - let colorImage = generateSettingsMenuPeerColorsLabelIcon(colors: colors) - - items[.info]!.append(PeerInfoScreenDisclosureItem(id: ItemPeerColor, label: .image(colorImage, colorImage.size), text: presentationData.strings.Settings_YourColor, icon: nil, action: { - interaction.editingOpenNameColorSetup() - })) - - var displayPersonalChannel = false - if data.personalChannel != nil { - displayPersonalChannel = true - } else if let personalChannels = state.personalChannels, !personalChannels.isEmpty { - displayPersonalChannel = true - } - if displayPersonalChannel { - var personalChannelTitle: String? - if let personalChannel = data.personalChannel, let peer = personalChannel.peer.chatOrMonoforumMainPeer { - personalChannelTitle = peer.compactDisplayTitle - } - - items[.info]!.append(PeerInfoScreenDisclosureItem(id: ItemPeerPersonalChannel, label: .text(personalChannelTitle ?? presentationData.strings.Settings_PersonalChannelEmptyValue), text: presentationData.strings.Settings_PersonalChannelItem, icon: nil, action: { - interaction.editingOpenPersonalChannel() - })) - } - } - - items[.account]!.append(PeerInfoScreenActionItem(id: ItemAddAccount, text: presentationData.strings.Settings_AddAnotherAccount, alignment: .center, action: { - interaction.openSettings(.addAccount) - })) - - var hasPremiumAccounts = false - if data.peer?.isPremium == true && !context.account.testingEnvironment { - hasPremiumAccounts = true - } - if let settings = data.globalSettings { - for (accountContext, peer, _) in settings.accountsAndPeers { - if !accountContext.account.testingEnvironment { - if peer.isPremium { - hasPremiumAccounts = true - break - } - } - } - } - - items[.account]!.append(PeerInfoScreenCommentItem(id: ItemAddAccountHelp, text: hasPremiumAccounts ? presentationData.strings.Settings_AddAnotherAccount_PremiumHelp : presentationData.strings.Settings_AddAnotherAccount_Help)) - - items[.logout]!.append(PeerInfoScreenActionItem(id: ItemLogout, text: presentationData.strings.Settings_Logout, color: .destructive, alignment: .center, action: { - interaction.openSettings(.logout) - })) - - var result: [(AnyHashable, [PeerInfoScreenItem])] = [] - for section in Section.allCases { - if let sectionItems = items[section], !sectionItems.isEmpty { - result.append((section, sectionItems)) - } - } - return result -} - -private enum InfoSection: Int, CaseIterable { - case groupLocation - case calls - case personalChannel - case peerInfo - case balances - case permissions - case peerInfoTrailing - case peerSettings - case peerMembers - case channelMonoforum - case botAffiliateProgram -} - -private func infoItems(data: PeerInfoScreenData?, context: AccountContext, presentationData: PresentationData, interaction: PeerInfoInteraction, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], chatLocation: ChatLocation, isOpenedFromChat: Bool, isMyProfile: Bool) -> [(AnyHashable, [PeerInfoScreenItem])] { - guard let data = data else { - return [] - } - - var currentPeerInfoSection: InfoSection = .peerInfo - - var items: [InfoSection: [PeerInfoScreenItem]] = [:] - for section in InfoSection.allCases { - items[section] = [] - } - - let bioContextAction: (ASDisplayNode, ContextGesture?, CGPoint?) -> Void = { node, gesture, _ in - interaction.openBioContextMenu(node, gesture) - } - let noteContextAction: (ASDisplayNode, ContextGesture?, CGPoint?) -> Void = { node, gesture, _ in - interaction.openNoteContextMenu(node, gesture) - } - let bioLinkAction: (TextLinkItemActionType, TextLinkItem, ASDisplayNode, CGRect?, Promise?) -> Void = { action, item, _, _, _ in - interaction.performBioLinkAction(action, item) - } - let workingHoursContextAction: (ASDisplayNode, ContextGesture?, CGPoint?) -> Void = { node, gesture, _ in - interaction.openWorkingHoursContextMenu(node, gesture) - } - let businessLocationContextAction: (ASDisplayNode, ContextGesture?, CGPoint?) -> Void = { node, gesture, _ in - interaction.openBusinessLocationContextMenu(node, gesture) - } - let birthdayContextAction: (ASDisplayNode, ContextGesture?, CGPoint?) -> Void = { node, gesture, _ in - interaction.openBirthdayContextMenu(node, gesture) - } - - if let user = data.peer as? TelegramUser { - let ItemCallList = 1000 - let ItemPersonalChannelHeader = 2000 - let ItemPersonalChannel = 2001 - let ItemPhoneNumber = 3000 - let ItemUsername = 3001 - let ItemBirthdate = 3002 - let ItemAbout = 3003 - let ItemNote = 3004 - let ItemAppFooter = 3005 - let ItemAffiliate = 4000 - let ItemAffiliateInfo = 4001 - let ItemBusinessHours = 5000 - let ItemLocation = 5001 - let ItemSendMessage = 6000 - let ItemReport = 6001 - let ItemAddToContacts = 6002 - let ItemBlock = 6003 - let ItemEncryptionKey = 6004 - let ItemBalanceHeader = 7000 - let ItemBalanceTon = 7001 - let ItemBalanceStars = 7002 - let ItemBotPermissionsHeader = 8000 - let ItemBotPermissionsEmojiStatus = 8001 - let ItemBotPermissionsLocation = 8002 - let ItemBotPermissionsBiometry = 8003 - let ItemBotSettings = 9000 - let ItemBotReport = 9001 - let ItemBotAddToChat = 9002 - let ItemBotAddToChatInfo = 9003 - let ItemVerification = 9004 - - if !callMessages.isEmpty { - items[.calls]!.append(PeerInfoScreenCallListItem(id: ItemCallList, messages: callMessages)) - } - - if let personalChannel = data.personalChannel { - let peerId = personalChannel.peer.peerId - var label: String? - if let subscriberCount = personalChannel.subscriberCount { - label = presentationData.strings.Conversation_StatusSubscribers(Int32(subscriberCount)) - } - items[.personalChannel]?.append(PeerInfoScreenHeaderItem(id: ItemPersonalChannelHeader, text: presentationData.strings.Profile_PersonalChannelSectionTitle, label: label)) - items[.personalChannel]?.append(PeerInfoScreenPersonalChannelItem(id: ItemPersonalChannel, context: context, data: personalChannel, controller: { [weak interaction] in - guard let interaction else { - return nil - } - return interaction.getController() - }, action: { [weak interaction] in - guard let interaction else { - return - } - interaction.openChat(peerId) - })) - } - - if let phone = user.phone { - let formattedPhone = formatPhoneNumber(context: context, number: phone) - let label: String - if formattedPhone.hasPrefix("+888 ") { - label = presentationData.strings.UserInfo_AnonymousNumberLabel - } else { - label = presentationData.strings.ContactInfo_PhoneLabelMobile - } - items[currentPeerInfoSection]!.append(PeerInfoScreenLabeledValueItem(id: ItemPhoneNumber, label: label, text: formattedPhone, textColor: .accent, action: { node, progress in - interaction.openPhone(phone, node, nil, progress) - }, longTapAction: nil, contextAction: { node, gesture, _ in - interaction.openPhone(phone, node, gesture, nil) - }, requestLayout: { animated in - interaction.requestLayout(animated) - })) - } - if let mainUsername = user.addressName { - var additionalUsernames: String? - let usernames = user.usernames.filter { $0.isActive && $0.username != mainUsername } - if !usernames.isEmpty { - additionalUsernames = presentationData.strings.Profile_AdditionalUsernames(String(usernames.map { "@\($0.username)" }.joined(separator: ", "))).string - } - - items[currentPeerInfoSection]!.append( - PeerInfoScreenLabeledValueItem( - id: ItemUsername, - label: presentationData.strings.Profile_Username, - text: "@\(mainUsername)", - additionalText: additionalUsernames, - textColor: .accent, - icon: .qrCode, - action: { _, progress in - interaction.openUsername(mainUsername, true, progress) - }, linkItemAction: { type, item, _, _, progress in - if case .tap = type { - if case let .mention(username) = item { - interaction.openUsername(String(username[username.index(username.startIndex, offsetBy: 1)...]), false, progress) - } - } - }, iconAction: { - interaction.openQrCode() - }, contextAction: { node, gesture, _ in - interaction.openUsernameContextMenu(node, gesture) - }, requestLayout: { animated in - interaction.requestLayout(animated) - } - ) - ) - } - - if let cachedData = data.cachedData as? CachedUserData { - if let birthday = cachedData.birthday { - var hasBirthdayToday = false - let today = Calendar.current.dateComponents(Set([.day, .month]), from: Date()) - if today.day == Int(birthday.day) && today.month == Int(birthday.month) { - hasBirthdayToday = true - } - - var birthdayAction: ((ASDisplayNode, Promise?) -> Void)? - if isMyProfile { - birthdayAction = { node, _ in - birthdayContextAction(node, nil, nil) - } - } else if hasBirthdayToday && cachedData.disallowedGifts != TelegramDisallowedGifts.All { - birthdayAction = { _, _ in - interaction.openPremiumGift() - } - } - - items[currentPeerInfoSection]!.append(PeerInfoScreenLabeledValueItem(id: ItemBirthdate, context: context, label: hasBirthdayToday ? presentationData.strings.UserInfo_BirthdayToday : presentationData.strings.UserInfo_Birthday, text: stringForCompactBirthday(birthday, strings: presentationData.strings, showAge: true), textColor: .primary, leftIcon: hasBirthdayToday ? .birthday : nil, icon: hasBirthdayToday ? .premiumGift : nil, action: birthdayAction, longTapAction: nil, iconAction: { - interaction.openPremiumGift() - }, contextAction: birthdayContextAction, requestLayout: { _ in - })) - } - - var hasAbout = false - if let about = cachedData.about, !about.isEmpty { - hasAbout = true - } - var hasNote = false - if let note = cachedData.note, !note.text.isEmpty { - hasNote = true - } - - var hasWebApp = false - if let botInfo = user.botInfo, botInfo.flags.contains(.hasWebApp) { - hasWebApp = true - } - - if user.isFake { - items[currentPeerInfoSection]!.append(PeerInfoScreenLabeledValueItem(id: ItemAbout, label: "", text: user.botInfo != nil ? presentationData.strings.UserInfo_FakeBotWarning : presentationData.strings.UserInfo_FakeUserWarning, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: user.botInfo != nil ? enabledPrivateBioEntities : []), action: nil, requestLayout: { animated in - interaction.requestLayout(animated) - })) - } else if user.isScam { - items[currentPeerInfoSection]!.append(PeerInfoScreenLabeledValueItem(id: ItemAbout, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: user.botInfo != nil ? presentationData.strings.UserInfo_ScamBotWarning : presentationData.strings.UserInfo_ScamUserWarning, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: user.botInfo != nil ? enabledPrivateBioEntities : []), action: nil, requestLayout: { animated in - interaction.requestLayout(animated) - })) - } else if hasAbout || hasNote || hasWebApp { - var actionButton: PeerInfoScreenLabeledValueItem.Button? - if hasWebApp { - actionButton = PeerInfoScreenLabeledValueItem.Button(title: presentationData.strings.PeerInfo_OpenAppButton, action: { - guard let parentController = interaction.getController() else { - return - } - - if let navigationController = parentController.navigationController as? NavigationController, let minimizedContainer = navigationController.minimizedContainer { - for controller in minimizedContainer.controllers { - if let controller = controller as? AttachmentController, let mainController = controller.mainController as? WebAppController, mainController.botId == user.id && mainController.source == .generic { - navigationController.maximizeViewController(controller, animated: true) - return - } - } - } - - context.sharedContext.openWebApp( - context: context, - parentController: parentController, - updatedPresentationData: nil, - botPeer: .user(user), - chatPeer: nil, - threadId: nil, - buttonText: "", - url: "", - simple: true, - source: .generic, - skipTermsOfService: true, - payload: nil, - verifyAgeCompletion: nil - ) - }) - } - - if hasAbout || hasWebApp { - var label: String = "" - if let about = cachedData.about, !about.isEmpty { - label = user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo - } - items[currentPeerInfoSection]!.append(PeerInfoScreenLabeledValueItem(id: ItemAbout, label: label, text: cachedData.about ?? "", textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: user.isPremium ? enabledPublicBioEntities : enabledPrivateBioEntities), action: isMyProfile ? { node, _ in - bioContextAction(node, nil, nil) - } : nil, linkItemAction: bioLinkAction, button: actionButton, contextAction: bioContextAction, requestLayout: { animated in - interaction.requestLayout(animated) - })) - } - - if let note = cachedData.note, !note.text.isEmpty { - var entities = note.entities - if context.isPremium { - entities = generateTextEntities(note.text, enabledTypes: [.mention, .hashtag, .allUrl], currentEntities: entities) - } - items[currentPeerInfoSection]!.append(PeerInfoScreenLabeledValueItem(id: ItemNote, label: presentationData.strings.PeerInfo_Notes, rightLabel: presentationData.strings.PeerInfo_NotesInfo, text: note.text, entities: entities, handleSpoilers: true, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: []), action: nil, linkItemAction: bioLinkAction, button: nil, contextAction: noteContextAction, requestLayout: { animated in - interaction.requestLayout(animated) - })) - } - - if let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) { - items[currentPeerInfoSection]!.append(PeerInfoScreenCommentItem(id: ItemAppFooter, text: presentationData.strings.PeerInfo_AppFooterAdmin, linkAction: { action in - if case let .tap(url) = action { - context.sharedContext.applicationBindings.openUrl(url) - } - })) - - currentPeerInfoSection = .peerInfoTrailing - } else if actionButton != nil { - items[currentPeerInfoSection]!.append(PeerInfoScreenCommentItem(id: ItemAppFooter, text: presentationData.strings.PeerInfo_AppFooter, linkAction: { action in - if case let .tap(url) = action { - context.sharedContext.applicationBindings.openUrl(url) - } - })) - - currentPeerInfoSection = .peerInfoTrailing - } - - if let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) { - } else { - if let starRefProgram = cachedData.starRefProgram, starRefProgram.endDate == nil { - var canJoinRefProgram = false - if let data = context.currentAppConfiguration.with({ $0 }).data, let value = data["starref_connect_allowed"] { - if let value = value as? Double { - canJoinRefProgram = value != 0.0 - } else if let value = value as? Bool { - canJoinRefProgram = value - } - } - - if canJoinRefProgram { - if items[.botAffiliateProgram] == nil { - items[.botAffiliateProgram] = [] - } - let programTitleValue: String - programTitleValue = "\(formatPermille(starRefProgram.commissionPermille))%" - items[.botAffiliateProgram]!.append(PeerInfoScreenDisclosureItem(id: ItemAffiliate, label: .labelBadge(programTitleValue), additionalBadgeLabel: nil, text: presentationData.strings.PeerInfo_ItemAffiliateProgram_Title, icon: PresentationResourcesSettings.affiliateProgram, action: { - interaction.editingOpenAffiliateProgram() - })) - items[.botAffiliateProgram]!.append(PeerInfoScreenCommentItem(id: ItemAffiliateInfo, text: presentationData.strings.PeerInfo_ItemAffiliateProgram_Footer(EnginePeer.user(user).compactDisplayTitle, formatPermille(starRefProgram.commissionPermille)).string)) - } - } - } - } - - if let businessHours = cachedData.businessHours { - items[currentPeerInfoSection]!.append(PeerInfoScreenBusinessHoursItem(id: ItemBusinessHours, label: presentationData.strings.PeerInfo_BusinessHours_Label, businessHours: businessHours, requestLayout: { animated in - interaction.requestLayout(animated) - }, longTapAction: nil, contextAction: workingHoursContextAction)) - } - - if let businessLocation = cachedData.businessLocation { - if let coordinates = businessLocation.coordinates { - let imageSignal = chatMapSnapshotImage(engine: context.engine, resource: MapSnapshotMediaResource(latitude: coordinates.latitude, longitude: coordinates.longitude, width: 90, height: 90)) - items[currentPeerInfoSection]!.append(PeerInfoScreenAddressItem( - id: ItemLocation, - label: presentationData.strings.PeerInfo_Location_Label, - text: businessLocation.address, - imageSignal: imageSignal, - action: { - interaction.openLocation() - }, - contextAction: businessLocationContextAction - )) - } else { - items[currentPeerInfoSection]!.append(PeerInfoScreenAddressItem( - id: ItemLocation, - label: presentationData.strings.PeerInfo_Location_Label, - text: businessLocation.address, - imageSignal: nil, - action: nil, - contextAction: businessLocationContextAction - )) - } - } - } - - if !isMyProfile { - if let reactionSourceMessageId = reactionSourceMessageId, !data.isContact { - items[currentPeerInfoSection]!.append(PeerInfoScreenActionItem(id: ItemSendMessage, text: presentationData.strings.UserInfo_SendMessage, action: { - interaction.openChat(nil) - })) - - items[currentPeerInfoSection]!.append(PeerInfoScreenActionItem(id: ItemReport, text: presentationData.strings.ReportPeer_BanAndReport, color: .destructive, action: { - interaction.openReport(.reaction(reactionSourceMessageId)) - })) - } else if let _ = nearbyPeerDistance { - items[currentPeerInfoSection]!.append(PeerInfoScreenActionItem(id: ItemSendMessage, text: presentationData.strings.UserInfo_SendMessage, action: { - interaction.openChat(nil) - })) - - items[currentPeerInfoSection]!.append(PeerInfoScreenActionItem(id: ItemReport, text: presentationData.strings.ReportPeer_Report, color: .destructive, action: { - interaction.openReport(.user) - })) - } else { - if !data.isContact { - if user.botInfo == nil { - items[currentPeerInfoSection]!.append(PeerInfoScreenActionItem(id: ItemAddToContacts, text: presentationData.strings.PeerInfo_AddToContacts, action: { - interaction.openAddContact() - })) - } - } - - var isBlocked = false - if let cachedData = data.cachedData as? CachedUserData, cachedData.isBlocked { - isBlocked = true - } - - if isBlocked { - items[currentPeerInfoSection]!.append(PeerInfoScreenActionItem(id: ItemBlock, text: user.botInfo != nil ? presentationData.strings.Bot_Unblock : presentationData.strings.Conversation_Unblock, action: { - interaction.updateBlocked(false) - })) - } else { - if user.flags.contains(.isSupport) || data.isContact { - } else { - if user.botInfo == nil { - items[currentPeerInfoSection]!.append(PeerInfoScreenActionItem(id: ItemBlock, text: presentationData.strings.Conversation_BlockUser, color: .destructive, action: { - interaction.updateBlocked(true) - })) - } - } - } - - if let encryptionKeyFingerprint = data.encryptionKeyFingerprint { - items[currentPeerInfoSection]!.append(PeerInfoScreenDisclosureEncryptionKeyItem(id: ItemEncryptionKey, text: presentationData.strings.Profile_EncryptionKey, fingerprint: encryptionKeyFingerprint, action: { - interaction.openEncryptionKey() - })) - } - - let revenueBalance = data.revenueStatsState?.balances.currentBalance.amount.value ?? 0 - let overallRevenueBalance = data.revenueStatsState?.balances.overallRevenue.amount.value ?? 0 - - let starsBalance = data.starsRevenueStatsState?.balances.currentBalance.amount ?? StarsAmount.zero - let overallStarsBalance = data.starsRevenueStatsState?.balances.overallRevenue.amount ?? StarsAmount.zero - - if overallRevenueBalance > 0 || overallStarsBalance > StarsAmount.zero { - items[.balances]!.append(PeerInfoScreenHeaderItem(id: ItemBalanceHeader, text: presentationData.strings.PeerInfo_BotBalance_Title)) - if overallRevenueBalance > 0 { - let string = "*\(formatTonAmountText(revenueBalance, dateTimeFormat: presentationData.dateTimeFormat))" - let attributedString = NSMutableAttributedString(string: string, font: Font.regular(presentationData.listsFontSize.itemListBaseFontSize), textColor: presentationData.theme.list.itemSecondaryTextColor) - if let range = attributedString.string.range(of: "*") { - attributedString.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .ton(tinted: false)), range: NSRange(range, in: attributedString.string)) - attributedString.addAttribute(.baselineOffset, value: 1.5, range: NSRange(range, in: attributedString.string)) - } - items[.balances]!.append(PeerInfoScreenDisclosureItem(id: ItemBalanceTon, label: .attributedText(attributedString), text: presentationData.strings.PeerInfo_BotBalance_Ton, icon: PresentationResourcesSettings.ton, action: { - interaction.editingOpenRevenue() - })) - } - - if overallStarsBalance > StarsAmount.zero { - let formattedLabel = formatStarsAmountText(starsBalance, dateTimeFormat: presentationData.dateTimeFormat) - let smallLabelFont = Font.regular(floor(presentationData.listsFontSize.itemListBaseFontSize / 17.0 * 13.0)) - let labelFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize) - let labelColor = presentationData.theme.list.itemSecondaryTextColor - let attributedString = tonAmountAttributedString(formattedLabel, integralFont: labelFont, fractionalFont: smallLabelFont, color: labelColor, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator).mutableCopy() as! NSMutableAttributedString - attributedString.insert(NSAttributedString(string: "*", font: labelFont, textColor: labelColor), at: 0) - - if let range = attributedString.string.range(of: "*") { - attributedString.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: false)), range: NSRange(range, in: attributedString.string)) - attributedString.addAttribute(.baselineOffset, value: 1.5, range: NSRange(range, in: attributedString.string)) - } - items[.balances]!.append(PeerInfoScreenDisclosureItem(id: ItemBalanceStars, label: .attributedText(attributedString), text: presentationData.strings.PeerInfo_BotBalance_Stars, icon: PresentationResourcesSettings.stars, action: { - interaction.editingOpenStars() - })) - } - } - - if let _ = user.botInfo { - var canManageEmojiStatus = false - if let cachedData = data.cachedData as? CachedUserData, cachedData.flags.contains(.botCanManageEmojiStatus) { - canManageEmojiStatus = true - } - if canManageEmojiStatus || data.webAppPermissions?.emojiStatus?.isRequested == true { - items[.permissions]!.append(PeerInfoScreenSwitchItem(id: ItemBotPermissionsEmojiStatus, text: presentationData.strings.PeerInfo_Permissions_EmojiStatus, value: canManageEmojiStatus, icon: UIImage(bundleImageName: "Chat/Info/Status"), isLocked: false, toggled: { value in - let _ = (context.engine.peers.toggleBotEmojiStatusAccess(peerId: user.id, enabled: value) - |> deliverOnMainQueue).startStandalone() - - let _ = updateWebAppPermissionsStateInteractively(context: context, peerId: user.id) { current in - return WebAppPermissionsState(location: current?.location, emojiStatus: WebAppPermissionsState.EmojiStatus(isRequested: true)) - }.startStandalone() - })) - } - if data.webAppPermissions?.location?.isRequested == true || data.webAppPermissions?.location?.isAllowed == true { - items[.permissions]!.append(PeerInfoScreenSwitchItem(id: ItemBotPermissionsLocation, text: presentationData.strings.PeerInfo_Permissions_Geolocation, value: data.webAppPermissions?.location?.isAllowed ?? false, icon: UIImage(bundleImageName: "Chat/Info/Location"), isLocked: false, toggled: { value in - let _ = updateWebAppPermissionsStateInteractively(context: context, peerId: user.id) { current in - return WebAppPermissionsState(location: WebAppPermissionsState.Location(isRequested: true, isAllowed: value), emojiStatus: current?.emojiStatus) - }.startStandalone() - })) - } - if !"".isEmpty { - items[.permissions]!.append(PeerInfoScreenSwitchItem(id: ItemBotPermissionsBiometry, text: presentationData.strings.PeerInfo_Permissions_Biometry, value: true, icon: UIImage(bundleImageName: "Settings/Menu/TouchId"), isLocked: false, toggled: { value in - - })) - } - - if !items[.permissions]!.isEmpty { - items[.permissions]!.insert(PeerInfoScreenHeaderItem(id: ItemBotPermissionsHeader, text: presentationData.strings.PeerInfo_Permissions_Title), at: 0) - } - } - - if let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) { - items[currentPeerInfoSection]!.append(PeerInfoScreenDisclosureItem(id: ItemBotSettings, label: .none, text: presentationData.strings.Bot_Settings, icon: UIImage(bundleImageName: "Chat/Info/SettingsIcon"), action: { - interaction.openEditing() - })) - } - - if let botInfo = user.botInfo, !botInfo.flags.contains(.canEdit) { - items[currentPeerInfoSection]!.append(PeerInfoScreenActionItem(id: ItemBotReport, text: presentationData.strings.ReportPeer_Report, action: { - interaction.openReport(.default) - })) - } - - if let verification = (data.cachedData as? CachedUserData)?.verification { - let description: String - let descriptionString = verification.description - let entities = generateTextEntities(descriptionString, enabledTypes: [.allUrl]) - if let entity = entities.first { - let range = NSRange(location: entity.range.lowerBound, length: entity.range.upperBound - entity.range.lowerBound) - let url = (descriptionString as NSString).substring(with: range) - description = descriptionString.replacingOccurrences(of: url, with: "[\(url)](\(url))") - } else { - description = descriptionString - } - let attributedPrefix = NSMutableAttributedString(string: " ") - attributedPrefix.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: verification.iconFileId, file: nil), range: NSMakeRange(0, 1)) - - items[currentPeerInfoSection]!.append(PeerInfoScreenCommentItem(id: ItemVerification, text: description, attributedPrefix: attributedPrefix, useAccentLinkColor: false, linkAction: { action in - if case let .tap(url) = action, let navigationController = interaction.getController()?.navigationController as? NavigationController { - context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: url, forceExternal: false, presentationData: presentationData, navigationController: navigationController, dismissInput: {}) - } - })) - } else if let botInfo = user.botInfo, botInfo.flags.contains(.worksWithGroups) { - items[currentPeerInfoSection]!.append(PeerInfoScreenActionItem(id: ItemBotAddToChat, text: presentationData.strings.Bot_AddToChat, color: .accent, action: { - interaction.openAddBotToGroup() - })) - items[currentPeerInfoSection]!.append(PeerInfoScreenCommentItem(id: ItemBotAddToChatInfo, text: presentationData.strings.Bot_AddToChatInfo)) - } - } - } - } else if let channel = data.peer as? TelegramChannel { - let ItemUsername = 1 - let ItemUsernameInfo = 2 - let ItemAbout = 3 - let ItemLocationHeader = 4 - let ItemLocation = 5 - let ItemAdmins = 6 - let ItemMembers = 7 - let ItemMemberRequests = 8 - let ItemBalance = 9 - let ItemEdit = 10 - let ItemPeerPersonalChannel = 11 - - if let _ = data.threadData { - let mainUsername: String - if let addressName = channel.addressName { - mainUsername = addressName - } else { - mainUsername = "c/\(channel.id.id._internalGetInt64Value())" - } - - var threadId: Int64 = 0 - if case let .replyThread(message) = chatLocation { - threadId = message.threadId - } - - let linkText = "https://t.me/\(mainUsername)/\(threadId)" - - items[currentPeerInfoSection]!.append( - PeerInfoScreenLabeledValueItem( - id: ItemUsername, - label: presentationData.strings.Channel_LinkItem, - text: linkText, - textColor: .accent, - icon: .qrCode, - action: { _, progress in - interaction.openUsername(linkText, true, progress) - }, longTapAction: { sourceNode in - interaction.openPeerInfoContextMenu(.link(customLink: linkText), sourceNode, nil) - }, linkItemAction: { type, item, _, _, progress in - if case .tap = type { - if case let .mention(username) = item { - interaction.openUsername(String(username.suffix(from: username.index(username.startIndex, offsetBy: 1))), false, progress) - } - } - }, iconAction: { - interaction.openQrCode() - }, requestLayout: { animated in - interaction.requestLayout(animated) - } - ) - ) - if let _ = channel.addressName { - - } else { - items[currentPeerInfoSection]!.append(PeerInfoScreenCommentItem(id: ItemUsernameInfo, text: presentationData.strings.PeerInfo_PrivateShareLinkInfo)) - } - } else { - if let location = (data.cachedData as? CachedChannelData)?.peerGeoLocation { - items[.groupLocation]!.append(PeerInfoScreenHeaderItem(id: ItemLocationHeader, text: presentationData.strings.GroupInfo_Location.uppercased())) - - let imageSignal = chatMapSnapshotImage(engine: context.engine, resource: MapSnapshotMediaResource(latitude: location.latitude, longitude: location.longitude, width: 90, height: 90)) - items[.groupLocation]!.append(PeerInfoScreenAddressItem( - id: ItemLocation, - label: "", - text: location.address.replacingOccurrences(of: ", ", with: "\n"), - imageSignal: imageSignal, - action: { - interaction.openLocation() - } - )) - } - - if let mainUsername = channel.addressName { - var additionalUsernames: String? - let usernames = channel.usernames.filter { $0.isActive && $0.username != mainUsername } - if !usernames.isEmpty { - additionalUsernames = presentationData.strings.Profile_AdditionalUsernames(String(usernames.map { "@\($0.username)" }.joined(separator: ", "))).string - } - - items[currentPeerInfoSection]!.append( - PeerInfoScreenLabeledValueItem( - id: ItemUsername, - label: presentationData.strings.Channel_LinkItem, - text: "https://t.me/\(mainUsername)", - additionalText: additionalUsernames, - textColor: .accent, - icon: .qrCode, - action: { _, progress in - interaction.openUsername(mainUsername, true, progress) - }, longTapAction: { sourceNode in - interaction.openPeerInfoContextMenu(.link(customLink: nil), sourceNode, nil) - }, linkItemAction: { type, item, sourceNode, sourceRect, progress in - if case .tap = type { - if case let .mention(username) = item { - interaction.openUsername(String(username.suffix(from: username.index(username.startIndex, offsetBy: 1))), false, progress) - } - } else if case .longTap = type { - if case let .mention(username) = item { - interaction.openPeerInfoContextMenu(.link(customLink: username), sourceNode, sourceRect) - } - } - }, iconAction: { - interaction.openQrCode() - }, requestLayout: { animated in - interaction.requestLayout(animated) - } - ) - ) - } - if let cachedData = data.cachedData as? CachedChannelData { - let aboutText: String? - if channel.isFake { - if case .broadcast = channel.info { - aboutText = presentationData.strings.ChannelInfo_FakeChannelWarning - } else { - aboutText = presentationData.strings.GroupInfo_FakeGroupWarning - } - } else if channel.isScam { - if case .broadcast = channel.info { - aboutText = presentationData.strings.ChannelInfo_ScamChannelWarning - } else { - aboutText = presentationData.strings.GroupInfo_ScamGroupWarning - } - } else if let about = cachedData.about, !about.isEmpty { - aboutText = about - } else { - aboutText = nil - } - - if let aboutText = aboutText { - var enabledEntities = enabledPublicBioEntities - if case .group = channel.info { - enabledEntities = enabledPrivateBioEntities - } - items[currentPeerInfoSection]!.append(PeerInfoScreenLabeledValueItem(id: ItemAbout, label: presentationData.strings.Channel_Info_Description, text: aboutText, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledEntities), action: isMyProfile ? { node, _ in - bioContextAction(node, nil, nil) - } : nil, linkItemAction: bioLinkAction, contextAction: bioContextAction, requestLayout: { animated in - interaction.requestLayout(animated) - })) - } - - if let verification = (data.cachedData as? CachedChannelData)?.verification { - let description: String - let descriptionString = verification.description - let entities = generateTextEntities(descriptionString, enabledTypes: [.allUrl]) - if let entity = entities.first { - let range = NSRange(location: entity.range.lowerBound, length: entity.range.upperBound - entity.range.lowerBound) - let url = (descriptionString as NSString).substring(with: range) - description = descriptionString.replacingOccurrences(of: url, with: "[\(url)](\(url))") - } else { - description = descriptionString - } - - let attributedPrefix = NSMutableAttributedString(string: " ") - attributedPrefix.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: verification.iconFileId, file: nil), range: NSMakeRange(0, 1)) - - items[currentPeerInfoSection]!.append(PeerInfoScreenCommentItem(id: 800, text: description, attributedPrefix: attributedPrefix, useAccentLinkColor: false, linkAction: { action in - if case let .tap(url) = action, let navigationController = interaction.getController()?.navigationController as? NavigationController { - context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: url, forceExternal: false, presentationData: presentationData, navigationController: navigationController, dismissInput: {}) - } - })) - } - - if case .broadcast = channel.info { - var canEditMembers = false - if channel.hasPermission(.banMembers) { - canEditMembers = true - } - if canEditMembers { - if channel.adminRights != nil || channel.flags.contains(.isCreator) { - let adminCount = cachedData.participantsSummary.adminCount ?? 0 - let memberCount = cachedData.participantsSummary.memberCount ?? 0 - - items[.peerMembers]!.append(PeerInfoScreenDisclosureItem(id: ItemAdmins, label: .text("\(adminCount == 0 ? "" : "\(presentationStringsFormattedNumber(adminCount, presentationData.dateTimeFormat.groupingSeparator))")"), text: presentationData.strings.GroupInfo_Administrators, icon: UIImage(bundleImageName: "Chat/Info/GroupAdminsIcon"), action: { - interaction.openParticipantsSection(.admins) - })) - items[.peerMembers]!.append(PeerInfoScreenDisclosureItem(id: ItemMembers, label: .text("\(memberCount == 0 ? "" : "\(presentationStringsFormattedNumber(memberCount, presentationData.dateTimeFormat.groupingSeparator))")"), text: presentationData.strings.Channel_Info_Subscribers, icon: UIImage(bundleImageName: "Chat/Info/GroupMembersIcon"), action: { - interaction.openParticipantsSection(.members) - })) - - if let count = data.requests?.count, count > 0 { - items[.peerMembers]!.append(PeerInfoScreenDisclosureItem(id: ItemMemberRequests, label: .badge(presentationStringsFormattedNumber(count, presentationData.dateTimeFormat.groupingSeparator), presentationData.theme.list.itemAccentColor), text: presentationData.strings.GroupInfo_MemberRequests, icon: UIImage(bundleImageName: "Chat/Info/GroupRequestsIcon"), action: { - interaction.openParticipantsSection(.memberRequests) - })) - } - } - } - } - - if channel.adminRights != nil || channel.flags.contains(.isCreator) { - let section: InfoSection - if case .group = channel.info { - section = .peerSettings - } else { - section = .peerMembers - } - if cachedData.flags.contains(.canViewRevenue) || cachedData.flags.contains(.canViewStarsRevenue) { - let revenueBalance = data.revenueStatsState?.balances.currentBalance.amount.value ?? 0 - let starsBalance = data.starsRevenueStatsState?.balances.currentBalance.amount ?? StarsAmount.zero - - let overallRevenueBalance = data.revenueStatsState?.balances.overallRevenue.amount.value ?? 0 - let overallStarsBalance = data.starsRevenueStatsState?.balances.overallRevenue.amount ?? StarsAmount.zero - - if overallRevenueBalance > 0 || overallStarsBalance > StarsAmount.zero { - let smallLabelFont = Font.regular(floor(presentationData.listsFontSize.itemListBaseFontSize / 17.0 * 13.0)) - let labelFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize) - let labelColor = presentationData.theme.list.itemSecondaryTextColor - - let attributedString = NSMutableAttributedString() - if overallRevenueBalance > 0 { - attributedString.append(NSAttributedString(string: "#\(formatTonAmountText(revenueBalance, dateTimeFormat: presentationData.dateTimeFormat))", font: labelFont, textColor: labelColor)) - } - if overallStarsBalance > StarsAmount.zero { - if !attributedString.string.isEmpty { - attributedString.append(NSAttributedString(string: " ", font: labelFont, textColor: labelColor)) - } - attributedString.append(NSAttributedString(string: "*", font: labelFont, textColor: labelColor)) - - let formattedLabel = formatStarsAmountText(starsBalance, dateTimeFormat: presentationData.dateTimeFormat) - let starsAttributedString = tonAmountAttributedString(formattedLabel, integralFont: labelFont, fractionalFont: smallLabelFont, color: labelColor, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator).mutableCopy() as! NSMutableAttributedString - attributedString.append(starsAttributedString) - } - if let range = attributedString.string.range(of: "#") { - attributedString.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .ton(tinted: false)), range: NSRange(range, in: attributedString.string)) - attributedString.addAttribute(.baselineOffset, value: 1.5, range: NSRange(range, in: attributedString.string)) - } - if let range = attributedString.string.range(of: "*") { - attributedString.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 1, file: nil, custom: .stars(tinted: false)), range: NSRange(range, in: attributedString.string)) - attributedString.addAttribute(.baselineOffset, value: 1.5, range: NSRange(range, in: attributedString.string)) - } - - items[section]!.append(PeerInfoScreenDisclosureItem(id: ItemBalance, label: .attributedText(attributedString), text: presentationData.strings.PeerInfo_Bot_Balance, icon: PresentationResourcesSettings.balance, action: { - interaction.openStats(.monetization) - })) - } - } - - let settingsTitle: String - switch channel.info { - case .broadcast: - settingsTitle = presentationData.strings.Channel_Info_Settings - case .group: - settingsTitle = presentationData.strings.Group_Info_Settings - } - items[section]!.append(PeerInfoScreenDisclosureItem(id: ItemEdit, label: .none, text: settingsTitle, icon: UIImage(bundleImageName: "Chat/Info/SettingsIcon"), action: { - interaction.openEditing() - })) - } - - if channel.hasPermission(.manageDirect), let personalChannel = data.personalChannel { - let peerId = personalChannel.peer.peerId - items[.channelMonoforum]?.append(PeerInfoScreenPersonalChannelItem(id: ItemPeerPersonalChannel, context: context, data: personalChannel, controller: { [weak interaction] in - guard let interaction else { - return nil - } - return interaction.getController() - }, action: { [weak interaction] in - guard let interaction else { - return - } - interaction.openChat(peerId) - })) - } - } - } - } else if let group = data.peer as? TelegramGroup { - if let cachedData = data.cachedData as? CachedGroupData { - let aboutText: String? - if group.isFake { - aboutText = presentationData.strings.GroupInfo_FakeGroupWarning - } else if group.isScam { - aboutText = presentationData.strings.GroupInfo_ScamGroupWarning - } else if let about = cachedData.about, !about.isEmpty { - aboutText = about - } else { - aboutText = nil - } - - if let aboutText = aboutText { - items[currentPeerInfoSection]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: presentationData.strings.Channel_Info_Description, text: aboutText, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledPrivateBioEntities), action: isMyProfile ? { node, _ in - bioContextAction(node, nil, nil) - } : nil, linkItemAction: bioLinkAction, contextAction: bioContextAction, requestLayout: { animated in - interaction.requestLayout(animated) - })) - } - } - } - - if let peer = data.peer, let members = data.members, case let .shortList(_, memberList) = members { - var canAddMembers = false - if let group = data.peer as? TelegramGroup { - switch group.role { - case .admin, .creator: - canAddMembers = true - case .member: - break - } - if !group.hasBannedPermission(.banAddMembers) { - canAddMembers = true - } - } else if let channel = data.peer as? TelegramChannel { - switch channel.info { - case .broadcast: - break - case .group: - if channel.flags.contains(.isCreator) || channel.hasPermission(.inviteMembers) { - canAddMembers = true - } - } - } - - if canAddMembers { - items[.peerMembers]!.append(PeerInfoScreenActionItem(id: 0, text: presentationData.strings.GroupInfo_AddParticipant, color: .accent, icon: UIImage(bundleImageName: "Contact List/AddMemberIcon"), alignment: .peerList, action: { - interaction.openAddMember() - })) - } - - for member in memberList { - let isAccountPeer = member.id == context.account.peerId - items[.peerMembers]!.append(PeerInfoScreenMemberItem(id: member.id, context: .account(context), enclosingPeer: peer, member: member, isAccount: false, action: isAccountPeer ? nil : { action in - switch action { - case .open: - interaction.openPeerInfo(member.peer, true) - case .promote: - interaction.performMemberAction(member, .promote) - case .restrict: - interaction.performMemberAction(member, .restrict) - case .remove: - interaction.performMemberAction(member, .remove) - } - }, openStories: { sourceView in - interaction.performMemberAction(member, .openStories(sourceView: sourceView)) - })) - } - } - - var result: [(AnyHashable, [PeerInfoScreenItem])] = [] - for section in InfoSection.allCases { - if let sectionItems = items[section], !sectionItems.isEmpty { - result.append((section, sectionItems)) - } - } - return result -} - -private func editingItems(data: PeerInfoScreenData?, boostStatus: ChannelBoostStatus?, state: PeerInfoState, chatLocation: ChatLocation, context: AccountContext, presentationData: PresentationData, interaction: PeerInfoInteraction) -> [(AnyHashable, [PeerInfoScreenItem])] { - enum Section: Int, CaseIterable { - case notifications - case groupLocation - case peerPublicSettings - case peerNote - case peerDataSettings - case peerVerifySettings - case peerSettings - case linkedMonoforum - case peerAdditionalSettings - case peerActions - } - - var items: [Section: [PeerInfoScreenItem]] = [:] - for section in Section.allCases { - items[section] = [] - } - - if let data = data { - if let user = data.peer as? TelegramUser { - let ItemNote: AnyHashable = AnyHashable("note_edit") - let ItemNoteInfo = 1 - - let ItemSuggestBirthdate = 2 - let ItemSuggestPhoto = 3 - let ItemCustomPhoto = 4 - let ItemReset = 5 - let ItemInfo = 6 - let ItemDelete = 7 - let ItemUsername = 8 - let ItemAffiliateProgram = 9 - - let ItemVerify = 10 - - let ItemIntro = 11 - let ItemCommands = 12 - let ItemBotSettings = 13 - let ItemBotInfo = 14 - - if let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) { - items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemUsername, label: .text("@\(user.addressName ?? "")"), text: presentationData.strings.PeerInfo_Bot_Username, icon: PresentationResourcesSettings.bot, action: { - interaction.editingOpenPublicLinkSetup() - })) - - var canSetupRefProgram = false - if let data = context.currentAppConfiguration.with({ $0 }).data, let value = data["starref_program_allowed"] { - if let value = value as? Double { - canSetupRefProgram = value != 0.0 - } else if let value = value as? Bool { - canSetupRefProgram = value - } - } - - if canSetupRefProgram { - let programTitleValue: PeerInfoScreenDisclosureItem.Label - if let cachedData = data.cachedData as? CachedUserData, let starRefProgram = cachedData.starRefProgram, starRefProgram.endDate == nil { - programTitleValue = .labelBadge("\(formatPermille(starRefProgram.commissionPermille))%") - } else { - programTitleValue = .text(presentationData.strings.PeerInfo_ItemAffiliateProgram_ValueOff) - } - items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAffiliateProgram, label: programTitleValue, additionalBadgeLabel: presentationData.strings.Settings_New, text: presentationData.strings.PeerInfo_ItemAffiliateProgram_Title, icon: PresentationResourcesSettings.affiliateProgram, action: { - interaction.editingOpenAffiliateProgram() - })) - } - - if let cachedUserData = data.cachedData as? CachedUserData, let _ = cachedUserData.botInfo?.verifierSettings { - items[.peerVerifySettings]!.append(PeerInfoScreenActionItem(id: ItemVerify, text: presentationData.strings.PeerInfo_VerifyAccounts, icon: UIImage(bundleImageName: "Peer Info/BotVerify"), action: { - interaction.editingOpenVerifyAccounts() - })) - } - - items[.peerSettings]!.append(PeerInfoScreenActionItem(id: ItemIntro, text: presentationData.strings.PeerInfo_Bot_EditIntro, icon: UIImage(bundleImageName: "Peer Info/BotIntro"), action: { - interaction.openPeerMention("botfather", .withBotStartPayload(ChatControllerInitialBotStart(payload: "\(user.addressName ?? "")-intro", behavior: .interactive))) - })) - items[.peerSettings]!.append(PeerInfoScreenActionItem(id: ItemCommands, text: presentationData.strings.PeerInfo_Bot_EditCommands, icon: UIImage(bundleImageName: "Peer Info/BotCommands"), action: { - interaction.openPeerMention("botfather", .withBotStartPayload(ChatControllerInitialBotStart(payload: "\(user.addressName ?? "")-commands", behavior: .interactive))) - })) - items[.peerSettings]!.append(PeerInfoScreenActionItem(id: ItemBotSettings, text: presentationData.strings.PeerInfo_Bot_ChangeSettings, icon: UIImage(bundleImageName: "Peer Info/BotSettings"), action: { - interaction.openPeerMention("botfather", .withBotStartPayload(ChatControllerInitialBotStart(payload: user.addressName ?? "", behavior: .interactive))) - })) - items[.peerSettings]!.append(PeerInfoScreenCommentItem(id: ItemBotInfo, text: presentationData.strings.PeerInfo_Bot_BotFatherInfo, linkAction: { _ in - interaction.openPeerMention("botfather", .default) - })) - } else if !user.flags.contains(.isSupport) { - let compactName = EnginePeer(user).compactDisplayTitle - - if let cachedData = data.cachedData as? CachedUserData { - items[.peerNote]!.append(PeerInfoScreenNoteListItem( - id: ItemNote, - initialValue: chatInputStateStringWithAppliedEntities(cachedData.note?.text ?? "", entities: cachedData.note?.entities ?? []), - valueUpdated: { value in - interaction.updateNote(value) - }, - requestLayout: { animated in - interaction.requestLayout(animated) - } - )) - - items[.peerNote]!.append(PeerInfoScreenCommentItem(id: ItemNoteInfo, text: presentationData.strings.PeerInfo_AddNotesInfo)) - - if let _ = cachedData.sendPaidMessageStars { - - } else { - if cachedData.birthday == nil { - items[.peerDataSettings]!.append(PeerInfoScreenActionItem(id: ItemSuggestBirthdate, text: presentationData.strings.UserInfo_SuggestBirthdate, color: .accent, icon: UIImage(bundleImageName: "Contact List/AddBirthdayIcon"), action: { - interaction.suggestBirthdate() - })) - } - - items[.peerDataSettings]!.append(PeerInfoScreenActionItem(id: ItemSuggestPhoto, text: presentationData.strings.UserInfo_SuggestPhoto(compactName).string, color: .accent, icon: UIImage(bundleImageName: "Peer Info/SuggestAvatar"), action: { - interaction.suggestPhoto() - })) - } - } - - let setText: String - if user.photo.first?.isPersonal == true || state.updatingAvatar != nil { - setText = presentationData.strings.UserInfo_ChangeCustomPhoto(compactName).string - } else { - setText = presentationData.strings.UserInfo_SetCustomPhoto(compactName).string - } - - items[.peerDataSettings]!.append(PeerInfoScreenActionItem(id: ItemCustomPhoto, text: setText, color: .accent, icon: UIImage(bundleImageName: "Settings/SetAvatar"), action: { - interaction.setCustomPhoto() - })) - - if user.photo.first?.isPersonal == true || state.updatingAvatar != nil { - var representation: TelegramMediaImageRepresentation? - var originalIsVideo: Bool? - if let cachedData = data.cachedData as? CachedUserData, case let .known(photo) = cachedData.photo { - representation = photo?.representationForDisplayAtSize(PixelDimensions(width: 28, height: 28)) - originalIsVideo = !(photo?.videoRepresentations.isEmpty ?? true) - } - - let removeText: String - if let originalIsVideo { - removeText = originalIsVideo ? presentationData.strings.UserInfo_ResetCustomVideo : presentationData.strings.UserInfo_ResetCustomPhoto - } else { - removeText = user.photo.first?.hasVideo == true ? presentationData.strings.UserInfo_RemoveCustomVideo : presentationData.strings.UserInfo_RemoveCustomPhoto - } - - let imageSignal: Signal - if let representation, let signal = peerAvatarImage(account: context.account, peerReference: PeerReference(user), authorOfMessage: nil, representation: representation, displayDimensions: CGSize(width: 28.0, height: 28.0)) { - imageSignal = signal - |> map { data -> UIImage? in - return data?.0 - } - } else { - imageSignal = peerAvatarCompleteImage(account: context.account, peer: EnginePeer(user), forceProvidedRepresentation: true, representation: representation, size: CGSize(width: 28.0, height: 28.0)) - } - - items[.peerDataSettings]!.append(PeerInfoScreenActionItem(id: ItemReset, text: removeText, color: .accent, icon: nil, iconSignal: imageSignal, action: { - interaction.resetCustomPhoto() - })) - } - items[.peerDataSettings]!.append(PeerInfoScreenCommentItem(id: ItemInfo, text: presentationData.strings.UserInfo_CustomPhotoInfo(compactName).string)) - } - - if data.isContact { - items[.peerSettings]!.append(PeerInfoScreenActionItem(id: ItemDelete, text: presentationData.strings.UserInfo_DeleteContact, color: .destructive, action: { - interaction.requestDeleteContact() - })) - } - } else if let channel = data.peer as? TelegramChannel { - switch channel.info { - case .broadcast: - let ItemUsername = 1 - let ItemPeerColor = 2 - let ItemInviteLinks = 3 - let ItemDiscussionGroup = 4 - let ItemDeleteChannel = 5 - let ItemReactions = 6 - let ItemAdmins = 7 - let ItemMembers = 8 - let ItemMemberRequests = 9 - let ItemStats = 10 - let ItemBanned = 11 - let ItemRecentActions = 12 - let ItemAffiliatePrograms = 13 - let ItemPostSuggestionsSettings = 14 - let ItemPeerAutoTranslate = 15 - - let isCreator = channel.flags.contains(.isCreator) - - if isCreator { - let linkText: String - if let _ = channel.addressName { - linkText = presentationData.strings.Channel_Setup_TypePublic - } else { - linkText = presentationData.strings.Channel_Setup_TypePrivate - } - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemUsername, label: .text(linkText), text: presentationData.strings.Channel_TypeSetup_Title, icon: UIImage(bundleImageName: "Chat/Info/GroupChannelIcon"), action: { - interaction.editingOpenPublicLinkSetup() - })) - } - - if (isCreator && (channel.addressName?.isEmpty ?? true)) || (!channel.flags.contains(.isCreator) && channel.adminRights?.rights.contains(.canInviteUsers) == true) { - let invitesText: String - if let count = data.invitations?.count, count > 0 { - invitesText = "\(count)" - } else { - invitesText = "" - } - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, icon: UIImage(bundleImageName: "Chat/Info/GroupLinksIcon"), action: { - interaction.editingOpenInviteLinksSetup() - })) - } - - if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) { - let discussionGroupTitle: String - if let _ = data.cachedData as? CachedChannelData { - if let peer = data.linkedDiscussionPeer { - if let addressName = peer.addressName, !addressName.isEmpty { - discussionGroupTitle = "@\(addressName)" - } else { - discussionGroupTitle = EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - } - } else { - discussionGroupTitle = presentationData.strings.Channel_DiscussionGroupAdd - } - } else { - discussionGroupTitle = "..." - } - - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemDiscussionGroup, label: .text(discussionGroupTitle), text: presentationData.strings.Channel_DiscussionGroup, icon: UIImage(bundleImageName: "Chat/Info/GroupDiscussionIcon"), action: { - interaction.editingOpenDiscussionGroupSetup() - })) - } - - if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) { - let label: String - if let cachedData = data.cachedData as? CachedChannelData, case let .known(reactionSettings) = cachedData.reactionSettings { - switch reactionSettings.allowedReactions { - case .all: - label = presentationData.strings.PeerInfo_LabelAllReactions - case .empty: - if let starsAllowed = reactionSettings.starsAllowed, starsAllowed { - label = "1" - } else { - label = presentationData.strings.PeerInfo_ReactionsDisabled - } - case let .limited(reactions): - var countValue = reactions.count - if let starsAllowed = reactionSettings.starsAllowed, starsAllowed { - countValue += 1 - } - label = "\(countValue)" - } - } else { - label = "" - } - let additionalBadgeLabel: String? = nil - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemReactions, label: .text(label), additionalBadgeLabel: additionalBadgeLabel, text: presentationData.strings.PeerInfo_Reactions, icon: UIImage(bundleImageName: "Settings/Menu/Reactions"), action: { - interaction.editingOpenReactionsSetup() - })) - } - - if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) { - var colors: [PeerNameColors.Colors] = [] - if let nameColor = channel.nameColor.flatMap({ context.peerNameColors.get($0, dark: presentationData.theme.overallDarkAppearance) }) { - colors.append(nameColor) - } - if let profileColor = channel.profileColor.flatMap({ context.peerNameColors.getProfile($0, dark: presentationData.theme.overallDarkAppearance, subject: .palette) }) { - colors.append(profileColor) - } - let colorImage = generateSettingsMenuPeerColorsLabelIcon(colors: colors) - - var boostIcon: UIImage? - if let approximateBoostLevel = channel.approximateBoostLevel, approximateBoostLevel < 1 { - boostIcon = generateDisclosureActionBoostLevelBadgeImage(text: presentationData.strings.Channel_Info_BoostLevelPlusBadge("1").string) - } else { - /*let labelText = NSAttributedString(string: presentationData.strings.Settings_New, font: Font.medium(11.0), textColor: presentationData.theme.list.itemCheckColors.foregroundColor) - let labelBounds = labelText.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: [.usesLineFragmentOrigin], context: nil) - let labelSize = CGSize(width: ceil(labelBounds.width), height: ceil(labelBounds.height)) - let badgeSize = CGSize(width: labelSize.width + 8.0, height: labelSize.height + 2.0 + 1.0) - boostIcon = generateImage(badgeSize, rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - - let rect = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height - UIScreenPixel * 2.0)) - - context.addPath(UIBezierPath(roundedRect: rect, cornerRadius: 5.0).cgPath) - context.setFillColor(presentationData.theme.list.itemCheckColors.fillColor.cgColor) - context.fillPath() - - UIGraphicsPushContext(context) - labelText.draw(at: CGPoint(x: 4.0, y: 1.0 + UIScreenPixel)) - UIGraphicsPopContext() - })*/ - } - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPeerColor, label: .image(colorImage, colorImage.size), additionalBadgeIcon: boostIcon, text: presentationData.strings.Channel_Info_AppearanceItem, icon: UIImage(bundleImageName: "Chat/Info/NameColorIcon"), action: { - interaction.editingOpenNameColorSetup() - })) - - let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) - var isLocked = true - if let boostLevel = boostStatus?.level, boostLevel >= BoostSubject.autoTranslate.requiredLevel(group: false, context: context, configuration: premiumConfiguration) { - isLocked = false - } - items[.peerSettings]!.append(PeerInfoScreenSwitchItem(id: ItemPeerAutoTranslate, text: presentationData.strings.Channel_Info_AutoTranslate, value: channel.flags.contains(.autoTranslateEnabled), icon: UIImage(bundleImageName: "Settings/Menu/AutoTranslate"), isLocked: isLocked, toggled: { value in - if isLocked { - interaction.displayAutoTranslateLocked() - } else { - interaction.editingToggleAutoTranslate(value) - } - })) - } - - if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) { - let labelString: NSAttributedString - if channel.linkedMonoforumId != nil { - if let monoforumPeer = data.linkedMonoforumPeer as? TelegramChannel { - if let sendPaidMessageStars = monoforumPeer.sendPaidMessageStars { - let formattedLabel = formatStarsAmountText(sendPaidMessageStars, dateTimeFormat: presentationData.dateTimeFormat) - let smallLabelFont = Font.regular(floor(presentationData.listsFontSize.itemListBaseFontSize / 17.0 * 13.0)) - let labelFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize) - let labelColor = presentationData.theme.list.itemSecondaryTextColor - let attributedString = tonAmountAttributedString(formattedLabel, integralFont: labelFont, fractionalFont: smallLabelFont, color: labelColor, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator).mutableCopy() as! NSMutableAttributedString - attributedString.insert(NSAttributedString(string: "*", font: labelFont, textColor: labelColor), at: 0) - - if let range = attributedString.string.range(of: "*") { - attributedString.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: false)), range: NSRange(range, in: attributedString.string)) - attributedString.addAttribute(.baselineOffset, value: 1.5, range: NSRange(range, in: attributedString.string)) - } - labelString = attributedString - } else { - let labelFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize) - let labelColor = presentationData.theme.list.itemSecondaryTextColor - - labelString = NSAttributedString(string: presentationData.strings.PeerInfo_AllowChannelMessages_Free, font: labelFont, textColor: labelColor) - } - } else { - let labelFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize) - let labelColor = presentationData.theme.list.itemSecondaryTextColor - - labelString = NSAttributedString(string: " ", font: labelFont, textColor: labelColor) - } - } else { - let labelFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize) - let labelColor = presentationData.theme.list.itemSecondaryTextColor - - labelString = NSAttributedString(string: presentationData.strings.PeerInfo_AllowChannelMessages_Off, font: labelFont, textColor: labelColor) - } - - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPostSuggestionsSettings, label: .attributedText(labelString), additionalBadgeLabel: presentationData.strings.Settings_New, text: presentationData.strings.PeerInfo_AllowChannelMessages, icon: PresentationResourcesSettings.channelMessages, action: { - interaction.editingOpenPostSuggestionsSetup() - })) - - if let personalChannel = data.personalChannel { - let peerId = personalChannel.peer.peerId - items[.linkedMonoforum]?.append(PeerInfoScreenPersonalChannelItem(id: 1, context: context, data: personalChannel, controller: { [weak interaction] in - guard let interaction else { - return nil - } - return interaction.getController() - }, action: { [weak interaction] in - guard let interaction else { - return - } - interaction.openChat(peerId) - })) - } - } - - var canEditMembers = false - if channel.hasPermission(.banMembers) && (channel.adminRights != nil || channel.flags.contains(.isCreator)) { - canEditMembers = true - } - if canEditMembers { - let adminCount: Int32 - let memberCount: Int32 - if let cachedData = data.cachedData as? CachedChannelData { - adminCount = cachedData.participantsSummary.adminCount ?? 0 - memberCount = cachedData.participantsSummary.memberCount ?? 0 - } else { - adminCount = 0 - memberCount = 0 - } - - items[.peerAdditionalSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAdmins, label: .text("\(adminCount == 0 ? "" : "\(presentationStringsFormattedNumber(adminCount, presentationData.dateTimeFormat.groupingSeparator))")"), text: presentationData.strings.GroupInfo_Administrators, icon: UIImage(bundleImageName: "Chat/Info/GroupAdminsIcon"), action: { - interaction.openParticipantsSection(.admins) - })) - items[.peerAdditionalSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemMembers, label: .text("\(memberCount == 0 ? "" : "\(presentationStringsFormattedNumber(memberCount, presentationData.dateTimeFormat.groupingSeparator))")"), text: presentationData.strings.Channel_Info_Subscribers, icon: UIImage(bundleImageName: "Chat/Info/GroupMembersIcon"), action: { - interaction.openParticipantsSection(.members) - })) - - if let count = data.requests?.count, count > 0 { - items[.peerAdditionalSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemMemberRequests, label: .badge(presentationStringsFormattedNumber(count, presentationData.dateTimeFormat.groupingSeparator), presentationData.theme.list.itemAccentColor), text: presentationData.strings.GroupInfo_MemberRequests, icon: UIImage(bundleImageName: "Chat/Info/GroupRequestsIcon"), action: { - interaction.openParticipantsSection(.memberRequests) - })) - } - } - - if let cachedData = data.cachedData as? CachedChannelData, cachedData.flags.contains(.canViewStats) { - items[.peerAdditionalSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemStats, label: .none, text: presentationData.strings.Channel_Info_Stats, icon: UIImage(bundleImageName: "Chat/Info/StatsIcon"), action: { - interaction.openStats(.stats) - })) - } - - if canEditMembers { - let bannedCount: Int32 - if let cachedData = data.cachedData as? CachedChannelData { - bannedCount = cachedData.participantsSummary.kickedCount ?? 0 - } else { - bannedCount = 0 - } - items[.peerAdditionalSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemBanned, label: .text("\(bannedCount == 0 ? "" : "\(presentationStringsFormattedNumber(bannedCount, presentationData.dateTimeFormat.groupingSeparator))")"), text: presentationData.strings.GroupInfo_Permissions_Removed, icon: UIImage(bundleImageName: "Chat/Info/GroupRemovedIcon"), action: { - interaction.openParticipantsSection(.banned) - })) - - items[.peerAdditionalSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemRecentActions, label: .none, text: presentationData.strings.Group_Info_AdminLog, icon: UIImage(bundleImageName: "Chat/Info/RecentActionsIcon"), action: { - interaction.openRecentActions() - })) - } - - if channel.hasPermission(.changeInfo) { - var canJoinRefProgram = false - if let data = context.currentAppConfiguration.with({ $0 }).data, let value = data["starref_connect_allowed"] { - if let value = value as? Double { - canJoinRefProgram = value != 0.0 - } else if let value = value as? Bool { - canJoinRefProgram = value - } - } - - if canJoinRefProgram { - items[.peerAdditionalSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAffiliatePrograms, label: .text(""), additionalBadgeLabel: nil, text: presentationData.strings.PeerInfo_ItemAffiliatePrograms_Title, icon: PresentationResourcesSettings.affiliateProgram, action: { - interaction.editingOpenAffiliateProgram() - })) - } - } - - if isCreator { //if let cachedData = data.cachedData as? CachedChannelData, cachedData.flags.contains(.canDeleteHistory) { - items[.peerActions]!.append(PeerInfoScreenActionItem(id: ItemDeleteChannel, text: presentationData.strings.ChannelInfo_DeleteChannel, color: .destructive, icon: nil, alignment: .natural, action: { - interaction.openDeletePeer() - })) - } - case .group: - let ItemUsername = 101 - let ItemInviteLinks = 102 - let ItemLinkedChannel = 103 - let ItemPreHistory = 104 - let ItemMembers = 106 - let ItemPermissions = 107 - let ItemAdmins = 108 - let ItemMemberRequests = 109 - let ItemRemovedUsers = 110 - let ItemRecentActions = 111 - let ItemLocationHeader = 112 - let ItemLocation = 113 - let ItemLocationSetup = 114 - let ItemDeleteGroup = 115 - let ItemReactions = 116 - let ItemTopics = 117 - let ItemTopicsText = 118 - let ItemAppearance = 119 - - let isCreator = channel.flags.contains(.isCreator) - let isPublic = channel.addressName != nil - - if let cachedData = data.cachedData as? CachedChannelData { - if isCreator, let location = cachedData.peerGeoLocation { - items[.groupLocation]!.append(PeerInfoScreenHeaderItem(id: ItemLocationHeader, text: presentationData.strings.GroupInfo_Location.uppercased())) - - let imageSignal = chatMapSnapshotImage(engine: context.engine, resource: MapSnapshotMediaResource(latitude: location.latitude, longitude: location.longitude, width: 90, height: 90)) - items[.groupLocation]!.append(PeerInfoScreenAddressItem( - id: ItemLocation, - label: "", - text: location.address.replacingOccurrences(of: ", ", with: "\n"), - imageSignal: imageSignal, - action: { - interaction.openLocation() - } - )) - if cachedData.flags.contains(.canChangePeerGeoLocation) { - items[.groupLocation]!.append(PeerInfoScreenActionItem(id: ItemLocationSetup, text: presentationData.strings.Group_Location_ChangeLocation, action: { - interaction.editingOpenSetupLocation() - })) - } - } - - if isCreator || (channel.adminRights != nil && channel.hasPermission(.pinMessages)) { - if cachedData.peerGeoLocation != nil { - if isCreator { - let linkText: String - if let username = channel.addressName { - linkText = "@\(username)" - } else { - linkText = presentationData.strings.GroupInfo_PublicLinkAdd - } - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemUsername, label: .text(linkText), text: presentationData.strings.GroupInfo_PublicLink, icon: UIImage(bundleImageName: "Chat/Info/GroupLinksIcon"), action: { - interaction.editingOpenPublicLinkSetup() - })) - } - } else { - if cachedData.flags.contains(.canChangeUsername) { - items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemUsername, label: .text(isPublic ? presentationData.strings.Group_Setup_TypePublic : presentationData.strings.Group_Setup_TypePrivate), text: presentationData.strings.GroupInfo_GroupType, icon: UIImage(bundleImageName: "Chat/Info/GroupTypeIcon"), action: { - interaction.editingOpenPublicLinkSetup() - })) - } - } - } - - if (isCreator && (channel.addressName?.isEmpty ?? true) && cachedData.peerGeoLocation == nil) || (!isCreator && channel.adminRights?.rights.contains(.canInviteUsers) == true) { - let invitesText: String - if let count = data.invitations?.count, count > 0 { - invitesText = "\(count)" - } else { - invitesText = "" - } - - items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, icon: UIImage(bundleImageName: "Chat/Info/GroupLinksIcon"), action: { - interaction.editingOpenInviteLinksSetup() - })) - } - - if (isCreator || (channel.adminRights != nil && channel.hasPermission(.pinMessages))) && cachedData.peerGeoLocation == nil { - if let linkedDiscussionPeer = data.linkedDiscussionPeer { - let peerTitle: String - if let addressName = linkedDiscussionPeer.addressName, !addressName.isEmpty { - peerTitle = "@\(addressName)" - } else { - peerTitle = EnginePeer(linkedDiscussionPeer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - } - items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemLinkedChannel, label: .text(peerTitle), text: presentationData.strings.Group_LinkedChannel, icon: UIImage(bundleImageName: "Chat/Info/GroupLinkedChannelIcon"), action: { - interaction.editingOpenDiscussionGroupSetup() - })) - } - - if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) { - let label: String - if let cachedData = data.cachedData as? CachedChannelData, case let .known(reactionSettings) = cachedData.reactionSettings { - switch reactionSettings.allowedReactions { - case .all: - label = presentationData.strings.PeerInfo_LabelAllReactions - case .empty: - label = presentationData.strings.PeerInfo_ReactionsDisabled - case let .limited(reactions): - label = "\(reactions.count)" - } - } else { - label = "" - } - items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemReactions, label: .text(label), text: presentationData.strings.PeerInfo_Reactions, icon: UIImage(bundleImageName: "Settings/Menu/Reactions"), action: { - interaction.editingOpenReactionsSetup() - })) - } - } else { - if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) { - let label: String - if let cachedData = data.cachedData as? CachedChannelData, case let .known(reactionSettings) = cachedData.reactionSettings { - switch reactionSettings.allowedReactions { - case .all: - label = presentationData.strings.PeerInfo_LabelAllReactions - case .empty: - label = presentationData.strings.PeerInfo_ReactionsDisabled - case let .limited(reactions): - label = "\(reactions.count)" - } - } else { - label = "" - } - items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemReactions, label: .text(label), text: presentationData.strings.PeerInfo_Reactions, icon: UIImage(bundleImageName: "Settings/Menu/Reactions"), action: { - interaction.editingOpenReactionsSetup() - })) - } - } - - if isCreator || channel.adminRights?.rights.contains(.canChangeInfo) == true { - var colors: [PeerNameColors.Colors] = [] - if let nameColor = channel.nameColor.flatMap({ context.peerNameColors.get($0, dark: presentationData.theme.overallDarkAppearance) }) { - colors.append(nameColor) - } - if let profileColor = channel.profileColor.flatMap({ context.peerNameColors.getProfile($0, dark: presentationData.theme.overallDarkAppearance, subject: .palette) }) { - colors.append(profileColor) - } - let colorImage = generateSettingsMenuPeerColorsLabelIcon(colors: colors) - - var boostIcon: UIImage? - if let approximateBoostLevel = channel.approximateBoostLevel, approximateBoostLevel < 1 { - boostIcon = generateDisclosureActionBoostLevelBadgeImage(text: presentationData.strings.Channel_Info_BoostLevelPlusBadge("1").string) - } else { - boostIcon = nil - /*let labelText = NSAttributedString(string: presentationData.strings.Settings_New, font: Font.medium(11.0), textColor: presentationData.theme.list.itemCheckColors.foregroundColor) - let labelBounds = labelText.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: [.usesLineFragmentOrigin], context: nil) - let labelSize = CGSize(width: ceil(labelBounds.width), height: ceil(labelBounds.height)) - let badgeSize = CGSize(width: labelSize.width + 8.0, height: labelSize.height + 2.0 + 1.0) - boostIcon = generateImage(badgeSize, rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - - let rect = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height - UIScreenPixel * 2.0)) - - context.addPath(UIBezierPath(roundedRect: rect, cornerRadius: 5.0).cgPath) - context.setFillColor(presentationData.theme.list.itemCheckColors.fillColor.cgColor) - context.fillPath() - - UIGraphicsPushContext(context) - labelText.draw(at: CGPoint(x: 4.0, y: 1.0 + UIScreenPixel)) - UIGraphicsPopContext() - })*/ - } - items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAppearance, label: .image(colorImage, colorImage.size), additionalBadgeIcon: boostIcon, text: presentationData.strings.Channel_Info_AppearanceItem, icon: UIImage(bundleImageName: "Chat/Info/NameColorIcon"), action: { - interaction.editingOpenNameColorSetup() - })) - } - - if (isCreator || (channel.adminRights != nil && channel.hasPermission(.banMembers))) && cachedData.peerGeoLocation == nil, !isPublic, case .known(nil) = cachedData.linkedDiscussionPeerId, !channel.isForumOrMonoForum { - items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPreHistory, label: .text(cachedData.flags.contains(.preHistoryEnabled) ? presentationData.strings.GroupInfo_GroupHistoryVisible : presentationData.strings.GroupInfo_GroupHistoryHidden), text: presentationData.strings.GroupInfo_GroupHistoryShort, icon: UIImage(bundleImageName: "Chat/Info/GroupDiscussionIcon"), action: { - interaction.editingOpenPreHistorySetup() - })) - } - - if isCreator, let appConfiguration = data.appConfiguration { - var minParticipants = 200 - if let data = appConfiguration.data, let value = data["forum_upgrade_participants_min"] as? Double { - minParticipants = Int(value) - } - - var canSetupTopics = false - var topicsLimitedReason: TopicsLimitedReason? - if channel.flags.contains(.isForum) { - canSetupTopics = true - } else if case let .known(value) = cachedData.linkedDiscussionPeerId, value != nil { - canSetupTopics = true - topicsLimitedReason = .discussion - } else if let memberCount = cachedData.participantsSummary.memberCount { - canSetupTopics = true - if Int(memberCount) < minParticipants { - topicsLimitedReason = .participants(minParticipants) - } - } - - if canSetupTopics { - let label = channel.flags.contains(.isForum) ? presentationData.strings.PeerInfo_OptionTopics_Enabled : presentationData.strings.PeerInfo_OptionTopics_Disabled - items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemTopics, label: .text(label), text: presentationData.strings.PeerInfo_OptionTopics, icon: UIImage(bundleImageName: "Settings/Menu/Topics"), action: { - if let topicsLimitedReason = topicsLimitedReason { - interaction.displayTopicsLimited(topicsLimitedReason) - } else { - interaction.openForumSettings() - } - })) - - items[.peerDataSettings]!.append(PeerInfoScreenCommentItem(id: ItemTopicsText, text: presentationData.strings.PeerInfo_OptionTopicsText)) - } - } - - var canViewAdminsAndBanned = false - if let _ = channel.adminRights { - canViewAdminsAndBanned = true - } else if channel.flags.contains(.isCreator) { - canViewAdminsAndBanned = true - } - - if canViewAdminsAndBanned { - var activePermissionCount: Int? - if let defaultBannedRights = channel.defaultBannedRights { - var count = 0 - for (right, _) in allGroupPermissionList(peer: .channel(channel), expandMedia: true) { - if right == .banSendMedia { - if banSendMediaSubList().allSatisfy({ !defaultBannedRights.flags.contains($0.0) }) { - count += 1 - } - } else { - if !defaultBannedRights.flags.contains(right) { - count += 1 - } - } - } - activePermissionCount = count - } - - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemMembers, label: .text(cachedData.participantsSummary.memberCount.flatMap { "\(presentationStringsFormattedNumber($0, presentationData.dateTimeFormat.groupingSeparator))" } ?? ""), text: presentationData.strings.Group_Info_Members, icon: UIImage(bundleImageName: "Chat/Info/GroupMembersIcon"), action: { - interaction.openParticipantsSection(.members) - })) - if !channel.flags.contains(.isGigagroup) { - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPermissions, label: .text(activePermissionCount.flatMap({ "\($0)/\(allGroupPermissionList(peer: .channel(channel), expandMedia: true).count)" }) ?? ""), text: presentationData.strings.GroupInfo_Permissions, icon: UIImage(bundleImageName: "Settings/Menu/SetPasscode"), action: { - interaction.openPermissions() - })) - } - - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAdmins, label: .text(cachedData.participantsSummary.adminCount.flatMap { "\(presentationStringsFormattedNumber($0, presentationData.dateTimeFormat.groupingSeparator))" } ?? ""), text: presentationData.strings.GroupInfo_Administrators, icon: UIImage(bundleImageName: "Chat/Info/GroupAdminsIcon"), action: { - interaction.openParticipantsSection(.admins) - })) - - if let count = data.requests?.count, count > 0 { - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemMemberRequests, label: .badge(presentationStringsFormattedNumber(count, presentationData.dateTimeFormat.groupingSeparator), presentationData.theme.list.itemAccentColor), text: presentationData.strings.GroupInfo_MemberRequests, icon: UIImage(bundleImageName: "Chat/Info/GroupRequestsIcon"), action: { - interaction.openParticipantsSection(.memberRequests) - })) - } - - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemRemovedUsers, label: .text(cachedData.participantsSummary.kickedCount.flatMap { $0 > 0 ? "\(presentationStringsFormattedNumber($0, presentationData.dateTimeFormat.groupingSeparator))" : "" } ?? ""), text: presentationData.strings.GroupInfo_Permissions_Removed, icon: UIImage(bundleImageName: "Chat/Info/GroupRemovedIcon"), action: { - interaction.openParticipantsSection(.banned) - })) - - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemRecentActions, label: .none, text: presentationData.strings.Group_Info_AdminLog, icon: UIImage(bundleImageName: "Chat/Info/RecentActionsIcon"), action: { - interaction.openRecentActions() - })) - } - - if isCreator { - items[.peerActions]!.append(PeerInfoScreenActionItem(id: ItemDeleteGroup, text: presentationData.strings.Group_DeleteGroup, color: .destructive, icon: nil, alignment: .natural, action: { - interaction.openDeletePeer() - })) - } - } - } - } else if let group = data.peer as? TelegramGroup { - let ItemUsername = 101 - let ItemInviteLinks = 102 - let ItemPreHistory = 103 - let ItemPermissions = 104 - let ItemAdmins = 105 - let ItemMemberRequests = 106 - let ItemReactions = 107 - let ItemTopics = 108 - let ItemTopicsText = 109 - - var canViewAdminsAndBanned = false - - if case .creator = group.role { - if let cachedData = data.cachedData as? CachedGroupData { - if cachedData.flags.contains(.canChangeUsername) { - items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemUsername, label: .text(presentationData.strings.Group_Setup_TypePrivate), text: presentationData.strings.GroupInfo_GroupType, icon: UIImage(bundleImageName: "Chat/Info/GroupTypeIcon"), action: { - interaction.editingOpenPublicLinkSetup() - })) - } - } - - if (group.addressName?.isEmpty ?? true) { - let invitesText: String - if let count = data.invitations?.count, count > 0 { - invitesText = "\(count)" - } else { - invitesText = "" - } - - items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, icon: UIImage(bundleImageName: "Chat/Info/GroupLinksIcon"), action: { - interaction.editingOpenInviteLinksSetup() - })) - } - - items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPreHistory, label: .text(presentationData.strings.GroupInfo_GroupHistoryHidden), text: presentationData.strings.GroupInfo_GroupHistoryShort, icon: UIImage(bundleImageName: "Chat/Info/GroupDiscussionIcon"), action: { - interaction.editingOpenPreHistorySetup() - })) - - var canSetupTopics = false - if case .creator = group.role { - canSetupTopics = true - } - var topicsLimitedReason: TopicsLimitedReason? - if let appConfiguration = data.appConfiguration { - var minParticipants = 200 - if let data = appConfiguration.data, let value = data["forum_upgrade_participants_min"] as? Double { - minParticipants = Int(value) - } - if Int(group.participantCount) < minParticipants { - topicsLimitedReason = .participants(minParticipants) - } - } - - if canSetupTopics { - items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemTopics, label: .text(presentationData.strings.PeerInfo_OptionTopics_Disabled), text: presentationData.strings.PeerInfo_OptionTopics, icon: UIImage(bundleImageName: "Settings/Menu/Topics"), action: { - if let topicsLimitedReason = topicsLimitedReason { - interaction.displayTopicsLimited(topicsLimitedReason) - } else { - interaction.openForumSettings() - } - })) - - items[.peerPublicSettings]!.append(PeerInfoScreenCommentItem(id: ItemTopicsText, text: presentationData.strings.PeerInfo_OptionTopicsText)) - } - - let label: String - if let cachedData = data.cachedData as? CachedGroupData, case let .known(reactionSettings) = cachedData.reactionSettings { - switch reactionSettings.allowedReactions { - case .all: - label = presentationData.strings.PeerInfo_LabelAllReactions - case .empty: - label = presentationData.strings.PeerInfo_ReactionsDisabled - case let .limited(reactions): - label = "\(reactions.count)" - } - } else { - label = "" - } - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemReactions, label: .text(label), text: presentationData.strings.PeerInfo_Reactions, icon: UIImage(bundleImageName: "Settings/Menu/Reactions"), action: { - interaction.editingOpenReactionsSetup() - })) - - canViewAdminsAndBanned = true - } else if case let .admin(rights, _) = group.role { - let label: String - if let cachedData = data.cachedData as? CachedGroupData, case let .known(reactionSettings) = cachedData.reactionSettings { - switch reactionSettings.allowedReactions { - case .all: - label = presentationData.strings.PeerInfo_LabelAllReactions - case .empty: - label = presentationData.strings.PeerInfo_ReactionsDisabled - case let .limited(reactions): - label = "\(reactions.count)" - } - } else { - label = "" - } - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemReactions, label: .text(label), text: presentationData.strings.PeerInfo_Reactions, icon: UIImage(bundleImageName: "Settings/Menu/Reactions"), action: { - interaction.editingOpenReactionsSetup() - })) - - if rights.rights.contains(.canInviteUsers) { - let invitesText: String - if let count = data.invitations?.count, count > 0 { - invitesText = "\(count)" - } else { - invitesText = "" - } - - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemInviteLinks, label: .text(invitesText), text: presentationData.strings.GroupInfo_InviteLinks, icon: UIImage(bundleImageName: "Chat/Info/GroupLinksIcon"), action: { - interaction.editingOpenInviteLinksSetup() - })) - } - - canViewAdminsAndBanned = true - } - - if canViewAdminsAndBanned { - var activePermissionCount: Int? - if let defaultBannedRights = group.defaultBannedRights { - var count = 0 - for (right, _) in allGroupPermissionList(peer: .legacyGroup(group), expandMedia: true) { - if right == .banSendMedia { - if banSendMediaSubList().allSatisfy({ !defaultBannedRights.flags.contains($0.0) }) { - count += 1 - } - } else { - if !defaultBannedRights.flags.contains(right) { - count += 1 - } - } - } - activePermissionCount = count - } - - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPermissions, label: .text(activePermissionCount.flatMap({ "\($0)/\(allGroupPermissionList(peer: .legacyGroup(group), expandMedia: true).count)" }) ?? ""), text: presentationData.strings.GroupInfo_Permissions, icon: UIImage(bundleImageName: "Settings/Menu/SetPasscode"), action: { - interaction.openPermissions() - })) - - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAdmins, text: presentationData.strings.GroupInfo_Administrators, icon: UIImage(bundleImageName: "Chat/Info/GroupAdminsIcon"), action: { - interaction.openParticipantsSection(.admins) - })) - - if let count = data.requests?.count, count > 0 { - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemMemberRequests, label: .badge(presentationStringsFormattedNumber(count, presentationData.dateTimeFormat.groupingSeparator), presentationData.theme.list.itemAccentColor), text: presentationData.strings.GroupInfo_MemberRequests, icon: UIImage(bundleImageName: "Chat/Info/GroupRequestsIcon"), action: { - interaction.openParticipantsSection(.memberRequests) - })) - } - } - } - } - - var result: [(AnyHashable, [PeerInfoScreenItem])] = [] - for section in Section.allCases { - if let sectionItems = items[section], !sectionItems.isEmpty { - result.append((section, sectionItems)) - } - } - return result -} - final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodeProtocol, ASScrollViewDelegate { - private weak var controller: PeerInfoScreenImpl? + weak var controller: PeerInfoScreenImpl? - private let context: AccountContext + let context: AccountContext let peerId: PeerId - private let isOpenedFromChat: Bool - private let videoCallsEnabled: Bool - private let callMessages: [Message] - private let chatLocation: ChatLocation - private let chatLocationContextHolder: Atomic - private let switchToStoryFolder: Int64? - private let switchToGiftsTarget: PeerInfoSwitchToGiftsTarget? - private let sharedMediaFromForumTopic: (EnginePeer.Id, Int64)? + let isOpenedFromChat: Bool + let videoCallsEnabled: Bool + let callMessages: [Message] + let chatLocation: ChatLocation + let chatLocationContextHolder: Atomic + let switchToStoryFolder: Int64? + let switchToGiftsTarget: PeerInfoSwitchToGiftsTarget? + let sharedMediaFromForumTopic: (EnginePeer.Id, Int64)? let isSettings: Bool let isMyProfile: Bool - private let isMediaOnly: Bool + let isMediaOnly: Bool let initialExpandPanes: Bool - private var presentationData: PresentationData + private(set) var presentationData: PresentationData - fileprivate let cachedDataPromise = Promise() + let cachedDataPromise = Promise() let scrollNode: ASScrollNode - private let edgeEffectView: EdgeEffectView + let edgeEffectView: EdgeEffectView let headerNode: PeerInfoHeaderNode - private var regularSections: [AnyHashable: PeerInfoScreenItemSectionContainerNode] = [:] - private var editingSections: [AnyHashable: PeerInfoScreenItemSectionContainerNode] = [:] - private let paneContainerNode: PeerInfoPaneContainerNode - private var ignoreScrolling: Bool = false - private lazy var hapticFeedback = { HapticFeedback() }() + var regularSections: [AnyHashable: PeerInfoScreenItemSectionContainerNode] = [:] + var editingSections: [AnyHashable: PeerInfoScreenItemSectionContainerNode] = [:] + let paneContainerNode: PeerInfoPaneContainerNode + var ignoreScrolling: Bool = false + lazy var hapticFeedback = { HapticFeedback() }() - private var customStatusData: (PeerInfoStatusData?, PeerInfoStatusData?, CGFloat?) - private let customStatusPromise = Promise<(PeerInfoStatusData?, PeerInfoStatusData?, CGFloat?)>((nil, nil, nil)) - private var customStatusDisposable: Disposable? + var customStatusData: (PeerInfoStatusData?, PeerInfoStatusData?, CGFloat?) + let customStatusPromise = Promise<(PeerInfoStatusData?, PeerInfoStatusData?, CGFloat?)>((nil, nil, nil)) + var customStatusDisposable: Disposable? - private var refreshMessageTagStatsDisposable: Disposable? + var refreshMessageTagStatsDisposable: Disposable? - private var searchDisplayController: SearchDisplayController? + var searchDisplayController: SearchDisplayController? private var _interaction: PeerInfoInteraction? - fileprivate var interaction: PeerInfoInteraction { + var interaction: PeerInfoInteraction { return self._interaction! } - private var _chatInterfaceInteraction: ChatControllerInteraction? - private var chatInterfaceInteraction: ChatControllerInteraction { + var _chatInterfaceInteraction: ChatControllerInteraction? + var chatInterfaceInteraction: ChatControllerInteraction { return self._chatInterfaceInteraction! } - private var hiddenMediaDisposable: Disposable? - private let hiddenAvatarRepresentationDisposable = MetaDisposable() + var hiddenMediaDisposable: Disposable? + let hiddenAvatarRepresentationDisposable = MetaDisposable() - private var autoTranslateDisposable: Disposable? + var autoTranslateDisposable: Disposable? - private var resolvePeerByNameDisposable: MetaDisposable? - private let navigationActionDisposable = MetaDisposable() - private let enqueueMediaMessageDisposable = MetaDisposable() + var resolvePeerByNameDisposable: MetaDisposable? + let navigationActionDisposable = MetaDisposable() + let enqueueMediaMessageDisposable = MetaDisposable() private(set) var validLayout: (ContainerViewLayout, CGFloat)? private(set) var data: PeerInfoScreenData? + var state = PeerInfoState( isEditing: false, selectedMessageIds: nil, @@ -3070,62 +278,64 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro updatingBirthDate: nil, personalChannels: nil ) - private var forceIsContactPromise = ValuePromise(false) - private let nearbyPeerDistance: Int32? - private let reactionSourceMessageId: MessageId? - private var dataDisposable: Disposable? + var forceIsContactPromise = ValuePromise(false) + let nearbyPeerDistance: Int32? + let reactionSourceMessageId: MessageId? + var dataDisposable: Disposable? - private let activeActionDisposable = MetaDisposable() - private let resolveUrlDisposable = MetaDisposable() - private let toggleShouldChannelMessagesSignaturesDisposable = MetaDisposable() - private let toggleMessageCopyProtectionDisposable = MetaDisposable() - private let selectAddMemberDisposable = MetaDisposable() - private let addMemberDisposable = MetaDisposable() - private let preloadHistoryDisposable = MetaDisposable() - private var shareStatusDisposable: MetaDisposable? - private let joinChannelDisposable = MetaDisposable() + let activeActionDisposable = MetaDisposable() + let resolveUrlDisposable = MetaDisposable() + let toggleShouldChannelMessagesSignaturesDisposable = MetaDisposable() + let toggleMessageCopyProtectionDisposable = MetaDisposable() + let selectAddMemberDisposable = MetaDisposable() + let addMemberDisposable = MetaDisposable() + let preloadHistoryDisposable = MetaDisposable() + var shareStatusDisposable: MetaDisposable? + let joinChannelDisposable = MetaDisposable() let updateAvatarDisposable = MetaDisposable() - private let currentAvatarMixin = Atomic(value: nil) + let currentAvatarMixin = Atomic(value: nil) - private var groupMembersSearchContext: GroupMembersSearchContext? + var groupMembersSearchContext: GroupMembersSearchContext? - private let displayAsPeersPromise = Promise<[FoundPeer]>([]) + let displayAsPeersPromise = Promise<[FoundPeer]>([]) - fileprivate let accountsAndPeers = Promise<[(AccountContext, EnginePeer, Int32)]>() - fileprivate let activeSessionsContextAndCount = Promise<(ActiveSessionsContext, Int, WebSessionsContext)?>() - private let notificationExceptions = Promise() - fileprivate let privacySettings = Promise() - private let archivedPacks = Promise<[ArchivedStickerPackItem]?>() - private let blockedPeers = Promise(nil) - private let hasTwoStepAuth = Promise(nil) - private let twoStepAccessConfiguration = Promise(nil) - private let twoStepAuthData = Promise(nil) - private let supportPeerDisposable = MetaDisposable() - private let tipsPeerDisposable = MetaDisposable() - private let cachedFaq = Promise(nil) + let accountsAndPeers = Promise<[(AccountContext, EnginePeer, Int32)]>() + let activeSessionsContextAndCount = Promise<(ActiveSessionsContext, Int, WebSessionsContext)?>() + let notificationExceptions = Promise() + let privacySettings = Promise() + let archivedPacks = Promise<[ArchivedStickerPackItem]?>() + let blockedPeers = Promise(nil) + let hasTwoStepAuth = Promise(nil) + let twoStepAccessConfiguration = Promise(nil) + let twoStepAuthData = Promise(nil) + let supportPeerDisposable = MetaDisposable() + let tipsPeerDisposable = MetaDisposable() + let cachedFaq = Promise(nil) - private weak var copyProtectionTooltipController: TooltipController? + weak var copyProtectionTooltipController: TooltipController? weak var emojiStatusSelectionController: ViewController? - private var forumTopicNotificationExceptions: [EngineMessageHistoryThread.NotificationException] = [] - private var forumTopicNotificationExceptionsDisposable: Disposable? + var forumTopicNotificationExceptions: [EngineMessageHistoryThread.NotificationException] = [] + var forumTopicNotificationExceptionsDisposable: Disposable? - private var translationState: ChatTranslationState? - private var translationStateDisposable: Disposable? + var translationState: ChatTranslationState? + var translationStateDisposable: Disposable? - private var boostStatus: ChannelBoostStatus? - private var boostStatusDisposable: Disposable? + var boostStatus: ChannelBoostStatus? + var boostStatusDisposable: Disposable? - private var expiringStoryList: PeerExpiringStoryListContext? - private var expiringStoryListState: PeerExpiringStoryListContext.State? - private var expiringStoryListDisposable: Disposable? - private var storyUploadProgressDisposable: Disposable? - private var postingAvailabilityDisposable: Disposable? + var expiringStoryList: PeerExpiringStoryListContext? + var expiringStoryListState: PeerExpiringStoryListContext.State? + var expiringStoryListDisposable: Disposable? + var storyUploadProgressDisposable: Disposable? + var postingAvailabilityDisposable: Disposable? - private let storiesReady = ValuePromise(true, ignoreRepeated: true) + let storiesReady = ValuePromise(true, ignoreRepeated: true) - private var personalChannelsDisposable: Disposable? + var personalChannelsDisposable: Disposable? + + var effectiveAreaExpansionFraction: CGFloat = 0.0 private let _ready = Promise() var ready: Promise { @@ -3273,9 +483,6 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro openPermissions: { [weak self] in self?.openPermissions() }, - editingOpenStickerPackSetup: { [weak self] in - self?.editingOpenStickerPackSetup() - }, openLocation: { [weak self] in self?.openLocation() }, @@ -4047,6 +1254,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro self.scrollNode.view.delegate = self.wrappedScrollViewDelegate self.addSubnode(self.scrollNode) self.scrollNode.addSubnode(self.paneContainerNode) + self.scrollNode.view.addSubview(self.headerNode.headerEdgeEffectContainer) + self.scrollNode.view.addSubview(self.paneContainerNode.headerContainer) self.view.addSubview(self.edgeEffectView) @@ -4847,6 +2056,17 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } + self.headerNode.updateUnderHeaderContentsAlpha = { [weak self] alpha, transition in + guard let self else { + return + } + if !self.state.isEditing { + for (_, section) in self.regularSections { + transition.updateAlpha(node: section, alpha: alpha) + } + } + } + let screenData: Signal if self.isSettings { self.notificationExceptions.set(.single(NotificationExceptionsList(peers: [:], settings: [:])) @@ -5608,467 +2828,12 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } - private func openStories(fromAvatar: Bool) { - guard let controller = self.controller else { - return - } - if let expiringStoryList = self.expiringStoryList, let expiringStoryListState = self.expiringStoryListState, !expiringStoryListState.items.isEmpty { - if fromAvatar { - StoryContainerScreen.openPeerStories(context: self.context, peerId: self.peerId, parentController: controller, avatarNode: self.headerNode.avatarListNode.avatarContainerNode.avatarNode) - return - } - - let _ = expiringStoryList - let storyContent = StoryContentContextImpl(context: self.context, isHidden: false, focusedPeerId: self.peerId, singlePeer: true) - let _ = (storyContent.state - |> take(1) - |> deliverOnMainQueue).startStandalone(next: { [weak self] storyContentState in - guard let self else { - return - } - var transitionIn: StoryContainerScreen.TransitionIn? - - if fromAvatar { - let transitionView = self.headerNode.avatarListNode.avatarContainerNode.avatarNode.view - transitionIn = StoryContainerScreen.TransitionIn( - sourceView: transitionView, - sourceRect: transitionView.bounds, - sourceCornerRadius: transitionView.bounds.height * 0.5, - sourceIsAvatar: true - ) - self.headerNode.avatarListNode.avatarContainerNode.avatarNode.isHidden = true - } else if let (expandedStorySetIndicatorTransitionView, subRect) = self.headerNode.avatarListNode.listContainerNode.expandedStorySetIndicatorTransitionView { - transitionIn = StoryContainerScreen.TransitionIn( - sourceView: expandedStorySetIndicatorTransitionView, - sourceRect: subRect, - sourceCornerRadius: expandedStorySetIndicatorTransitionView.bounds.height * 0.5, - sourceIsAvatar: false - ) - expandedStorySetIndicatorTransitionView.isHidden = true - } - - let storyContainerScreen = StoryContainerScreen( - context: self.context, - content: storyContent, - transitionIn: transitionIn, - transitionOut: { [weak self] peerId, _ in - guard let self else { - return nil - } - if !fromAvatar { - self.headerNode.avatarListNode.avatarContainerNode.avatarNode.isHidden = false - - if let (expandedStorySetIndicatorTransitionView, subRect) = self.headerNode.avatarListNode.listContainerNode.expandedStorySetIndicatorTransitionView { - return StoryContainerScreen.TransitionOut( - destinationView: expandedStorySetIndicatorTransitionView, - transitionView: StoryContainerScreen.TransitionView( - makeView: { [weak expandedStorySetIndicatorTransitionView] in - let parentView = UIView() - if let copyView = expandedStorySetIndicatorTransitionView?.snapshotContentTree(unhide: true) { - copyView.layer.anchorPoint = CGPoint() - parentView.addSubview(copyView) - } - return parentView - }, - updateView: { copyView, state, transition in - guard let view = copyView.subviews.first else { - return - } - let size = state.sourceSize.interpolate(to: state.destinationSize, amount: state.progress) - transition.setPosition(view: view, position: CGPoint(x: 0.0, y: 0.0)) - transition.setScale(view: view, scale: size.width / state.destinationSize.width) - }, - insertCloneTransitionView: nil - ), - destinationRect: subRect, - destinationCornerRadius: expandedStorySetIndicatorTransitionView.bounds.height * 0.5, - destinationIsAvatar: false, - completed: { [weak self] in - guard let self else { - return - } - - if let (expandedStorySetIndicatorTransitionView, _) = self.headerNode.avatarListNode.listContainerNode.expandedStorySetIndicatorTransitionView { - expandedStorySetIndicatorTransitionView.isHidden = false - } - } - ) - } - - return nil - } - - let transitionView = self.headerNode.avatarListNode.avatarContainerNode.avatarNode.view - return StoryContainerScreen.TransitionOut( - destinationView: transitionView, - transitionView: StoryContainerScreen.TransitionView( - makeView: { [weak transitionView] in - let parentView = UIView() - if let copyView = transitionView?.snapshotContentTree(unhide: true) { - parentView.addSubview(copyView) - } - return parentView - }, - updateView: { copyView, state, transition in - guard let view = copyView.subviews.first else { - return - } - let size = state.sourceSize.interpolate(to: state.destinationSize, amount: state.progress) - transition.setPosition(view: view, position: CGPoint(x: size.width * 0.5, y: size.height * 0.5)) - transition.setScale(view: view, scale: size.width / state.destinationSize.width) - }, - insertCloneTransitionView: nil - ), - destinationRect: transitionView.bounds, - destinationCornerRadius: transitionView.bounds.height * 0.5, - destinationIsAvatar: true, - completed: { [weak self] in - guard let self else { - return - } - self.headerNode.avatarListNode.avatarContainerNode.avatarNode.isHidden = false - } - ) - } - ) - self.controller?.push(storyContainerScreen) - }) - - return - } - } - - private func openMessage(id: MessageId) -> Bool { - guard let controller = self.controller, let navigationController = controller.navigationController as? NavigationController else { - return false - } - var foundGalleryMessage: Message? - if let searchContentNode = self.searchDisplayController?.contentNode as? ChatHistorySearchContainerNode { - if let galleryMessage = searchContentNode.messageForGallery(id) { - self.context.engine.messages.ensureMessagesAreLocallyAvailable(messages: [EngineMessage(galleryMessage)]) - foundGalleryMessage = galleryMessage - } - } - if foundGalleryMessage == nil, let galleryMessage = self.paneContainerNode.findLoadedMessage(id: id) { - foundGalleryMessage = galleryMessage - } - - guard let galleryMessage = foundGalleryMessage else { - return false - } - self.view.endEditing(true) - - return self.context.sharedContext.openChatMessage(OpenChatMessageParams(context: self.context, chatLocation: self.chatLocation, chatFilterTag: nil, chatLocationContextHolder: self.chatLocationContextHolder, message: galleryMessage, standalone: false, reverseMessageGalleryOrder: true, navigationController: navigationController, dismissInput: { [weak self] in - self?.view.endEditing(true) - }, present: { [weak self] c, a, _ in - self?.controller?.present(c, in: .window(.root), with: a, blockInteraction: true) - }, transitionNode: { [weak self] messageId, media, _ in - guard let strongSelf = self else { - return nil - } - return strongSelf.paneContainerNode.transitionNodeForGallery(messageId: messageId, media: media) - }, addToTransitionSurface: { [weak self] view in - guard let strongSelf = self else { - return - } - strongSelf.paneContainerNode.currentPane?.node.addToTransitionSurface(view: view) - }, openUrl: { [weak self] url in - self?.openUrl(url: url, concealed: false, external: false) - }, openPeer: { [weak self] peer, navigation in - self?.openPeer(peerId: peer.id, navigation: navigation) - }, callPeer: { peerId, isVideo in - }, openConferenceCall: { _ in - }, enqueueMessage: { _ in - }, sendSticker: nil, sendEmoji: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, actionInteraction: GalleryControllerActionInteraction(openUrl: { [weak self] url, concealed, forceExternal in - if let strongSelf = self { - strongSelf.openUrl(url: url, concealed: false, external: forceExternal) - } - }, openUrlIn: { [weak self] url in - if let strongSelf = self { - strongSelf.openUrlIn(url) - } - }, openPeerMention: { [weak self] mention in - if let strongSelf = self { - strongSelf.openPeerMention(mention) - } - }, openPeer: { [weak self] peer in - if let strongSelf = self { - strongSelf.openPeer(peerId: peer.id, navigation: .default) - } - }, openHashtag: { [weak self] peerName, hashtag in - if let strongSelf = self { - strongSelf.openHashtag(hashtag, peerName: peerName) - } - }, openBotCommand: { _ in - }, openAd: { _ in - }, addContact: { [weak self] phoneNumber in - if let strongSelf = self { - strongSelf.context.sharedContext.openAddContact(context: strongSelf.context, firstName: "", lastName: "", phoneNumber: phoneNumber, label: defaultContactLabel, present: { [weak self] controller, arguments in - self?.controller?.present(controller, in: .window(.root), with: arguments) - }, pushController: { [weak self] controller in - if let strongSelf = self { - strongSelf.controller?.push(controller) - } - }, completed: {}) - } - }, storeMediaPlaybackState: { [weak self] messageId, timestamp, playbackRate in - guard let strongSelf = self else { - return - } - var storedState: MediaPlaybackStoredState? - if let timestamp = timestamp { - storedState = MediaPlaybackStoredState(timestamp: timestamp, playbackRate: AudioPlaybackRate(playbackRate)) - } - let _ = updateMediaPlaybackStoredStateInteractively(engine: strongSelf.context.engine, messageId: messageId, state: storedState).startStandalone() - }, editMedia: { [weak self] messageId, snapshots, transitionCompletion in - guard let strongSelf = self else { - return - } - - let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: messageId)) - |> deliverOnMainQueue).startStandalone(next: { [weak self] message in - guard let strongSelf = self, let message = message else { - return - } - - var mediaReference: AnyMediaReference? - for media in message.media { - if let image = media as? TelegramMediaImage { - mediaReference = AnyMediaReference.standalone(media: image) - } else if let file = media as? TelegramMediaFile { - mediaReference = AnyMediaReference.standalone(media: file) - } - } - - if let mediaReference = mediaReference, let peer = message.peers[message.id.peerId] { - legacyMediaEditor(context: strongSelf.context, peer: peer, threadTitle: message.associatedThreadInfo?.title, media: mediaReference, mode: .draw, initialCaption: NSAttributedString(), snapshots: snapshots, transitionCompletion: { - transitionCompletion() - }, getCaptionPanelView: { - return nil - }, sendMessagesWithSignals: { [weak self] signals, _, _, _ in - if let strongSelf = self { - strongSelf.enqueueMediaMessageDisposable.set((legacyAssetPickerEnqueueMessages(context: strongSelf.context, account: strongSelf.context.account, signals: signals!) - |> deliverOnMainQueue).startStrict(next: { [weak self] messages in - if let strongSelf = self { - let _ = enqueueMessages(account: strongSelf.context.account, peerId: strongSelf.peerId, messages: messages.map { $0.message }).startStandalone() - } - })) - } - }, present: { [weak self] c, a in - self?.controller?.present(c, in: .window(.root), with: a) - }) - } - }) - }, updateCanReadHistory: { _ in - }), centralItemUpdated: { [weak self] messageId in - let _ = self?.paneContainerNode.requestExpandTabs?() - self?.paneContainerNode.currentPane?.node.ensureMessageIsVisible(id: messageId) - })) - } - - private func openResolved(_ result: ResolvedUrl) { - guard let navigationController = self.controller?.navigationController as? NavigationController else { - return - } - self.context.sharedContext.openResolvedUrl(result, context: self.context, urlContext: .chat(peerId: self.peerId, message: nil, updatedPresentationData: self.controller?.updatedPresentationData), navigationController: navigationController, forceExternal: false, forceUpdate: false, openPeer: { [weak self] peer, navigation in - guard let strongSelf = self else { - return - } - switch navigation { - case let .chat(inputState, subject, peekData): - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), subject: subject, updateTextInputState: inputState, activateInput: inputState != nil ? .text : nil, keepStack: .always, peekData: peekData)) - case .info: - if let strongSelf = self, peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil { - if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) { - strongSelf.controller?.push(infoController) - } - } - case let .withBotStartPayload(startPayload): - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), botStart: startPayload, keepStack: .always)) - case let .withAttachBot(attachBotStart): - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), attachBotStart: attachBotStart)) - case let .withBotApp(botAppStart): - strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), botAppStart: botAppStart)) - default: - break - } - }, - sendFile: nil, - sendSticker: nil, - sendEmoji: nil, - requestMessageActionUrlAuth: nil, - joinVoiceChat: { peerId, invite, call in - - }, present: { [weak self] c, a in - self?.controller?.present(c, in: .window(.root), with: a) - }, dismissInput: { [weak self] in - self?.view.endEditing(true) - }, contentContext: nil, progress: nil, completion: nil) - } - - private func openUrl(url: String, concealed: Bool, external: Bool, forceExternal: Bool = false, commit: @escaping () -> Void = {}) { - let _ = openUserGeneratedUrl(context: self.context, peerId: self.peerId, url: url, concealed: concealed, present: { [weak self] c in - self?.controller?.present(c, in: .window(.root)) - }, openResolved: { [weak self] tempResolved in - guard let strongSelf = self else { - return - } - - let result: ResolvedUrl = external ? .externalUrl(url) : tempResolved - - strongSelf.context.sharedContext.openResolvedUrl(result, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.controller?.navigationController as? NavigationController, forceExternal: forceExternal, forceUpdate: false, openPeer: { peer, navigation in - self?.openPeer(peerId: peer.id, navigation: navigation) - commit() - }, sendFile: nil, - sendSticker: nil, - sendEmoji: nil, - requestMessageActionUrlAuth: nil, - joinVoiceChat: { peerId, invite, call in - - }, - present: { c, a in - self?.controller?.present(c, in: .window(.root), with: a) - }, dismissInput: { - self?.view.endEditing(true) - }, contentContext: nil, progress: nil, completion: nil) - }) - } - - private func openUrlIn(_ url: String) { - let actionSheet = OpenInActionSheetController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, item: .url(url: url), openUrl: { [weak self] url in - if let strongSelf = self, let navigationController = strongSelf.controller?.navigationController as? NavigationController { - strongSelf.context.sharedContext.openExternalUrl(context: strongSelf.context, urlContext: .generic, url: url, forceExternal: true, presentationData: strongSelf.presentationData, navigationController: navigationController, dismissInput: { - }) - } - }) - self.controller?.present(actionSheet, in: .window(.root)) - } - - private func openPeer(peerId: PeerId, navigation: ChatControllerInteractionNavigateToPeer) { + func openPeer(peerId: PeerId, navigation: ChatControllerInteractionNavigateToPeer) { guard let navigationController = self.controller?.navigationController as? NavigationController else { return } PeerInfoScreenImpl.openPeer(context: self.context, peerId: peerId, navigation: navigation, navigationController: navigationController) } - - private func openPeerMention(_ name: String, navigation: ChatControllerInteractionNavigateToPeer = .default) { - let disposable: MetaDisposable - if let resolvePeerByNameDisposable = self.resolvePeerByNameDisposable { - disposable = resolvePeerByNameDisposable - } else { - disposable = MetaDisposable() - self.resolvePeerByNameDisposable = disposable - } - var resolveSignal = self.context.engine.peers.resolvePeerByName(name: name, referrer: nil, ageLimit: 10) - |> mapToSignal { result -> Signal in - guard case let .result(result) = result else { - return .complete() - } - return .single(result) - } - - var cancelImpl: (() -> Void)? - let presentationData = self.presentationData - let progressSignal = Signal { [weak self] subscriber in - let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: { - cancelImpl?() - })) - self?.controller?.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() - - resolveSignal = resolveSignal - |> afterDisposed { - Queue.mainQueue().async { - progressDisposable.dispose() - } - } - cancelImpl = { [weak self] in - self?.resolvePeerByNameDisposable?.set(nil) - } - disposable.set((resolveSignal - |> take(1) - |> mapToSignal { peer -> Signal in - return .single(peer?._asPeer()) - } - |> deliverOnMainQueue).start(next: { [weak self] peer in - if let strongSelf = self { - if let peer = peer { - var navigation = navigation - if case .default = navigation { - if let peer = peer as? TelegramUser, peer.botInfo != nil { - navigation = .chat(textInputState: nil, subject: nil, peekData: nil) - } - } - strongSelf.openResolved(.peer(peer, navigation)) - } else { - strongSelf.controller?.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.controller?.updatedPresentationData, title: nil, text: strongSelf.presentationData.strings.Resolve_ErrorNotFound, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - } - } - })) - } - - private func openHashtag(_ hashtag: String, peerName: String?) { - if self.resolvePeerByNameDisposable == nil { - self.resolvePeerByNameDisposable = MetaDisposable() - } - var resolveSignal: Signal - if let peerName = peerName { - resolveSignal = self.context.engine.peers.resolvePeerByName(name: peerName, referrer: nil) - |> mapToSignal { result -> Signal in - guard case let .result(result) = result else { - return .complete() - } - return .single(result) - } - |> mapToSignal { peer -> Signal in - return .single(peer?._asPeer()) - } - } else { - resolveSignal = self.context.account.postbox.loadedPeerWithId(self.peerId) - |> map(Optional.init) - } - var cancelImpl: (() -> Void)? - let presentationData = self.presentationData - let progressSignal = Signal { [weak self] subscriber in - let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: { - cancelImpl?() - })) - self?.controller?.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() - - resolveSignal = resolveSignal - |> afterDisposed { - Queue.mainQueue().async { - progressDisposable.dispose() - } - } - cancelImpl = { [weak self] in - self?.resolvePeerByNameDisposable?.set(nil) - } - self.resolvePeerByNameDisposable?.set((resolveSignal - |> deliverOnMainQueue).start(next: { [weak self] peer in - if let strongSelf = self, !hashtag.isEmpty { - let searchController = HashtagSearchController(context: strongSelf.context, peer: peer.flatMap(EnginePeer.init), query: hashtag) - strongSelf.controller?.push(searchController) - } - })) - } private func openBotApp(_ bot: AttachMenuBot) { guard let controller = self.controller else { @@ -6237,1271 +3002,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro }) } - private func performButtonAction(key: PeerInfoHeaderButtonKey, gesture: ContextGesture?) { - guard let controller = self.controller else { - return - } - switch key { - case .message: - if let navigationController = controller.navigationController as? NavigationController, let peer = self.data?.peer { - if let channel = peer as? TelegramChannel, case let .broadcast(info) = channel.info, info.flags.contains(.hasMonoforum), let linkedMonoforumId = channel.linkedMonoforumId { - Task { @MainActor [weak self] in - guard let self else { - return - } - - guard let peer = await self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: linkedMonoforumId)).get() else { - return - } - - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), keepStack: .default)) - } - } else { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(EnginePeer(peer)), keepStack: self.nearbyPeerDistance != nil ? .always : .default, peerNearbyData: self.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0) }), completion: { [weak self] _ in - if let strongSelf = self, strongSelf.nearbyPeerDistance != nil { - var viewControllers = navigationController.viewControllers - viewControllers = viewControllers.filter { controller in - if controller is PeerInfoScreen { - return false - } - return true - } - navigationController.setViewControllers(viewControllers, animated: false) - } - })) - } - } - case .discussion: - if let cachedData = self.data?.cachedData as? CachedChannelData, case let .known(maybeLinkedDiscussionPeerId) = cachedData.linkedDiscussionPeerId, let linkedDiscussionPeerId = maybeLinkedDiscussionPeerId { - let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: linkedDiscussionPeerId)) - |> deliverOnMainQueue).startStandalone(next: { [weak self] linkedDiscussionPeer in - guard let self, let linkedDiscussionPeer else { - return - } - if let navigationController = controller.navigationController as? NavigationController { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(linkedDiscussionPeer))) - } - }) - } - case .call: - self.requestCall(isVideo: false) - case .videoCall: - self.requestCall(isVideo: true) - case .voiceChat: - self.requestCall(isVideo: false, gesture: gesture) - case .mute: - var displayCustomNotificationSettings = false - - let chatIsMuted = peerInfoIsChatMuted(peer: self.data?.peer, peerNotificationSettings: self.data?.peerNotificationSettings, threadNotificationSettings: self.data?.threadNotificationSettings, globalNotificationSettings: self.data?.globalNotificationSettings) - if chatIsMuted { - } else { - displayCustomNotificationSettings = true - } - if self.data?.threadData == nil, let channel = self.data?.peer as? TelegramChannel, channel.isForumOrMonoForum { - displayCustomNotificationSettings = true - } - - let peerId = self.data?.peer?.id ?? self.peerId - - if !displayCustomNotificationSettings { - let _ = self.context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: self.chatLocation.threadId, muteInterval: 0).startStandalone() - - let iconColor: UIColor = .white - self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .universal(animation: "anim_profileunmute", scale: 0.075, colors: [ - "Middle.Group 1.Fill 1": iconColor, - "Top.Group 1.Fill 1": iconColor, - "Bottom.Group 1.Fill 1": iconColor, - "EXAMPLE.Group 1.Fill 1": iconColor, - "Line.Group 1.Stroke 1": iconColor - ], title: nil, text: self.presentationData.strings.PeerInfo_TooltipUnmuted, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) - } else { - self.state = self.state.withHighlightedButton(.mute) - if let (layout, navigationHeight) = self.validLayout { - self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) - } - - var items: [ContextMenuItem] = [] - - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_MuteFor, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Mute2d"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] c, _ in - guard let strongSelf = self else { - return - } - var subItems: [ContextMenuItem] = [] - - subItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Common_Back, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) - }, iconPosition: .left, action: { c, _ in - c?.popItems() - }))) - subItems.append(.separator) - - let presetValues: [Int32] = [ - 1 * 60 * 60, - 8 * 60 * 60, - 1 * 24 * 60 * 60, - 7 * 24 * 60 * 60 - ] - - for value in presetValues { - subItems.append(.action(ContextMenuActionItem(text: muteForIntervalString(strings: strongSelf.presentationData.strings, value: value), icon: { _ in - return nil - }, action: { _, f in - f(.default) - - let _ = strongSelf.context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: strongSelf.chatLocation.threadId, muteInterval: value).startStandalone() - - strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_mute_for", scale: 0.066, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipMutedFor(mutedForTimeIntervalString(strings: strongSelf.presentationData.strings, value: value)).string, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) - }))) - } - - subItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_MuteForCustom, icon: { _ in - return nil - }, action: { _, f in - f(.default) - - self?.openCustomMute() - }))) - - c?.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) - }))) - - items.append(.separator) - - var isSoundEnabled = true - let notificationSettings = self.data?.threadNotificationSettings ?? self.data?.peerNotificationSettings - if let notificationSettings { - switch notificationSettings.messageSound { - case .none: - isSoundEnabled = false - default: - break - } - } - - if !chatIsMuted { - if !isSoundEnabled { - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_EnableSound, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/SoundOn"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.default) - - guard let strongSelf = self else { - return - } - let _ = strongSelf.context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, threadId: strongSelf.chatLocation.threadId, sound: .default).startStandalone() - - strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_sound_on", scale: 0.056, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipSoundEnabled, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) - }))) - } else { - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_DisableSound, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/SoundOff"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.default) - - guard let strongSelf = self else { - return - } - let _ = strongSelf.context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, threadId: strongSelf.chatLocation.threadId, sound: .none).startStandalone() - - strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_sound_off", scale: 0.056, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipSoundDisabled, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) - }))) - } - } - - let context = self.context - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_NotificationsCustomize, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Customize"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.dismissWithoutContent) - - let _ = (context.engine.data.get( - TelegramEngine.EngineData.Item.NotificationSettings.Global() - ) - |> deliverOnMainQueue).startStandalone(next: { globalSettings in - guard let strongSelf = self, let peer = strongSelf.data?.peer else { - return - } - let threadId = strongSelf.chatLocation.threadId - - let context = strongSelf.context - let updatePeerSound: (PeerId, PeerMessageSound) -> Signal = { peerId, sound in - return context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, threadId: threadId, sound: sound) |> deliverOnMainQueue - } - - let updatePeerNotificationInterval: (PeerId, Int32?) -> Signal = { peerId, muteInterval in - return context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: threadId, muteInterval: muteInterval) |> deliverOnMainQueue - } - - let updatePeerDisplayPreviews: (PeerId, PeerNotificationDisplayPreviews) -> Signal = { - peerId, displayPreviews in - return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: threadId, displayPreviews: displayPreviews) |> deliverOnMainQueue - } - - let updatePeerStoriesMuted: (PeerId, PeerStoryNotificationSettings.Mute) -> Signal = { - peerId, mute in - return context.engine.peers.updatePeerStoriesMutedSetting(peerId: peerId, mute: mute) |> deliverOnMainQueue - } - - let updatePeerStoriesHideSender: (PeerId, PeerStoryNotificationSettings.HideSender) -> Signal = { - peerId, hideSender in - return context.engine.peers.updatePeerStoriesHideSenderSetting(peerId: peerId, hideSender: hideSender) |> deliverOnMainQueue - } - - let updatePeerStorySound: (PeerId, PeerMessageSound) -> Signal = { peerId, sound in - return context.engine.peers.updatePeerStorySoundInteractive(peerId: peerId, sound: sound) |> deliverOnMainQueue - } - - let mode: NotificationExceptionMode - let defaultSound: PeerMessageSound - if let _ = peer as? TelegramUser { - mode = .users([:]) - defaultSound = globalSettings.privateChats.sound._asMessageSound() - } else if let _ = peer as? TelegramSecretChat { - mode = .users([:]) - defaultSound = globalSettings.privateChats.sound._asMessageSound() - } else if let channel = peer as? TelegramChannel { - if case .broadcast = channel.info { - mode = .channels([:]) - defaultSound = globalSettings.channels.sound._asMessageSound() - } else { - mode = .groups([:]) - defaultSound = globalSettings.groupChats.sound._asMessageSound() - } - } else { - mode = .groups([:]) - defaultSound = globalSettings.groupChats.sound._asMessageSound() - } - let _ = mode - - let canRemove = false - - let exceptionController = notificationPeerExceptionController(context: context, updatedPresentationData: strongSelf.controller?.updatedPresentationData, peer: EnginePeer(peer), threadId: threadId, isStories: nil, canRemove: canRemove, defaultSound: defaultSound, defaultStoriesSound: globalSettings.privateChats.storySettings.sound, edit: true, updatePeerSound: { peerId, sound in - let _ = (updatePeerSound(peer.id, sound) - |> deliverOnMainQueue).startStandalone(next: { _ in - }) - }, updatePeerNotificationInterval: { peerId, muteInterval in - let _ = (updatePeerNotificationInterval(peerId, muteInterval) - |> deliverOnMainQueue).startStandalone(next: { _ in - guard let strongSelf = self else { - return - } - if let muteInterval = muteInterval, muteInterval == Int32.max { - let iconColor: UIColor = .white - strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_profilemute", scale: 0.075, colors: [ - "Middle.Group 1.Fill 1": iconColor, - "Top.Group 1.Fill 1": iconColor, - "Bottom.Group 1.Fill 1": iconColor, - "EXAMPLE.Group 1.Fill 1": iconColor, - "Line.Group 1.Stroke 1": iconColor - ], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipMutedForever, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) - } - }) - }, updatePeerDisplayPreviews: { peerId, displayPreviews in - let _ = (updatePeerDisplayPreviews(peerId, displayPreviews) - |> deliverOnMainQueue).startStandalone(next: { _ in - - }) - }, updatePeerStoriesMuted: { peerId, mute in - let _ = (updatePeerStoriesMuted(peerId, mute) - |> deliverOnMainQueue).startStandalone() - }, updatePeerStoriesHideSender: { peerId, hideSender in - let _ = (updatePeerStoriesHideSender(peerId, hideSender) - |> deliverOnMainQueue).startStandalone() - }, updatePeerStorySound: { peerId, sound in - let _ = (updatePeerStorySound(peer.id, sound) - |> deliverOnMainQueue).startStandalone() - }, removePeerFromExceptions: { - }, modifiedPeer: { - }) - exceptionController.navigationPresentation = .modal - controller.push(exceptionController) - }) - }))) - - if chatIsMuted { - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_ButtonUnmute, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Unmute"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.default) - - guard let self else { - return - } - - let _ = self.context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: self.chatLocation.threadId, muteInterval: 0).startStandalone() - - let iconColor: UIColor = .white - self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .universal(animation: "anim_profileunmute", scale: 0.075, colors: [ - "Middle.Group 1.Fill 1": iconColor, - "Top.Group 1.Fill 1": iconColor, - "Bottom.Group 1.Fill 1": iconColor, - "EXAMPLE.Group 1.Fill 1": iconColor, - "Line.Group 1.Stroke 1": iconColor - ], title: nil, text: self.presentationData.strings.PeerInfo_TooltipUnmuted, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) - }))) - } else { - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_MuteForever, textColor: .destructive, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Muted"), color: theme.contextMenu.destructiveColor) - }, action: { [weak self] _, f in - f(.default) - - guard let strongSelf = self else { - return - } - - let _ = strongSelf.context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: strongSelf.chatLocation.threadId, muteInterval: Int32.max).startStandalone() - - let iconColor: UIColor = .white - strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_profilemute", scale: 0.075, colors: [ - "Middle.Group 1.Fill 1": iconColor, - "Top.Group 1.Fill 1": iconColor, - "Bottom.Group 1.Fill 1": iconColor, - "EXAMPLE.Group 1.Fill 1": iconColor, - "Line.Group 1.Stroke 1": iconColor - ], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipMutedForever, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) - }))) - } - - var tip: ContextController.Tip? - tip = nil - if !self.forumTopicNotificationExceptions.isEmpty { - items.append(.separator) - - let text: String = self.presentationData.strings.PeerInfo_TopicNotificationExceptions(Int32(self.forumTopicNotificationExceptions.count)) - - items.append(.action(ContextMenuActionItem( - text: text, - textLayout: .multiline, - textFont: .small, - parseMarkdown: true, - badge: nil, - icon: { _ in - return nil - }, - action: { [weak self] _, f in - guard let self else { - return - } - f(.default) - self.controller?.push(threadNotificationExceptionsScreen(context: self.context, peerId: self.peerId, notificationExceptions: self.forumTopicNotificationExceptions, updated: { [weak self] value in - guard let self else { - return - } - self.forumTopicNotificationExceptions = value - })) - } - ))) - } - - self.view.endEditing(true) - - if let sourceNode = self.headerNode.buttonNodes[.mute]?.referenceNode { - let contextController = ContextController(presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items), tip: tip)), gesture: gesture) - contextController.dismissed = { [weak self] in - if let strongSelf = self { - strongSelf.state = strongSelf.state.withHighlightedButton(nil) - if let (layout, navigationHeight) = strongSelf.validLayout { - strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) - } - } - } - controller.presentInGlobalOverlay(contextController) - } - } - case .more: - guard let data = self.data, let peer = data.peer, let chatPeer = data.chatPeer else { - return - } - let presentationData = self.presentationData - self.state = self.state.withHighlightedButton(.more) - if let (layout, navigationHeight) = self.validLayout { - self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) - } - - var mainItemsImpl: (() -> Signal<[ContextMenuItem], NoError>)? - mainItemsImpl = { [weak self] in - var items: [ContextMenuItem] = [] - guard let strongSelf = self else { - return .single(items) - } - - let allHeaderButtons = Set(peerInfoHeaderButtons(peer: peer, cachedData: data.cachedData, isOpenedFromChat: strongSelf.isOpenedFromChat, isExpanded: false, videoCallsEnabled: strongSelf.videoCallsEnabled, isSecretChat: strongSelf.peerId.namespace == Namespaces.Peer.SecretChat, isContact: strongSelf.data?.isContact ?? false, threadInfo: data.threadData?.info)) - let headerButtons = Set(peerInfoHeaderButtons(peer: peer, cachedData: data.cachedData, isOpenedFromChat: strongSelf.isOpenedFromChat, isExpanded: true, videoCallsEnabled: strongSelf.videoCallsEnabled, isSecretChat: strongSelf.peerId.namespace == Namespaces.Peer.SecretChat, isContact: strongSelf.data?.isContact ?? false, threadInfo: strongSelf.data?.threadData?.info)) - - let filteredButtons = allHeaderButtons.subtracting(headerButtons) - - var currentAutoremoveTimeout: Int32? - if let cachedData = data.cachedData as? CachedUserData { - switch cachedData.autoremoveTimeout { - case let .known(value): - currentAutoremoveTimeout = value?.peerValue - case .unknown: - break - } - } else if let cachedData = data.cachedData as? CachedGroupData { - switch cachedData.autoremoveTimeout { - case let .known(value): - currentAutoremoveTimeout = value?.peerValue - case .unknown: - break - } - } else if let cachedData = data.cachedData as? CachedChannelData { - switch cachedData.autoremoveTimeout { - case let .known(value): - currentAutoremoveTimeout = value?.peerValue - case .unknown: - break - } - } - - var canSetupAutoremoveTimeout = false - - if let secretChat = chatPeer as? TelegramSecretChat { - currentAutoremoveTimeout = secretChat.messageAutoremoveTimeout - canSetupAutoremoveTimeout = false - } else if let group = chatPeer as? TelegramGroup { - if !group.hasBannedPermission(.banChangeInfo) { - canSetupAutoremoveTimeout = true - } - } else if let user = chatPeer as? TelegramUser { - if user.id != strongSelf.context.account.peerId { - canSetupAutoremoveTimeout = true - } - } else if let channel = chatPeer as? TelegramChannel { - if channel.hasPermission(.changeInfo) { - canSetupAutoremoveTimeout = true - } - } - - if filteredButtons.contains(.call) { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.PeerInfo_ButtonCall, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Call"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.dismissWithoutContent) - self?.requestCall(isVideo: false) - }))) - } - if filteredButtons.contains(.search) { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.ChatSearch_SearchPlaceholder, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Search"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.dismissWithoutContent) - self?.openChatWithMessageSearch() - }))) - } - - var hasDiscussion = false - if let channel = chatPeer as? TelegramChannel { - switch channel.info { - case let .broadcast(info): - hasDiscussion = info.flags.contains(.hasDiscussionGroup) - case .group: - hasDiscussion = false - } - } - if !headerButtons.contains(.discussion) && hasDiscussion { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.PeerInfo_ViewDiscussion, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MessageBubble"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.dismissWithoutContent) - self?.performButtonAction(key: .discussion, gesture: nil) - }))) - } - - if let user = peer as? TelegramUser { - if user.botInfo == nil && strongSelf.data?.encryptionKeyFingerprint == nil && !user.isDeleted { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_ChangeWallpaper, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ApplyTheme"), color: theme.contextMenu.primaryColor) - }, action: { _, f in - f(.dismissWithoutContent) - - self?.openChatForThemeChange() - }))) - } - - if let _ = user.botInfo { - if user.addressName != nil { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_ShareBot, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.dismissWithoutContent) - self?.openShareBot() - }))) - } - - var addedPrivacy = false - var privacyPolicyUrl: String? - if let cachedData = (data.cachedData as? CachedUserData), let botInfo = cachedData.botInfo { - if let url = botInfo.privacyPolicyUrl { - privacyPolicyUrl = url - } else if botInfo.commands.contains(where: { $0.text == "privacy" }) { - - } else { - privacyPolicyUrl = presentationData.strings.WebApp_PrivacyPolicy_URL - } - } - if let privacyPolicyUrl { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_BotPrivacy, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.dismissWithoutContent) - - guard let self else { - return - } - self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: privacyPolicyUrl, forceExternal: false, presentationData: self.presentationData, navigationController: self.controller?.navigationController as? NavigationController, dismissInput: {}) - }))) - addedPrivacy = true - } - if let cachedData = data.cachedData as? CachedUserData, let botInfo = cachedData.botInfo { - for command in botInfo.commands { - if command.text == "settings" { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_BotSettings, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Bots"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.dismissWithoutContent) - self?.performBotCommand(command: .settings) - }))) - } else if command.text == "help" { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_BotHelp, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Help"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.dismissWithoutContent) - self?.performBotCommand(command: .help) - }))) - } else if command.text == "privacy" && !addedPrivacy { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_BotPrivacy, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.dismissWithoutContent) - self?.performBotCommand(command: .privacy) - }))) - } - } - } - } - - if strongSelf.peerId.namespace == Namespaces.Peer.CloudUser && user.botInfo == nil && !user.flags.contains(.isSupport) { - if let cachedUserData = strongSelf.data?.cachedData as? CachedUserData, let _ = cachedUserData.sendPaidMessageStars { - - } else { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_StartSecretChat, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Lock"), color: theme.contextMenu.primaryColor) - }, action: { _, f in - f(.dismissWithoutContent) - - self?.openStartSecretChat() - }))) - } - } - - if user.botInfo == nil && data.isContact, let peer = strongSelf.data?.peer as? TelegramUser, let phone = peer.phone { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.Profile_ShareContactButton, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.dismissWithoutContent) - - if let strongSelf = self { - let contact = TelegramMediaContact(firstName: peer.firstName ?? "", lastName: peer.lastName ?? "", phoneNumber: phone, peerId: peer.id, vCardData: nil) - let shareController = ShareController(context: strongSelf.context, subject: .media(.standalone(media: contact), nil), updatedPresentationData: strongSelf.controller?.updatedPresentationData) - shareController.completed = { [weak self] peerIds in - if let strongSelf = self { - let _ = (strongSelf.context.engine.data.get( - EngineDataList( - peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init) - ) - ) - |> deliverOnMainQueue).startStandalone(next: { [weak self] peerList in - guard let strongSelf = self else { - return - } - - let peers = peerList.compactMap { $0 } - - let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - - let text: String - var savedMessages = false - if peerIds.count == 1, let peerId = peerIds.first, peerId == strongSelf.context.account.peerId { - text = presentationData.strings.UserInfo_ContactForwardTooltip_SavedMessages_One - savedMessages = true - } else { - if peers.count == 1, let peer = peers.first { - let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - text = presentationData.strings.UserInfo_ContactForwardTooltip_Chat_One(peerName).string - } else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last { - let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - text = presentationData.strings.UserInfo_ContactForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string - } else if let peer = peers.first { - let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - text = presentationData.strings.UserInfo_ContactForwardTooltip_ManyChats_One(peerName, "\(peers.count - 1)").string - } else { - text = "" - } - } - - strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in - if savedMessages, let self, action == .info { - let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) - |> deliverOnMainQueue).start(next: { [weak self] peer in - guard let self, let peer else { - return - } - guard let navigationController = self.controller?.navigationController as? NavigationController else { - return - } - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), forceOpenChat: true)) - }) - } - return false - }), in: .current) - }) - } - } - strongSelf.controller?.present(shareController, in: .window(.root)) - } - }))) - } - - if strongSelf.peerId.namespace == Namespaces.Peer.CloudUser, !user.isDeleted && user.botInfo == nil && !user.flags.contains(.isSupport) { - if let cachedData = data.cachedData as? CachedUserData, cachedData.disallowedGifts == .All { - } else { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.Profile_SendGift, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Gift"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.dismissWithoutContent) - - if let self { - self.openPremiumGift() - } - }))) - } - } - - if let cachedData = data.cachedData as? CachedUserData, canTranslateChats(context: strongSelf.context), cachedData.flags.contains(.translationHidden) { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ContextMenuTranslate, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Translate"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.dismissWithoutContent) - - if let strongSelf = self { - let _ = updateChatTranslationStateInteractively(engine: strongSelf.context.engine, peerId: strongSelf.peerId, threadId: nil, { current in - return current?.withIsEnabled(true) - }).startStandalone() - - Queue.mainQueue().after(0.2, { - let _ = (strongSelf.context.engine.messages.togglePeerMessagesTranslationHidden(peerId: strongSelf.peerId, hidden: false) - |> deliverOnMainQueue).startStandalone(completed: { [weak self] in - self?.openChatForTranslation() - }) - }) - } - }))) - } - - let itemsCount = items.count - - if canSetupAutoremoveTimeout { - let strings = strongSelf.presentationData.strings - items.append(.action(ContextMenuActionItem(text: currentAutoremoveTimeout == nil ? strongSelf.presentationData.strings.PeerInfo_EnableAutoDelete : strongSelf.presentationData.strings.PeerInfo_AdjustAutoDelete, icon: { theme in - if let currentAutoremoveTimeout = currentAutoremoveTimeout { - let text = NSAttributedString(string: shortTimeIntervalString(strings: strings, value: currentAutoremoveTimeout), font: Font.regular(14.0), textColor: theme.contextMenu.primaryColor) - let bounds = text.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil) - return generateImage(bounds.size.integralFloor, rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - UIGraphicsPushContext(context) - text.draw(in: bounds) - UIGraphicsPopContext() - }) - } else { - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Timer"), color: theme.contextMenu.primaryColor) - } - }, action: { [weak self] c, _ in - var subItems: [ContextMenuItem] = [] - - subItems.append(.action(ContextMenuActionItem(text: strings.Common_Back, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) - }, iconPosition: .left, action: { c, _ in - c?.popItems() - }))) - subItems.append(.separator) - - let presetValues: [Int32] = [ - 1 * 24 * 60 * 60, - 7 * 24 * 60 * 60, - 31 * 24 * 60 * 60 - ] - - for value in presetValues { - subItems.append(.action(ContextMenuActionItem(text: timeIntervalString(strings: strings, value: value), icon: { _ in - return nil - }, action: { _, f in - f(.default) - - self?.setAutoremove(timeInterval: value) - }))) - } - - subItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_AutoDeleteSettingOther, icon: { _ in - return nil - }, action: { _, f in - f(.default) - - self?.openAutoremove(currentValue: currentAutoremoveTimeout) - }))) - - if let _ = currentAutoremoveTimeout { - subItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_AutoDeleteDisable, textColor: .destructive, icon: { _ in - return nil - }, action: { _, f in - f(.default) - - self?.setAutoremove(timeInterval: nil) - }))) - } - - subItems.append(.separator) - - subItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_AutoDeleteInfo + "\n\n" + strongSelf.presentationData.strings.AutoremoveSetup_AdditionalGlobalSettingsInfo, textLayout: .multiline, textFont: .small, parseMarkdown: true, icon: { _ in - return nil - }, textLinkAction: { [weak c] in - c?.dismiss(completion: nil) - - guard let self else { - return - } - self.context.sharedContext.openResolvedUrl(.settings(.autoremoveMessages), context: self.context, urlContext: .generic, navigationController: self.controller?.navigationController as? NavigationController, forceExternal: false, forceUpdate: false, openPeer: { _, _ in }, sendFile: nil, sendSticker: nil, sendEmoji: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { _, _ in }, dismissInput: { [weak self] in - guard let self else { - return - } - self.controller?.view.endEditing(true) - }, contentContext: nil, progress: nil, completion: nil) - }, action: nil as ((ContextControllerProtocol?, @escaping (ContextMenuActionResult) -> Void) -> Void)?))) - - c?.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) - }))) - } - - let clearPeerHistory = ClearPeerHistory(context: strongSelf.context, peer: user, chatPeer: chatPeer, cachedData: strongSelf.data?.cachedData) - if clearPeerHistory.canClearForMyself != nil || clearPeerHistory.canClearForEveryone != nil { - items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_ClearMessages, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ClearMessages"), color: theme.contextMenu.primaryColor) - }, action: { c, _ in - if let c { - self?.openClearHistory(contextController: c, clearPeerHistory: clearPeerHistory, peer: user, chatPeer: user) - } - }))) - } - - if strongSelf.peerId.namespace == Namespaces.Peer.CloudUser && user.botInfo == nil && !user.flags.contains(.isSupport) { - if data.isContact { - if let cachedData = data.cachedData as? CachedUserData, cachedData.isBlocked { - } else { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_BlockUser, textColor: .destructive, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Restrict"), color: theme.contextMenu.destructiveColor) - }, action: { _, f in - f(.dismissWithoutContent) - - self?.updateBlocked(block: true) - }))) - } - } - } else if strongSelf.peerId.namespace == Namespaces.Peer.SecretChat && data.isContact { - if let cachedData = data.cachedData as? CachedUserData, cachedData.isBlocked { - } else { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_BlockUser, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Restrict"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.dismissWithoutContent) - - self?.updateBlocked(block: true) - }))) - } - } - - let finalItemsCount = items.count - - if finalItemsCount > itemsCount { - items.insert(.separator, at: itemsCount) - } - } else if let channel = peer as? TelegramChannel { - if let cachedData = strongSelf.data?.cachedData as? CachedChannelData { - if case .broadcast = channel.info, cachedData.flags.contains(.starGiftsAvailable) { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.Profile_SendGift, badge: nil, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Gift"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.dismissWithoutContent) - - self?.openPremiumGift() - }))) - } - - let boostTitle: String - switch channel.info { - case .group: - boostTitle = presentationData.strings.PeerInfo_Group_Boost - case .broadcast: - boostTitle = presentationData.strings.PeerInfo_Channel_Boost - } - items.append(.action(ContextMenuActionItem(text: boostTitle, badge: nil, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Boost"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.dismissWithoutContent) - - self?.openBoost() - }))) - - if channel.hasPermission(.editStories) { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.PeerInfo_Channel_ArchivedStories, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Archive"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.dismissWithoutContent) - - self?.openStoryArchive() - }))) - } - if cachedData.flags.contains(.canViewStats) { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.ChannelInfo_Stats, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Statistics"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.dismissWithoutContent) - - self?.openStats(section: .stats) - }))) - } - if cachedData.flags.contains(.translationHidden) { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ContextMenuTranslate, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Translate"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.dismissWithoutContent) - - if let strongSelf = self { - let _ = updateChatTranslationStateInteractively(engine: strongSelf.context.engine, peerId: strongSelf.peerId, threadId: nil, { current in - return current?.withIsEnabled(true) - }).startStandalone() - - Queue.mainQueue().after(0.2, { - let _ = (strongSelf.context.engine.messages.togglePeerMessagesTranslationHidden(peerId: strongSelf.peerId, hidden: false) - |> deliverOnMainQueue).startStandalone(completed: { [weak self] in - self?.openChatForTranslation() - }) - }) - } - }))) - } - } - - var canReport = true - if channel.adminRights != nil { - canReport = false - } - if channel.flags.contains(.isCreator) { - canReport = false - } - if canReport { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.ReportPeer_Report, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Report"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] c, f in - self?.openReport(type: .default, contextController: c, backAction: { c in - if let mainItemsImpl = mainItemsImpl { - c.setItems(mainItemsImpl() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true) - } - }) - }))) - } - - if canSetupAutoremoveTimeout { - let strings = strongSelf.presentationData.strings - items.append(.action(ContextMenuActionItem(text: currentAutoremoveTimeout == nil ? strongSelf.presentationData.strings.PeerInfo_EnableAutoDelete : strongSelf.presentationData.strings.PeerInfo_AdjustAutoDelete, icon: { theme in - if let currentAutoremoveTimeout = currentAutoremoveTimeout { - let text = NSAttributedString(string: shortTimeIntervalString(strings: strings, value: currentAutoremoveTimeout), font: Font.regular(14.0), textColor: theme.contextMenu.primaryColor) - let bounds = text.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil) - return generateImage(bounds.size.integralFloor, rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - UIGraphicsPushContext(context) - text.draw(in: bounds) - UIGraphicsPopContext() - }) - } else { - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Timer"), color: theme.contextMenu.primaryColor) - } - }, action: { [weak self] c, _ in - var subItems: [ContextMenuItem] = [] - - subItems.append(.action(ContextMenuActionItem(text: strings.Common_Back, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) - }, iconPosition: .left, action: { c, _ in - c?.popItems() - }))) - subItems.append(.separator) - - let presetValues: [Int32] = [ - 1 * 24 * 60 * 60, - 7 * 24 * 60 * 60, - 31 * 24 * 60 * 60 - ] - - for value in presetValues { - subItems.append(.action(ContextMenuActionItem(text: timeIntervalString(strings: strings, value: value), icon: { _ in - return nil - }, action: { _, f in - f(.default) - - self?.setAutoremove(timeInterval: value) - }))) - } - - subItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_AutoDeleteSettingOther, icon: { _ in - return nil - }, action: { _, f in - f(.default) - - self?.openAutoremove(currentValue: currentAutoremoveTimeout) - }))) - - if let _ = currentAutoremoveTimeout { - subItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_AutoDeleteDisable, textColor: .destructive, icon: { _ in - return nil - }, action: { _, f in - f(.default) - - self?.setAutoremove(timeInterval: nil) - }))) - } - - subItems.append(.separator) - - let baseText: String - if case .broadcast = channel.info { - baseText = strongSelf.presentationData.strings.PeerInfo_ChannelAutoDeleteInfo - } else { - baseText = strongSelf.presentationData.strings.PeerInfo_AutoDeleteInfo - } - - subItems.append(.action(ContextMenuActionItem(text: baseText + "\n\n" + strongSelf.presentationData.strings.AutoremoveSetup_AdditionalGlobalSettingsInfo, textLayout: .multiline, textFont: .small, parseMarkdown: true, icon: { _ in - return nil - }, textLinkAction: { [weak c] in - c?.dismiss(completion: nil) - - guard let self else { - return - } - self.context.sharedContext.openResolvedUrl(.settings(.autoremoveMessages), context: self.context, urlContext: .generic, navigationController: self.controller?.navigationController as? NavigationController, forceExternal: false, forceUpdate: false, openPeer: { _, _ in }, sendFile: nil, sendSticker: nil, sendEmoji: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { _, _ in }, dismissInput: { [weak self] in - guard let self else { - return - } - self.controller?.view.endEditing(true) - }, contentContext: nil, progress: nil, completion: nil) - }, action: nil as ((ContextControllerProtocol?, @escaping (ContextMenuActionResult) -> Void) -> Void)?))) - - c?.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) - }))) - } - - let clearPeerHistory = ClearPeerHistory(context: strongSelf.context, peer: channel, chatPeer: channel, cachedData: strongSelf.data?.cachedData) - if clearPeerHistory.canClearForMyself != nil || clearPeerHistory.canClearForEveryone != nil { - items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_ClearMessages, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ClearMessages"), color: theme.contextMenu.primaryColor) - }, action: { c, _ in - if let c { - self?.openClearHistory(contextController: c, clearPeerHistory: clearPeerHistory, peer: channel, chatPeer: channel) - } - }))) - } - - switch channel.info { - case .broadcast: - if case .member = channel.participationStatus, !headerButtons.contains(.leave) { - if !items.isEmpty { - items.append(.separator) - } - items.append(.action(ContextMenuActionItem(text: presentationData.strings.Channel_LeaveChannel, textColor: .destructive, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Logout"), color: theme.contextMenu.destructiveColor) - }, action: { [weak self] _, f in - f(.dismissWithoutContent) - - self?.openLeavePeer(delete: false) - }))) - } - case .group: - if case .member = channel.participationStatus, !headerButtons.contains(.leave) { - if !items.isEmpty { - items.append(.separator) - } - items.append(.action(ContextMenuActionItem(text: presentationData.strings.Group_LeaveGroup, textColor: .primary, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Logout"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.dismissWithoutContent) - - self?.openLeavePeer(delete: false) - }))) - if let cachedData = data.cachedData as? CachedChannelData, cachedData.flags.contains(.canDeleteHistory) { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.Group_DeleteGroup, textColor: .destructive, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.contextMenu.destructiveColor) - }, action: { [weak self] _, f in - f(.dismissWithoutContent) - - self?.openLeavePeer(delete: true) - }))) - } - } - } - } else if let group = peer as? TelegramGroup { - if canSetupAutoremoveTimeout { - let strings = strongSelf.presentationData.strings - items.append(.action(ContextMenuActionItem(text: currentAutoremoveTimeout == nil ? strongSelf.presentationData.strings.PeerInfo_EnableAutoDelete : strongSelf.presentationData.strings.PeerInfo_AdjustAutoDelete, icon: { theme in - if let currentAutoremoveTimeout = currentAutoremoveTimeout { - let text = NSAttributedString(string: shortTimeIntervalString(strings: strings, value: currentAutoremoveTimeout), font: Font.regular(14.0), textColor: theme.contextMenu.primaryColor) - let bounds = text.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil) - return generateImage(bounds.size.integralFloor, rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - UIGraphicsPushContext(context) - text.draw(in: bounds) - UIGraphicsPopContext() - }) - } else { - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Timer"), color: theme.contextMenu.primaryColor) - } - }, action: { [weak self] c, _ in - var subItems: [ContextMenuItem] = [] - - subItems.append(.action(ContextMenuActionItem(text: strings.Common_Back, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) - }, iconPosition: .left, action: { c, _ in - c?.popItems() - }))) - subItems.append(.separator) - - let presetValues: [Int32] = [ - 1 * 24 * 60 * 60, - 7 * 24 * 60 * 60, - 31 * 24 * 60 * 60 - ] - - for value in presetValues { - subItems.append(.action(ContextMenuActionItem(text: timeIntervalString(strings: strings, value: value), icon: { _ in - return nil - }, action: { _, f in - f(.default) - - self?.setAutoremove(timeInterval: value) - }))) - } - - subItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_AutoDeleteSettingOther, icon: { _ in - return nil - }, action: { _, f in - f(.default) - - self?.openAutoremove(currentValue: currentAutoremoveTimeout) - }))) - - if let _ = currentAutoremoveTimeout { - subItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_AutoDeleteDisable, textColor: .destructive, icon: { _ in - return nil - }, action: { _, f in - f(.default) - - self?.setAutoremove(timeInterval: nil) - }))) - } - - subItems.append(.separator) - - subItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_AutoDeleteInfo + "\n\n" + strongSelf.presentationData.strings.AutoremoveSetup_AdditionalGlobalSettingsInfo, textLayout: .multiline, textFont: .small, parseMarkdown: true, icon: { _ in - return nil - }, textLinkAction: { [weak c] in - c?.dismiss(completion: nil) - - guard let self else { - return - } - self.context.sharedContext.openResolvedUrl(.settings(.autoremoveMessages), context: self.context, urlContext: .generic, navigationController: self.controller?.navigationController as? NavigationController, forceExternal: false, forceUpdate: false, openPeer: { _, _ in }, sendFile: nil, sendSticker: nil, sendEmoji: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { _, _ in }, dismissInput: { [weak self] in - guard let self else { - return - } - self.controller?.view.endEditing(true) - }, contentContext: nil, progress: nil, completion: nil) - }, action: nil as ((ContextControllerProtocol?, @escaping (ContextMenuActionResult) -> Void) -> Void)?))) - - c?.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) - }))) - } - - if let cachedData = data.cachedData as? CachedGroupData, cachedData.flags.contains(.translationHidden) { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ContextMenuTranslate, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Translate"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.dismissWithoutContent) - - if let strongSelf = self { - let _ = updateChatTranslationStateInteractively(engine: strongSelf.context.engine, peerId: strongSelf.peerId, threadId: nil, { current in - return current?.withIsEnabled(true) - }).startStandalone() - - Queue.mainQueue().after(0.2, { - let _ = (strongSelf.context.engine.messages.togglePeerMessagesTranslationHidden(peerId: strongSelf.peerId, hidden: false) - |> deliverOnMainQueue).startStandalone(completed: { [weak self] in - self?.openChatForTranslation() - }) - }) - } - }))) - } - - var canReport = true - if case .creator = group.role { - canReport = false - } - if canReport { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.ReportPeer_Report, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Report"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] c, f in - self?.openReport(type: .default, contextController: c, backAction: { c in - if let mainItemsImpl = mainItemsImpl { - c.setItems(mainItemsImpl() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true) - } - }) - }))) - } - - let clearPeerHistory = ClearPeerHistory(context: strongSelf.context, peer: group, chatPeer: group, cachedData: strongSelf.data?.cachedData) - if clearPeerHistory.canClearForMyself != nil || clearPeerHistory.canClearForEveryone != nil { - items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_ClearMessages, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ClearMessages"), color: theme.contextMenu.primaryColor) - }, action: { c, _ in - if let c { - self?.openClearHistory(contextController: c, clearPeerHistory: clearPeerHistory, peer: group, chatPeer: group) - } - }))) - } - - if case .Member = group.membership, !headerButtons.contains(.leave) { - if !items.isEmpty { - items.append(.separator) - } - items.append(.action(ContextMenuActionItem(text: presentationData.strings.Group_LeaveGroup, textColor: .destructive, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Logout"), color: theme.contextMenu.destructiveColor) - }, action: { [weak self] _, f in - f(.dismissWithoutContent) - - self?.openLeavePeer(delete: false) - }))) - - if case .creator = group.role { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.Group_DeleteGroup, textColor: .destructive, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.contextMenu.destructiveColor) - }, action: { [weak self] _, f in - f(.dismissWithoutContent) - - self?.openLeavePeer(delete: true) - }))) - } - } - } - - return .single(items) - } - - self.view.endEditing(true) - - if let sourceNode = self.headerNode.buttonNodes[.more]?.referenceNode { - let items = mainItemsImpl?() ?? .single([]) - let contextController = ContextController(presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) - contextController.dismissed = { [weak self] in - if let strongSelf = self { - strongSelf.state = strongSelf.state.withHighlightedButton(nil) - if let (layout, navigationHeight) = strongSelf.validLayout { - strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) - } - } - } - controller.presentInGlobalOverlay(contextController) - } - case .addMember: - self.openAddMember() - case .search: - self.openChatWithMessageSearch() - case .leave: - self.openLeavePeer(delete: false) - case .stop: - self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .universal(animation: "anim_banned", scale: 0.066, colors: [:], title: self.presentationData.strings.PeerInfo_BotBlockedTitle, text: self.presentationData.strings.PeerInfo_BotBlockedText, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) - self.updateBlocked(block: true) - case .addContact: - self.openAddContact() - } - } - - private func openChatWithMessageSearch() { - if let navigationController = (self.controller?.navigationController as? NavigationController) { - if case let .replyThread(currentMessage) = self.chatLocation, let current = navigationController.viewControllers.first(where: { controller in - if let controller = controller as? ChatController, case let .replyThread(message) = controller.chatLocation, message.peerId == currentMessage.peerId, message.threadId == currentMessage.threadId { - return true - } - return false - }) as? ChatController { - var viewControllers = navigationController.viewControllers - if let index = viewControllers.firstIndex(of: current) { - viewControllers.removeSubrange(index + 1 ..< viewControllers.count) - } - navigationController.setViewControllers(viewControllers, animated: true) - current.activateSearch(domain: .everything, query: "") - } else if let peer = self.data?.chatPeer { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(EnginePeer(peer)), keepStack: self.nearbyPeerDistance != nil ? .always : .default, activateMessageSearch: (.everything, ""), peerNearbyData: self.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0) }), completion: { [weak self] _ in - if let strongSelf = self, strongSelf.nearbyPeerDistance != nil { - var viewControllers = navigationController.viewControllers - viewControllers = viewControllers.filter { controller in - if controller is PeerInfoScreen { - return false - } - return true - } - navigationController.setViewControllers(viewControllers, animated: false) - } - })) - } - } - } - - private func openChatForReporting(title: String, option: Data, message: String?) { - if let peer = self.data?.peer, let navigationController = (self.controller?.navigationController as? NavigationController) { - if let channel = peer as? TelegramChannel, channel.isForumOrMonoForum { - //let _ = self.context.engine.peers.reportPeer(peerId: peer.id, reason: reason, message: "").startStandalone() - //self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .emoji(name: "PoliceCar", text: self.presentationData.strings.Report_Succeed), elevatedLayout: false, action: { _ in return false }), in: .current) - } else { - self.context.sharedContext.navigateToChatController( - NavigateToChatControllerParams( - navigationController: navigationController, - context: self.context, - chatLocation: .peer(EnginePeer(peer)), - keepStack: .default, - reportReason: NavigateToChatControllerParams.ReportReason(title: title, option: option, message: message), - completion: { _ in - } - ) - ) - } - } - } - - private func openChatForThemeChange() { - if let peer = self.data?.peer, let navigationController = (self.controller?.navigationController as? NavigationController) { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(EnginePeer(peer)), keepStack: .default, changeColors: true, completion: { _ in - })) - } - } - - private func openChatForTranslation() { - if let peer = self.data?.peer, let navigationController = (self.controller?.navigationController as? NavigationController) { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(EnginePeer(peer)), keepStack: .default, changeColors: false, completion: { _ in - })) - } - } - - private func openAutoremove(currentValue: Int32?) { + func openAutoremove(currentValue: Int32?) { let controller = ChatTimerScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, style: .default, mode: .autoremove, currentTime: currentValue, dismissByTapOutside: true, completion: { [weak self] value in guard let strongSelf = self else { return @@ -7530,7 +3031,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro self.controller?.present(controller, in: .window(.root)) } - private func openCustomMute() { + func openCustomMute() { let controller = ChatTimerScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, style: .default, mode: .mute, currentTime: nil, dismissByTapOutside: true, completion: { [weak self] value in guard let strongSelf = self, let peer = strongSelf.data?.peer else { return @@ -7549,7 +3050,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro self.controller?.present(controller, in: .window(.root)) } - private func setAutoremove(timeInterval: Int32?) { + func setAutoremove(timeInterval: Int32?) { let _ = (self.context.engine.peers.setChatMessageAutoremoveTimeoutInteractively(peerId: self.peerId, timeout: timeInterval) |> deliverOnMainQueue).startStandalone(completed: { [weak self] in guard let strongSelf = self else { @@ -7569,7 +3070,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro }) } - private func openStartSecretChat() { + func openStartSecretChat() { guard let controller = self.controller, !controller.presentAccountFrozenInfoIfNeeded() else { return } @@ -7656,7 +3157,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro }) } - private func openClearHistory(contextController: ContextControllerProtocol, clearPeerHistory: ClearPeerHistory, peer: Peer, chatPeer: Peer) { + func openClearHistory(contextController: ContextControllerProtocol, clearPeerHistory: ClearPeerHistory, peer: Peer, chatPeer: Peer) { var subItems: [ContextMenuItem] = [] subItems.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Common_Back, icon: { theme in @@ -7729,853 +3230,6 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro contextController.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) } - private func openUsername(value: String, isMainUsername: Bool, progress: Promise?) { - let url: String - if value.hasPrefix("https://") { - url = value - } else { - url = "https://t.me/\(value)" - } - - let openShare: (TelegramCollectibleItemInfo?) -> Void = { [weak self] collectibleItemInfo in - guard let self else { - return - } - let shareController = ShareController(context: self.context, subject: .url(url), updatedPresentationData: self.controller?.updatedPresentationData, collectibleItemInfo: collectibleItemInfo) - shareController.completed = { [weak self] peerIds in - guard let strongSelf = self else { - return - } - let _ = (strongSelf.context.engine.data.get( - EngineDataList( - peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init) - ) - ) - |> deliverOnMainQueue).startStandalone(next: { [weak self] peerList in - guard let strongSelf = self else { - return - } - - let peers = peerList.compactMap { $0 } - let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - - let text: String - var savedMessages = false - if peerIds.count == 1, let peerId = peerIds.first, peerId == strongSelf.context.account.peerId { - text = presentationData.strings.UserInfo_LinkForwardTooltip_SavedMessages_One - savedMessages = true - } else { - if peers.count == 1, let peer = peers.first { - let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - text = presentationData.strings.UserInfo_LinkForwardTooltip_Chat_One(peerName).string - } else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last { - let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - text = presentationData.strings.UserInfo_LinkForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string - } else if let peer = peers.first { - let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - text = presentationData.strings.UserInfo_LinkForwardTooltip_ManyChats_One(peerName, "\(peers.count - 1)").string - } else { - text = "" - } - } - - strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in - if savedMessages, let self, action == .info { - let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) - |> deliverOnMainQueue).start(next: { [weak self] peer in - guard let self, let peer else { - return - } - guard let navigationController = self.controller?.navigationController as? NavigationController else { - return - } - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), forceOpenChat: true)) - }) - } - return false - }), in: .current) - }) - } - shareController.actionCompleted = { [weak self] in - if let strongSelf = self { - let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) - } - } - self.view.endEditing(true) - self.controller?.present(shareController, in: .window(.root)) - } - - if let pathComponents = URL(string: url)?.pathComponents, pathComponents.count >= 2, !pathComponents[1].isEmpty { - let namePart = pathComponents[1] - progress?.set(.single(true)) - let _ = (self.context.sharedContext.makeCollectibleItemInfoScreenInitialData(context: self.context, peerId: self.peerId, subject: .username(namePart)) - |> deliverOnMainQueue).start(next: { [weak self] initialData in - guard let self else { - return - } - - progress?.set(.single(false)) - - if let initialData { - if isMainUsername { - openShare(initialData.collectibleItemInfo) - } else { - self.view.endEditing(true) - self.controller?.push(self.context.sharedContext.makeCollectibleItemInfoScreen(context: self.context, initialData: initialData)) - } - } else { - openShare(nil) - } - }) - } else { - openShare(nil) - } - } - - private func requestCall(isVideo: Bool, gesture: ContextGesture? = nil, contextController: ContextControllerProtocol? = nil, result: ((ContextMenuActionResult) -> Void)? = nil, backAction: ((ContextControllerProtocol) -> Void)? = nil) { - guard let controller = self.controller, !controller.presentAccountFrozenInfoIfNeeded() else { - return - } - let peerId = self.peerId - let requestCall: (PeerId?, EngineGroupCallDescription?) -> Void = { [weak self] defaultJoinAsPeerId, activeCall in - if let activeCall = activeCall { - self?.context.joinGroupCall(peerId: peerId, invite: nil, requestJoinAsPeerId: { completion in - if let defaultJoinAsPeerId = defaultJoinAsPeerId { - result?(.dismissWithoutContent) - completion(defaultJoinAsPeerId) - } else { - self?.openVoiceChatDisplayAsPeerSelection(completion: { joinAsPeerId in - completion(joinAsPeerId) - }, gesture: gesture, contextController: contextController, result: result, backAction: backAction) - } - }, activeCall: activeCall) - } else { - self?.openVoiceChatOptions(defaultJoinAsPeerId: defaultJoinAsPeerId, gesture: gesture, contextController: contextController) - } - } - - if let cachedChannelData = self.data?.cachedData as? CachedChannelData { - requestCall(cachedChannelData.callJoinPeerId, cachedChannelData.activeCall.flatMap(EngineGroupCallDescription.init)) - return - } else if let cachedGroupData = self.data?.cachedData as? CachedGroupData { - requestCall(cachedGroupData.callJoinPeerId, cachedGroupData.activeCall.flatMap(EngineGroupCallDescription.init)) - return - } - - guard let peer = self.data?.peer as? TelegramUser, let cachedUserData = self.data?.cachedData as? CachedUserData else { - return - } - if cachedUserData.callsPrivate { - self.controller?.push(self.context.sharedContext.makeSendInviteLinkScreen(context: self.context, subject: .groupCall(.create), peers: [TelegramForbiddenInvitePeer( - peer: EnginePeer(peer), - canInviteWithPremium: false, - premiumRequiredToContact: false - )], theme: self.presentationData.theme)) - return - } - - self.context.requestCall(peerId: peer.id, isVideo: isVideo, completion: {}) - } - - private func scheduleGroupCall() { - guard let controller = self.controller, !controller.presentAccountFrozenInfoIfNeeded() else { - return - } - self.context.scheduleGroupCall(peerId: self.peerId, parentController: controller) - } - - private func createExternalStream(credentialsPromise: Promise?) { - guard let controller = self.controller, !controller.presentAccountFrozenInfoIfNeeded() else { - return - } - self.controller?.push(CreateExternalMediaStreamScreen(context: self.context, peerId: self.peerId, credentialsPromise: credentialsPromise, mode: .create(liveStream: false))) - } - - private func createAndJoinGroupCall(peerId: PeerId, joinAsPeerId: PeerId?) { - guard let controller = self.controller, !controller.presentAccountFrozenInfoIfNeeded() else { - return - } - if let _ = self.context.sharedContext.callManager { - let startCall: (Bool) -> Void = { [weak self] endCurrentIfAny in - guard let strongSelf = self else { - return - } - - var cancelImpl: (() -> Void)? - let presentationData = strongSelf.presentationData - let progressSignal = Signal { [weak self] subscriber in - let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: { - cancelImpl?() - })) - self?.controller?.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() - let createSignal = strongSelf.context.engine.calls.createGroupCall(peerId: peerId, title: nil, scheduleDate: nil, isExternalStream: false) - |> afterDisposed { - Queue.mainQueue().async { - progressDisposable.dispose() - } - } - cancelImpl = { [weak self] in - self?.activeActionDisposable.set(nil) - } - strongSelf.activeActionDisposable.set((createSignal - |> deliverOnMainQueue).start(next: { [weak self] info in - guard let strongSelf = self else { - return - } - strongSelf.context.joinGroupCall(peerId: peerId, invite: nil, requestJoinAsPeerId: { result in - result(joinAsPeerId) - }, activeCall: EngineGroupCallDescription(id: info.id, accessHash: info.accessHash, title: info.title, scheduleTimestamp: nil, subscribedToScheduled: false, isStream: info.isStream)) - }, error: { [weak self] error in - guard let strongSelf = self else { - return - } - strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel, nil, nil) - - let text: String - switch error { - case .generic, .scheduledTooLate: - text = strongSelf.presentationData.strings.Login_UnknownError - case .anonymousNotAllowed: - if let channel = strongSelf.data?.peer as? TelegramChannel, case .broadcast = channel.info { - text = strongSelf.presentationData.strings.LiveStream_AnonymousDisabledAlertText - } else { - text = strongSelf.presentationData.strings.VoiceChat_AnonymousDisabledAlertText - } - } - strongSelf.controller?.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.controller?.updatedPresentationData, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - })) - } - - startCall(true) - } - } - - private func openUsernameContextMenu(node: ASDisplayNode, gesture: ContextGesture?) { - guard let sourceNode = node as? ContextExtractedContentContainingNode else { - return - } - guard let peer = self.data?.peer else { - return - } - guard let username = peer.addressName else { - return - } - - let copyAction = { [weak self] in - guard let self else { - return - } - UIPasteboard.general.string = "@\(username)" - - self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .copy(text: self.presentationData.strings.Conversation_UsernameCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) - } - - var items: [ContextMenuItem] = [] - - if self.isMyProfile { - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_UsernameActionEdit, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c?.dismiss { - guard let self else { - return - } - self.openSettings(section: .username) - } - }))) - } - - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_UsernameActionCopy, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c?.dismiss { - copyAction() - } - }))) - - let actions = ContextController.Items(content: .list(items)) - - let contextController = ContextController(presentationData: self.presentationData, source: .extracted(PeerInfoContextExtractedContentSource(sourceNode: sourceNode)), items: .single(actions), gesture: gesture) - self.controller?.present(contextController, in: .window(.root)) - } - - private func openBioContextMenu(node: ASDisplayNode, gesture: ContextGesture?) { - let _ = (self.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.translationSettings]) - |> take(1) - |> deliverOnMainQueue).startStandalone(next: { [weak self] sharedData in - guard let self else { - return - } - - let translationSettings: TranslationSettings - if let current = sharedData.entries[ApplicationSpecificSharedDataKeys.translationSettings]?.get(TranslationSettings.self) { - translationSettings = current - } else { - translationSettings = TranslationSettings.defaultSettings - } - - guard let sourceNode = node as? ContextExtractedContentContainingNode else { - return - } - guard let cachedData = self.data?.cachedData else { - return - } - - var bioText: String? - if let cachedData = cachedData as? CachedUserData { - bioText = cachedData.about - } else if let cachedData = cachedData as? CachedChannelData { - bioText = cachedData.about - } else if let cachedData = cachedData as? CachedGroupData { - bioText = cachedData.about - } - - guard let bioText, !bioText.isEmpty else { - return - } - - let copyAction = { [weak self] in - guard let self else { - return - } - UIPasteboard.general.string = bioText - - let toastText: String - if let _ = self.data?.peer as? TelegramUser { - toastText = self.presentationData.strings.MyProfile_ToastBioCopied - } else { - toastText = self.presentationData.strings.ChannelProfile_ToastAboutCopied - } - - self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .copy(text: toastText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) - } - - var items: [ContextMenuItem] = [] - - if self.isMyProfile { - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_BioActionEdit, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c?.dismiss { - guard let self else { - return - } - self.headerNode.navigationButtonContainer.performAction?(.edit, nil, nil) - - for (_, section) in self.editingSections { - for (id, itemNode) in section.itemNodes { - if id == AnyHashable("bio_edit") { - if let itemNode = itemNode as? PeerInfoScreenMultilineInputItemNode { - itemNode.focus() - } - break - } - } - } - } - }))) - } - - let copyText: String - if let _ = self.data?.peer as? TelegramUser { - copyText = self.presentationData.strings.MyProfile_BioActionCopy - } else { - copyText = self.presentationData.strings.ChannelProfile_AboutActionCopy - } - items.append(.action(ContextMenuActionItem(text: copyText, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c?.dismiss { - copyAction() - } - }))) - - let (canTranslate, language) = canTranslateText(context: self.context, text: bioText, showTranslate: translationSettings.showTranslate, showTranslateIfTopical: false, ignoredLanguages: translationSettings.ignoredLanguages) - if canTranslate { - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Conversation_ContextMenuTranslate, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Translate"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c?.dismiss { - guard let self else { - return - } - - let controller = TranslateScreen(context: self.context, text: bioText, canCopy: true, fromLanguage: language, ignoredLanguages: translationSettings.ignoredLanguages) - controller.pushController = { [weak self] c in - (self?.controller?.navigationController as? NavigationController)?._keepModalDismissProgress = true - self?.controller?.push(c) - } - controller.presentController = { [weak self] c in - self?.controller?.present(c, in: .window(.root)) - } - self.controller?.present(controller, in: .window(.root)) - } - }))) - } - - let actions = ContextController.Items(content: .list(items)) - - let contextController = ContextController(presentationData: self.presentationData, source: .extracted(PeerInfoContextExtractedContentSource(sourceNode: sourceNode)), items: .single(actions), gesture: gesture) - self.controller?.present(contextController, in: .window(.root)) - }) - } - - private func openWorkingHoursContextMenu(node: ASDisplayNode, gesture: ContextGesture?) { - guard let sourceNode = node as? ContextExtractedContentContainingNode else { - return - } - guard let cachedData = self.data?.cachedData else { - return - } - - var businessHours: TelegramBusinessHours? - if let cachedData = cachedData as? CachedUserData { - businessHours = cachedData.businessHours - } - - guard let businessHours else { - return - } - - let copyAction = { [weak self] in - guard let self else { - return - } - UIPasteboard.general.string = businessHoursTextToCopy(businessHours: businessHours, presentationData: self.presentationData, displayLocalTimezone: false) - - self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .copy(text: self.presentationData.strings.MyProfile_ToastHoursCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) - } - - var items: [ContextMenuItem] = [] - - if self.isMyProfile { - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_HoursActionEdit, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c?.dismiss { - guard let self else { - return - } - let businessHoursSetupScreen = self.context.sharedContext.makeBusinessHoursSetupScreen(context: self.context, initialValue: businessHours, completion: { _ in }) - self.controller?.push(businessHoursSetupScreen) - } - }))) - } - - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_HoursActionCopy, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c?.dismiss { - copyAction() - } - }))) - - if self.isMyProfile { - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_HoursActionRemove, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, _ - in - guard let self else { - return - } - - var subItems: [ContextMenuItem] = [] - let noAction: ((ContextMenuActionItem.Action) -> Void)? = nil - subItems.append(.action(ContextMenuActionItem( - text: self.presentationData.strings.MyProfile_HoursRemoveConfirmation_Title, - textLayout: .multiline, - textFont: .small, - icon: { _ in nil }, - action: noAction - ))) - subItems.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_HoursRemoveConfirmation_Action, textColor: .destructive, icon: { _ in nil }, action: { [weak self] c, _ in - c?.dismiss { - guard let self else { - return - } - let _ = self.context.engine.accountData.updateAccountBusinessHours(businessHours: nil).startStandalone() - } - }))) - c?.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) - }))) - } - - let actions = ContextController.Items(content: .list(items)) - - let contextController = ContextController(presentationData: self.presentationData, source: .extracted(PeerInfoContextExtractedContentSource(sourceNode: sourceNode)), items: .single(actions), gesture: gesture) - self.controller?.present(contextController, in: .window(.root)) - } - - private func openBusinessLocationContextMenu(node: ASDisplayNode, gesture: ContextGesture?) { - guard let sourceNode = node as? ContextExtractedContentContainingNode else { - return - } - guard let cachedData = self.data?.cachedData else { - return - } - - var businessLocation: TelegramBusinessLocation? - if let cachedData = cachedData as? CachedUserData { - businessLocation = cachedData.businessLocation - } - - guard let businessLocation else { - return - } - - let copyAction = { [weak self] in - guard let self else { - return - } - UIPasteboard.general.string = businessLocation.address - - self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .copy(text: self.presentationData.strings.MyProfile_ToastLocationCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) - } - - var items: [ContextMenuItem] = [] - - if businessLocation.coordinates != nil { - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_LocationActionOpen, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Media Editor/LocationSmall"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c?.dismiss(completion: { - guard let self else { - return - } - self.interaction.openLocation() - }) - }))) - } - - if !businessLocation.address.isEmpty { - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_LocationActionCopy, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c?.dismiss { - copyAction() - } - }))) - } - - if self.isMyProfile { - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_LocationActionEdit, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c?.dismiss { - guard let self else { - return - } - let businessLocationSetupScreen = self.context.sharedContext.makeBusinessLocationSetupScreen(context: self.context, initialValue: businessLocation, completion: { _ in }) - self.controller?.push(businessLocationSetupScreen) - } - }))) - - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_LocationActionRemove, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, _ in - guard let self else { - return - } - - var subItems: [ContextMenuItem] = [] - let noAction: ((ContextMenuActionItem.Action) -> Void)? = nil - subItems.append(.action(ContextMenuActionItem( - text: self.presentationData.strings.MyProfile_LocationRemoveConfirmation_Title, - textLayout: .multiline, - textFont: .small, - icon: { _ in nil }, - action: noAction - ))) - subItems.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_LocationRemoveConfirmation_Action, textColor: .destructive, icon: { _ in nil }, action: { [weak self] c, _ in - c?.dismiss { - guard let self else { - return - } - let _ = self.context.engine.accountData.updateAccountBusinessLocation(businessLocation: nil).startStandalone() - } - }))) - c?.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) - }))) - } - - let actions = ContextController.Items(content: .list(items)) - - let contextController = ContextController(presentationData: self.presentationData, source: .extracted(PeerInfoContextExtractedContentSource(sourceNode: sourceNode)), items: .single(actions), gesture: gesture) - self.controller?.present(contextController, in: .window(.root)) - } - - private func openNoteContextMenu(node: ASDisplayNode, gesture: ContextGesture?) { - guard let sourceNode = node as? ContextExtractedContentContainingNode else { - return - } - guard let cachedData = self.data?.cachedData else { - return - } - - var noteText: String? - var noteEntities: [MessageTextEntity]? - if let cachedData = cachedData as? CachedUserData { - noteText = cachedData.note?.text - noteEntities = cachedData.note?.entities - } - - guard let noteText, !noteText.isEmpty else { - return - } - - let copyAction = { [weak self] in - guard let self else { - return - } - storeMessageTextInPasteboard(noteText, entities: noteEntities ?? []) - - let toastText = self.presentationData.strings.PeerInfo_ToastNoteCopied - self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .copy(text: toastText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) - } - - var items: [ContextMenuItem] = [] - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_NoteActionEdit, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c?.dismiss { - guard let self else { - return - } - self.headerNode.navigationButtonContainer.performAction?(.edit, nil, nil) - - for (_, section) in self.editingSections { - for (id, itemNode) in section.itemNodes { - if id == AnyHashable("note_edit") { - if let itemNode = itemNode as? PeerInfoScreenNoteListItemNode { - itemNode.focus() - } - break - } - } - } - } - }))) - - let copyText = self.presentationData.strings.PeerInfo_NoteActionCopy - items.append(.action(ContextMenuActionItem(text: copyText, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c?.dismiss { - copyAction() - } - }))) - - let actions = ContextController.Items(content: .list(items)) - - let contextController = ContextController(presentationData: self.presentationData, source: .extracted(PeerInfoContextExtractedContentSource(sourceNode: sourceNode)), items: .single(actions), gesture: gesture) - self.controller?.present(contextController, in: .window(.root)) - } - - private func openBirthdayContextMenu(node: ASDisplayNode, gesture: ContextGesture?) { - guard let sourceNode = node as? ContextExtractedContentContainingNode else { - return - } - guard let cachedData = self.data?.cachedData else { - return - } - - var birthday: TelegramBirthday? - if let cachedData = cachedData as? CachedUserData { - birthday = cachedData.birthday - } - - guard let birthday else { - return - } - - let copyAction = { [weak self] in - guard let self else { - return - } - let presentationData = self.presentationData - let text = stringForCompactBirthday(birthday, strings: presentationData.strings) - - UIPasteboard.general.string = text - - self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .copy(text: self.presentationData.strings.MyProfile_ToastBirthdayCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) - } - - var items: [ContextMenuItem] = [] - - if self.isMyProfile { - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_BirthdayActionEdit, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c?.dismiss { - guard let self else { - return - } - - self.state = self.state.withIsEditingBirthDate(true) - self.headerNode.navigationButtonContainer.performAction?(.edit, nil, nil) - } - }))) - } - - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_BirthdayActionCopy, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c?.dismiss { - copyAction() - } - }))) - - let actions = ContextController.Items(content: .list(items)) - - let contextController = ContextController(presentationData: self.presentationData, source: .extracted(PeerInfoContextExtractedContentSource(sourceNode: sourceNode)), items: .single(actions), gesture: gesture) - self.controller?.present(contextController, in: .window(.root)) - } - - private func openPhone(value: String, node: ASDisplayNode, gesture: ContextGesture?, progress: Promise?) { - guard let sourceNode = node as? ContextExtractedContentContainingNode else { - return - } - - let formattedPhoneNumber = formatPhoneNumber(context: self.context, number: value) - if gesture == nil, formattedPhoneNumber.hasPrefix("+888") { - let collectibleInfo = Promise() - collectibleInfo.set(self.context.sharedContext.makeCollectibleItemInfoScreenInitialData(context: self.context, peerId: self.peerId, subject: .phoneNumber(value))) - - progress?.set(.single(true)) - let _ = (collectibleInfo.get() - |> take(1) - |> deliverOnMainQueue).start(next: { [weak self] initialData in - progress?.set(.single(false)) - - guard let self else { - return - } - if let initialData { - self.view.endEditing(true) - self.controller?.push(self.context.sharedContext.makeCollectibleItemInfoScreen(context: self.context, initialData: initialData)) - } else { - self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: "https://fragment.com/numbers", forceExternal: true, presentationData: self.presentationData, navigationController: nil, dismissInput: {}) - } - }) - - return - } - - let _ = (combineLatest( - getUserPeer(engine: self.context.engine, peerId: self.peerId), - getUserPeer(engine: self.context.engine, peerId: self.context.account.peerId) - ) |> deliverOnMainQueue).startStandalone(next: { [weak self] peer, accountPeer in - guard let strongSelf = self else { - return - } - let presentationData = strongSelf.presentationData - - let telegramCallAction: (Bool) -> Void = { [weak self] isVideo in - guard let strongSelf = self else { - return - } - strongSelf.requestCall(isVideo: isVideo) - } - - let phoneCallAction = { [weak self] in - guard let strongSelf = self else { - return - } - strongSelf.context.sharedContext.applicationBindings.openUrl("tel:\(formatPhoneNumber(context: strongSelf.context, number: value).replacingOccurrences(of: " ", with: ""))") - } - - let copyAction = { [weak self] in - guard let strongSelf = self else { - return - } - UIPasteboard.general.string = formatPhoneNumber(context: strongSelf.context, number: value) - - strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .copy(text: presentationData.strings.Conversation_PhoneCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) - } - - var accountIsFromUS = false - if let accountPeer, case let .user(user) = accountPeer, let phone = user.phone { - if let (country, _) = lookupCountryIdByNumber(phone, configuration: strongSelf.context.currentCountriesConfiguration.with { $0 }) { - if country.id == "US" { - accountIsFromUS = true - } - } - } - - var isAnonymousNumber = false - var items: [ContextMenuItem] = [] - - if strongSelf.isMyProfile { - items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.MyProfile_PhoneActionEdit, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in - c?.dismiss { - guard let self else { - return - } - self.openSettings(section: .phoneNumber) - } - }))) - } - - if case let .user(peer) = peer, let peerPhoneNumber = peer.phone, formattedPhoneNumber == formatPhoneNumber(context: strongSelf.context, number: peerPhoneNumber) { - if !strongSelf.isMyProfile { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_TelegramCall, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Call"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c?.dismiss { - telegramCallAction(false) - } - }))) - items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_TelegramVideoCall, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/VideoCall"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c?.dismiss { - telegramCallAction(true) - } - }))) - } - if !formattedPhoneNumber.hasPrefix("+888") { - if !strongSelf.isMyProfile { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_PhoneCall, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/PhoneCall"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c?.dismiss { - phoneCallAction() - } - }))) - } - } else { - isAnonymousNumber = true - } - items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.MyProfile_PhoneActionCopy, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c?.dismiss { - copyAction() - } - }))) - } else { - if !formattedPhoneNumber.hasPrefix("+888") { - if !strongSelf.isMyProfile { - items.append( - .action(ContextMenuActionItem(text: presentationData.strings.UserInfo_PhoneCall, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/PhoneCall"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c?.dismiss { - phoneCallAction() - } - })) - ) - } - } else { - isAnonymousNumber = true - } - items.append( - .action(ContextMenuActionItem(text: strongSelf.presentationData.strings.MyProfile_PhoneActionCopy, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in - c?.dismiss { - copyAction() - } - })) - ) - } - var actions = ContextController.Items(content: .list(items)) - if isAnonymousNumber && !accountIsFromUS { - let collectibleInfo = Promise() - collectibleInfo.set(strongSelf.context.sharedContext.makeCollectibleItemInfoScreenInitialData(context: strongSelf.context, peerId: strongSelf.peerId, subject: .phoneNumber(value))) - - actions.tip = .animatedEmoji(text: strongSelf.presentationData.strings.UserInfo_AnonymousNumberInfo, arguments: nil, file: nil, action: { [weak self] in - guard let self else { - return - } - - let _ = (collectibleInfo.get() - |> take(1) - |> deliverOnMainQueue).start(next: { [weak self] initialData in - guard let self else { - return - } - if let initialData { - self.view.endEditing(true) - self.controller?.push(self.context.sharedContext.makeCollectibleItemInfoScreen(context: self.context, initialData: initialData)) - } else { - self.context.sharedContext.openExternalUrl(context: strongSelf.context, urlContext: .generic, url: "https://fragment.com/numbers", forceExternal: true, presentationData: self.presentationData, navigationController: nil, dismissInput: {}) - } - }) - }) - } - let contextController = ContextController(presentationData: strongSelf.presentationData, source: .extracted(PeerInfoContextExtractedContentSource(sourceNode: sourceNode)), items: .single(actions), gesture: gesture) - strongSelf.controller?.present(contextController, in: .window(.root)) - }) - } - private func editingOpenNotificationSettings() { let _ = (combineLatest( self.context.engine.data.get( @@ -8717,63 +3371,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro self.controller?.present(actionSheet, in: .window(.root)) } - private func openChat(peerId: EnginePeer.Id?) { - if let peerId { - let _ = (self.context.engine.data.get( - TelegramEngine.EngineData.Item.Peer.Peer(id: peerId) - ) - |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in - guard let self, let peer else { - return - } - guard let navigationController = self.controller?.navigationController as? NavigationController else { - return - } - - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), keepStack: .always)) - }) - return - } - - if let peer = self.data?.peer, let navigationController = self.controller?.navigationController as? NavigationController { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(EnginePeer(peer)), keepStack: self.nearbyPeerDistance != nil ? .always : .default, peerNearbyData: self.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0) }), completion: { [weak self] _ in - if let strongSelf = self, strongSelf.nearbyPeerDistance != nil { - var viewControllers = navigationController.viewControllers - viewControllers = viewControllers.filter { controller in - if controller is PeerInfoScreen { - return false - } - return true - } - navigationController.setViewControllers(viewControllers, animated: false) - } - })) - } - } - - private func openChatWithClearedHistory(type: InteractiveHistoryClearingType) { - guard let peer = self.data?.chatPeer, let navigationController = self.controller?.navigationController as? NavigationController else { - return - } - - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(EnginePeer(peer)), keepStack: self.nearbyPeerDistance != nil ? .always : .default, peerNearbyData: self.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0) }), setupController: { controller in - controller.beginClearHistory(type: type) - }, completion: { [weak self] _ in - if let strongSelf = self, strongSelf.nearbyPeerDistance != nil { - var viewControllers = navigationController.viewControllers - viewControllers = viewControllers.filter { controller in - if controller is PeerInfoScreen { - return false - } - return true - } - - navigationController.setViewControllers(viewControllers, animated: false) - } - })) - } - - private func openAddContact() { + func openAddContact() { let _ = (getUserPeer(engine: self.context.engine, peerId: self.peerId) |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in guard let strongSelf = self, let peer = peer else { @@ -8791,7 +3389,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro }) } - private func updateBlocked(block: Bool) { + func updateBlocked(block: Bool) { let _ = (getUserPeer(engine: self.context.engine, peerId: self.peerId) |> take(1) |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in @@ -8859,11 +3457,11 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro }) } - private func openStoryArchive() { + func openStoryArchive() { self.controller?.push(PeerInfoStoryGridScreen(context: self.context, peerId: self.peerId, scope: .archive)) } - private func openStats(section: ChannelStatsSection, boostStatus: ChannelBoostStatus? = nil) { + func openStats(section: ChannelStatsSection, boostStatus: ChannelBoostStatus? = nil) { guard let controller = self.controller, let data = self.data, let peer = data.peer else { return } @@ -8882,7 +3480,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro controller.push(statsController) } - private func openBoost() { + func openBoost() { guard let peer = self.data?.peer, let channel = peer as? TelegramChannel, let controller = self.controller else { return } @@ -8915,224 +3513,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } - private func openVoiceChatOptions(defaultJoinAsPeerId: PeerId?, gesture: ContextGesture? = nil, contextController: ContextControllerProtocol? = nil) { - guard let chatPeer = self.data?.peer else { - return - } - let context = self.context - let peerId = self.peerId - let defaultJoinAsPeerId = defaultJoinAsPeerId ?? self.context.account.peerId - let currentAccountPeer = self.context.account.postbox.loadedPeerWithId(self.context.account.peerId) - |> map { peer in - return [FoundPeer(peer: peer, subscribers: nil)] - } - let _ = (combineLatest(queue: Queue.mainQueue(), currentAccountPeer, self.displayAsPeersPromise.get() |> take(1)) - |> map { currentAccountPeer, availablePeers -> [FoundPeer] in - var result = currentAccountPeer - result.append(contentsOf: availablePeers) - return result - }).startStandalone(next: { [weak self] peers in - guard let strongSelf = self else { - return - } - - var items: [ContextMenuItem] = [] - - if peers.count > 1 { - var selectedPeer: FoundPeer? - for peer in peers { - if peer.peer.id == defaultJoinAsPeerId { - selectedPeer = peer - } - } - if let peer = selectedPeer { - let avatarSize = CGSize(width: 28.0, height: 28.0) - items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_DisplayAs, textLayout: .secondLineWithValue(EnginePeer(peer.peer).displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)), icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: avatarSize, signal: peerAvatarCompleteImage(account: strongSelf.context.account, peer: EnginePeer(peer.peer), size: avatarSize)), action: { c, f in - guard let strongSelf = self else { - return - } - - strongSelf.openVoiceChatDisplayAsPeerSelection(completion: { joinAsPeerId in - let _ = context.engine.calls.updateGroupCallJoinAsPeer(peerId: peerId, joinAs: joinAsPeerId).startStandalone() - self?.openVoiceChatOptions(defaultJoinAsPeerId: joinAsPeerId, gesture: nil, contextController: c) - }, gesture: gesture, contextController: c, result: f, backAction: { [weak self] c in - self?.openVoiceChatOptions(defaultJoinAsPeerId: defaultJoinAsPeerId, gesture: nil, contextController: c) - }) - - }))) - items.append(.separator) - } - } - - let createVoiceChatTitle: String - let scheduleVoiceChatTitle: String - if let channel = strongSelf.data?.peer as? TelegramChannel, case .broadcast = channel.info { - createVoiceChatTitle = strongSelf.presentationData.strings.ChannelInfo_CreateLiveStream - scheduleVoiceChatTitle = strongSelf.presentationData.strings.ChannelInfo_ScheduleLiveStream - } else { - createVoiceChatTitle = strongSelf.presentationData.strings.ChannelInfo_CreateVoiceChat - scheduleVoiceChatTitle = strongSelf.presentationData.strings.ChannelInfo_ScheduleVoiceChat - } - - items.append(.action(ContextMenuActionItem(text: createVoiceChatTitle, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/VoiceChat"), color: theme.contextMenu.primaryColor) }, action: { _, f in - f(.dismissWithoutContent) - - self?.createAndJoinGroupCall(peerId: peerId, joinAsPeerId: defaultJoinAsPeerId) - }))) - - items.append(.action(ContextMenuActionItem(text: scheduleVoiceChatTitle, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Schedule"), color: theme.contextMenu.primaryColor) }, action: { _, f in - f(.dismissWithoutContent) - - self?.scheduleGroupCall() - }))) - - var credentialsPromise: Promise? - var canCreateStream = false - switch chatPeer { - case let group as TelegramGroup: - if case .creator = group.role { - canCreateStream = true - } - case let channel as TelegramChannel: - if channel.hasPermission(.manageCalls) { - canCreateStream = true - credentialsPromise = Promise() - credentialsPromise?.set(context.engine.calls.getGroupCallStreamCredentials(peerId: peerId, isLiveStream: false, revokePreviousCredentials: false) |> `catch` { _ -> Signal in return .never() }) - } - default: - break - } - - if canCreateStream { - items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChannelInfo_CreateExternalStream, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/VoiceChat"), color: theme.contextMenu.primaryColor) }, action: { _, f in - f(.dismissWithoutContent) - - self?.createExternalStream(credentialsPromise: credentialsPromise) - }))) - } - - if let contextController = contextController { - contextController.setItems(.single(ContextController.Items(content: .list(items))), minHeight: nil, animated: true) - } else { - strongSelf.state = strongSelf.state.withHighlightedButton(.voiceChat) - if let (layout, navigationHeight) = strongSelf.validLayout { - strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) - } - - if let sourceNode = strongSelf.headerNode.buttonNodes[.voiceChat]?.referenceNode, let controller = strongSelf.controller { - let contextController = ContextController(presentationData: strongSelf.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) - contextController.dismissed = { [weak self] in - if let strongSelf = self { - strongSelf.state = strongSelf.state.withHighlightedButton(nil) - if let (layout, navigationHeight) = strongSelf.validLayout { - strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) - } - } - } - controller.presentInGlobalOverlay(contextController) - } - } - }) - } - - private func openVoiceChatDisplayAsPeerSelection(completion: @escaping (PeerId) -> Void, gesture: ContextGesture? = nil, contextController: ContextControllerProtocol? = nil, result: ((ContextMenuActionResult) -> Void)? = nil, backAction: ((ContextControllerProtocol) -> Void)? = nil) { - let dismissOnSelection = contextController == nil - let currentAccountPeer = self.context.account.postbox.loadedPeerWithId(context.account.peerId) - |> map { peer in - return [FoundPeer(peer: peer, subscribers: nil)] - } - let _ = (combineLatest(queue: Queue.mainQueue(), currentAccountPeer, self.displayAsPeersPromise.get() |> take(1)) - |> map { currentAccountPeer, availablePeers -> [FoundPeer] in - var result = currentAccountPeer - result.append(contentsOf: availablePeers) - return result - }).startStandalone(next: { [weak self] peers in - guard let strongSelf = self else { - return - } - if peers.count == 1, let peer = peers.first { - result?(.dismissWithoutContent) - completion(peer.peer.id) - } else { - var items: [ContextMenuItem] = [] - - var isGroup = false - for peer in peers { - if peer.peer is TelegramGroup { - isGroup = true - break - } else if let peer = peer.peer as? TelegramChannel, case .group = peer.info { - isGroup = true - break - } - } - - items.append(.custom(VoiceChatInfoContextItem(text: isGroup ? strongSelf.presentationData.strings.VoiceChat_DisplayAsInfoGroup : strongSelf.presentationData.strings.VoiceChat_DisplayAsInfo, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Call/Context Menu/Accounts"), color: theme.actionSheet.primaryTextColor) - }), true)) - - for peer in peers { - var subtitle: String? - if peer.peer.id.namespace == Namespaces.Peer.CloudUser { - subtitle = strongSelf.presentationData.strings.VoiceChat_PersonalAccount - } else if let subscribers = peer.subscribers { - if let peer = peer.peer as? TelegramChannel, case .broadcast = peer.info { - subtitle = strongSelf.presentationData.strings.Conversation_StatusSubscribers(subscribers) - } else { - subtitle = strongSelf.presentationData.strings.Conversation_StatusMembers(subscribers) - } - } - - let avatarSize = CGSize(width: 28.0, height: 28.0) - let avatarSignal = peerAvatarCompleteImage(account: strongSelf.context.account, peer: EnginePeer(peer.peer), size: avatarSize) - items.append(.action(ContextMenuActionItem(text: EnginePeer(peer.peer).displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), textLayout: subtitle.flatMap { .secondLineWithValue($0) } ?? .singleLine, icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: avatarSize, signal: avatarSignal), action: { _, f in - if dismissOnSelection { - f(.dismissWithoutContent) - } - completion(peer.peer.id) - }))) - - if peer.peer.id.namespace == Namespaces.Peer.CloudUser { - items.append(.separator) - } - } - if backAction != nil { - items.append(.separator) - items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Common_Back, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.actionSheet.primaryTextColor) - }, iconPosition: .left, action: { (c, _) in - if let c, let backAction = backAction { - backAction(c) - } - }))) - } - - if let contextController = contextController { - contextController.setItems(.single(ContextController.Items(content: .list(items))), minHeight: nil, animated: true) - } else { - strongSelf.state = strongSelf.state.withHighlightedButton(.voiceChat) - if let (layout, navigationHeight) = strongSelf.validLayout { - strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) - } - - if let sourceNode = strongSelf.headerNode.buttonNodes[.voiceChat]?.referenceNode, let controller = strongSelf.controller { - let contextController = ContextController(presentationData: strongSelf.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) - contextController.dismissed = { [weak self] in - if let strongSelf = self { - strongSelf.state = strongSelf.state.withHighlightedButton(nil) - if let (layout, navigationHeight) = strongSelf.validLayout { - strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) - } - } - } - controller.presentInGlobalOverlay(contextController) - } - } - } - }) - } - - private func openReport(type: PeerInfoReportType, contextController: ContextControllerProtocol?, backAction: ((ContextControllerProtocol) -> Void)?) { + func openReport(type: PeerInfoReportType, contextController: ContextControllerProtocol?, backAction: ((ContextControllerProtocol) -> Void)?) { self.view.endEditing(true) switch type { @@ -9189,7 +3570,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro self.controller?.push(SecretChatKeyController(context: self.context, fingerprint: encryptionKeyFingerprint, peer: EnginePeer(peer))) } - private func openShareLink(url: String) { + func openShareLink(url: String) { let shareController = ShareController(context: self.context, subject: .url(url), updatedPresentationData: self.controller?.updatedPresentationData) shareController.completed = { [weak self] peerIds in guard let strongSelf = self else { @@ -9256,7 +3637,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro self.controller?.present(shareController, in: .window(.root)) } - private func openShareBot() { + func openShareBot() { let _ = (getUserPeer(engine: self.context.engine, peerId: self.peerId) |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in guard let strongSelf = self else { @@ -9286,7 +3667,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro }, contentContext: nil, progress: nil, completion: nil) } - private func performBotCommand(command: PeerInfoBotCommand) { + func performBotCommand(command: PeerInfoBotCommand) { let _ = (self.context.account.postbox.loadedPeerWithId(peerId) |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in guard let strongSelf = self else { @@ -9750,31 +4131,6 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } - private func openRecentActions() { - guard let peer = self.data?.peer else { - return - } - let controller = self.context.sharedContext.makeChatRecentActionsController(context: self.context, peer: peer, adminPeerId: nil, starsState: self.data?.starsRevenueStatsState) - self.controller?.push(controller) - } - - private func openChannelMessages() { - guard let channel = self.data?.peer as? TelegramChannel, let linkedMonoforumId = channel.linkedMonoforumId else { - return - } - let _ = (self.context.engine.data.get( - TelegramEngine.EngineData.Item.Peer.Peer(id: linkedMonoforumId) - ) - |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in - guard let self, let peer else { - return - } - if let controller = self.controller, let navigationController = controller.navigationController as? NavigationController { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer))) - } - }) - } - private func editingOpenPreHistorySetup() { guard let data = self.data, let peer = data.peer else { return @@ -9809,13 +4165,6 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro self.controller?.push(channelPermissionsController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, peerId: peer.id)) } - private func editingOpenStickerPackSetup() { - guard let data = self.data, let peer = data.peer, let cachedData = data.cachedData as? CachedChannelData else { - return - } - self.controller?.push(groupStickerPackSetupController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, peerId: peer.id, currentPackInfo: cachedData.stickerPack)) - } - private func openLocation() { guard let data = self.data, let peer = data.peer else { return @@ -10009,170 +4358,6 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } - private func openPeerInfoContextMenu(subject: PeerInfoContextSubject, sourceNode: ASDisplayNode, sourceRect: CGRect?) { - guard let data = self.data, let peer = data.peer, let controller = self.controller else { - return - } - let context = self.context - switch subject { - case .birthday: - if let cachedData = data.cachedData as? CachedUserData, let birthday = cachedData.birthday { - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let text = stringForCompactBirthday(birthday, strings: presentationData.strings) - - let actions: [ContextMenuAction] = [ContextMenuAction(content: .text(title: presentationData.strings.Conversation_ContextMenuCopy, accessibilityLabel: presentationData.strings.Conversation_ContextMenuCopy), action: { [weak self] in - UIPasteboard.general.string = text - - self?.controller?.present(UndoOverlayController(presentationData: presentationData, content: .copy(text: presentationData.strings.Conversation_TextCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) - })] - let contextMenuController = makeContextMenuController(actions: actions) - controller.present(contextMenuController, in: .window(.root), with: ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self, weak sourceNode] in - if let controller = self?.controller, let sourceNode = sourceNode { - var rect = sourceNode.bounds.insetBy(dx: 0.0, dy: 2.0) - if let sourceRect = sourceRect { - rect = sourceRect.insetBy(dx: 0.0, dy: 2.0) - } - return (sourceNode, rect, controller.displayNode, controller.view.bounds) - } else { - return nil - } - })) - } - case .bio: - var text: String? - if let cachedData = data.cachedData as? CachedUserData { - text = cachedData.about - } else if let cachedData = data.cachedData as? CachedGroupData { - text = cachedData.about - } else if let cachedData = data.cachedData as? CachedChannelData { - text = cachedData.about - } - if let text = text, !text.isEmpty { - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let _ = (self.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.translationSettings]) - |> take(1) - |> deliverOnMainQueue).startStandalone(next: { [weak self] sharedData in - let translationSettings: TranslationSettings - if let current = sharedData.entries[ApplicationSpecificSharedDataKeys.translationSettings]?.get(TranslationSettings.self) { - translationSettings = current - } else { - translationSettings = TranslationSettings.defaultSettings - } - - var actions: [ContextMenuAction] = [ContextMenuAction(content: .text(title: presentationData.strings.Conversation_ContextMenuCopy, accessibilityLabel: presentationData.strings.Conversation_ContextMenuCopy), action: { [weak self] in - UIPasteboard.general.string = text - - self?.controller?.present(UndoOverlayController(presentationData: presentationData, content: .copy(text: presentationData.strings.Conversation_TextCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) - })] - - let (canTranslate, language) = canTranslateText(context: context, text: text, showTranslate: translationSettings.showTranslate, showTranslateIfTopical: false, ignoredLanguages: translationSettings.ignoredLanguages) - if canTranslate { - actions.append(ContextMenuAction(content: .text(title: presentationData.strings.Conversation_ContextMenuTranslate, accessibilityLabel: presentationData.strings.Conversation_ContextMenuTranslate), action: { [weak self] in - - let controller = TranslateScreen(context: context, text: text, canCopy: true, fromLanguage: language, ignoredLanguages: translationSettings.ignoredLanguages) - controller.pushController = { [weak self] c in - (self?.controller?.navigationController as? NavigationController)?._keepModalDismissProgress = true - self?.controller?.push(c) - } - controller.presentController = { [weak self] c in - self?.controller?.present(c, in: .window(.root)) - } - self?.controller?.present(controller, in: .window(.root)) - })) - } - - let contextMenuController = makeContextMenuController(actions: actions) - controller.present(contextMenuController, in: .window(.root), with: ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self, weak sourceNode] in - if let controller = self?.controller, let sourceNode = sourceNode { - var rect = sourceNode.bounds.insetBy(dx: 0.0, dy: 2.0) - if let sourceRect = sourceRect { - rect = sourceRect.insetBy(dx: 0.0, dy: 2.0) - } - return (sourceNode, rect, controller.displayNode, controller.view.bounds) - } else { - return nil - } - })) - }) - } - case let .phone(phone): - let contextMenuController = makeContextMenuController(actions: [ContextMenuAction(content: .text(title: self.presentationData.strings.Conversation_ContextMenuCopy, accessibilityLabel: self.presentationData.strings.Conversation_ContextMenuCopy), action: { [weak self] in - UIPasteboard.general.string = phone - - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - self?.controller?.present(UndoOverlayController(presentationData: presentationData, content: .copy(text: presentationData.strings.Conversation_PhoneCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) - })]) - controller.present(contextMenuController, in: .window(.root), with: ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self, weak sourceNode] in - if let controller = self?.controller, let sourceNode = sourceNode { - var rect = sourceNode.bounds.insetBy(dx: 0.0, dy: 2.0) - if let sourceRect = sourceRect { - rect = sourceRect.insetBy(dx: 0.0, dy: 2.0) - } - return (sourceNode, rect, controller.displayNode, controller.view.bounds) - } else { - return nil - } - })) - case let .link(customLink): - let text: String - let content: UndoOverlayContent - if let customLink = customLink { - text = customLink - content = .linkCopied(title: nil, text: self.presentationData.strings.Conversation_LinkCopied) - } else if let addressName = peer.addressName { - if peer is TelegramChannel { - text = "https://t.me/\(addressName)" - content = .linkCopied(title: nil, text: self.presentationData.strings.Conversation_LinkCopied) - } else { - text = "@" + addressName - content = .copy(text: self.presentationData.strings.Conversation_UsernameCopied) - } - } else { - text = "https://t.me/@id\(peer.id.id._internalGetInt64Value())" - content = .linkCopied(title: nil, text: self.presentationData.strings.Conversation_LinkCopied) - } - - let contextMenuController = makeContextMenuController(actions: [ContextMenuAction(content: .text(title: self.presentationData.strings.Conversation_ContextMenuCopy, accessibilityLabel: self.presentationData.strings.Conversation_ContextMenuCopy), action: { [weak self] in - UIPasteboard.general.string = text - - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - self?.controller?.present(UndoOverlayController(presentationData: presentationData, content: content, elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) - })]) - controller.present(contextMenuController, in: .window(.root), with: ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self, weak sourceNode] in - if let controller = self?.controller, let sourceNode = sourceNode { - var rect = sourceNode.bounds.insetBy(dx: 0.0, dy: 2.0) - if let sourceRect = sourceRect { - rect = sourceRect.insetBy(dx: 0.0, dy: 2.0) - } - return (sourceNode, rect, controller.displayNode, controller.view.bounds) - } else { - return nil - } - })) - case .businessHours(let text), .businessLocation(let text): - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - - let actions: [ContextMenuAction] = [ContextMenuAction(content: .text(title: presentationData.strings.Conversation_ContextMenuCopy, accessibilityLabel: presentationData.strings.Conversation_ContextMenuCopy), action: { [weak self] in - UIPasteboard.general.string = text - - self?.controller?.present(UndoOverlayController(presentationData: presentationData, content: .copy(text: presentationData.strings.Conversation_TextCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) - })] - - let contextMenuController = makeContextMenuController(actions: actions) - controller.present(contextMenuController, in: .window(.root), with: ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self, weak sourceNode] in - if let controller = self?.controller, let sourceNode = sourceNode { - var rect = sourceNode.bounds.insetBy(dx: 0.0, dy: 2.0) - if let sourceRect = sourceRect { - rect = sourceRect.insetBy(dx: 0.0, dy: 2.0) - } - return (sourceNode, rect, controller.displayNode, controller.view.bounds) - } else { - return nil - } - })) - } - } - private func performBioLinkAction(action: TextLinkItemActionType, item: TextLinkItem) { guard let data = self.data, let peer = data.peer, let controller = self.controller else { return @@ -10229,7 +4414,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro }) } - private func openLeavePeer(delete: Bool) { + func openLeavePeer(delete: Bool) { let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.peerId)) |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in guard let strongSelf = self, let peer = peer else { @@ -10270,8 +4455,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } strongSelf.view.endEditing(true) - - strongSelf.controller?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: title, text: text, actions: [ + + strongSelf.controller?.present(textAlertController(context: strongSelf.context, title: title, text: text, actions: [ TextAlertAction(type: .destructiveAction, title: actionText, action: { self?.deletePeerChat(peer: peer._asPeer(), globally: delete) }), @@ -10315,178 +4500,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } - func oldOpenAvatarForEditing(mode: PeerInfoAvatarEditingMode = .generic, fromGallery: Bool = false, completion: @escaping (UIImage?) -> Void = { _ in }) { - guard let peer = self.data?.peer, mode != .generic || canEditPeerInfo(context: self.context, peer: peer, chatLocation: self.chatLocation, threadData: self.data?.threadData) else { - return - } - - self.view.endEditing(true) - - var currentIsVideo = false - var emojiMarkup: TelegramMediaImage.EmojiMarkup? - let item = self.headerNode.avatarListNode.listContainerNode.currentItemNode?.item - if let item = item, case let .image(_, _, videoRepresentations, _, _, emojiMarkupValue) = item { - currentIsVideo = !videoRepresentations.isEmpty - emojiMarkup = emojiMarkupValue - } - let _ = emojiMarkup - - let peerId = self.peerId - let _ = (self.context.engine.data.get( - TelegramEngine.EngineData.Item.Peer.Peer(id: peerId), - TelegramEngine.EngineData.Item.Configuration.SearchBots() - ) - |> deliverOnMainQueue).startStandalone(next: { [weak self] peer, searchBotsConfiguration in - guard let strongSelf = self, let peer = peer else { - return - } - - let presentationData = strongSelf.presentationData - - var hasPhotos = false - if !peer.profileImageRepresentations.isEmpty { - hasPhotos = true - } - - var isForum = false - if let peer = strongSelf.data?.peer as? TelegramChannel, peer.isForumOrMonoForum { - isForum = true - } - - var hasDeleteButton = false - if case .generic = mode { - hasDeleteButton = hasPhotos && !fromGallery - } else if case .custom = mode { - hasDeleteButton = peer.profileImageRepresentations.first?.isPersonal == true - } else if case .fallback = mode { - if let cachedData = strongSelf.data?.cachedData as? CachedUserData, case let .known(photo) = cachedData.fallbackPhoto { - hasDeleteButton = photo != nil - } - } - - let title: String? - let confirmationTextPhoto: String? - let confirmationTextVideo: String? - let confirmationAction: String? - switch mode { - case .suggest: - title = strongSelf.presentationData.strings.UserInfo_SuggestPhotoTitle(peer.compactDisplayTitle).string - confirmationTextPhoto = strongSelf.presentationData.strings.UserInfo_SuggestPhoto_AlertPhotoText(peer.compactDisplayTitle).string - confirmationTextVideo = strongSelf.presentationData.strings.UserInfo_SuggestPhoto_AlertVideoText(peer.compactDisplayTitle).string - confirmationAction = strongSelf.presentationData.strings.UserInfo_SuggestPhoto_AlertSuggest - case .custom: - title = strongSelf.presentationData.strings.UserInfo_SetCustomPhotoTitle(peer.compactDisplayTitle).string - confirmationTextPhoto = strongSelf.presentationData.strings.UserInfo_SetCustomPhoto_AlertPhotoText(peer.compactDisplayTitle, peer.compactDisplayTitle).string - confirmationTextVideo = strongSelf.presentationData.strings.UserInfo_SetCustomPhoto_AlertVideoText(peer.compactDisplayTitle, peer.compactDisplayTitle).string - confirmationAction = strongSelf.presentationData.strings.UserInfo_SetCustomPhoto_AlertSet - default: - title = nil - confirmationTextPhoto = nil - confirmationTextVideo = nil - confirmationAction = nil - } - - let keyboardInputData = Promise() - keyboardInputData.set(AvatarEditorScreen.inputData(context: strongSelf.context, isGroup: peer.id.namespace != Namespaces.Peer.CloudUser)) - - let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme) - legacyController.statusBar.statusBarStyle = .Ignore - - let emptyController = LegacyEmptyController(context: legacyController.context)! - let navigationController = makeLegacyNavigationController(rootController: emptyController) - navigationController.setNavigationBarHidden(true, animated: false) - navigationController.navigationBar.transform = CGAffineTransform(translationX: -1000.0, y: 0.0) - - legacyController.bind(controller: navigationController) - - let parentController = (strongSelf.context.sharedContext.mainWindow?.viewController as? NavigationController)?.topViewController as? ViewController - parentController?.present(legacyController, in: .window(.root)) - - let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: hasDeleteButton, hasViewButton: false, personalPhoto: strongSelf.isSettings || strongSelf.isMyProfile, isVideo: currentIsVideo, saveEditedPhotos: false, saveCapturedMedia: false, signup: false, forum: isForum, title: title, isSuggesting: [.custom, .suggest].contains(mode))! - mixin.stickersContext = LegacyPaintStickersContext(context: strongSelf.context) - let _ = strongSelf.currentAvatarMixin.swap(mixin) - let isFromEditor = !"".isEmpty -// mixin.requestAvatarEditor = { [weak self, weak parentController] imageCompletion, videoCompletion in -// guard let strongSelf = self, let imageCompletion, let videoCompletion else { -// return -// } -// let peerType: AvatarEditorScreen.PeerType -// if mode == .suggest { -// peerType = .suggest -// } else if case .legacyGroup = peer { -// peerType = .group -// } else if case let .channel(channel) = peer { -// if case .group = channel.info { -// peerType = channel.flags.contains(.isForum) ? .forum : .group -// } else { -// peerType = .channel -// } -// } else { -// peerType = .user -// } -// let controller = AvatarEditorScreen(context: strongSelf.context, inputData: keyboardInputData.get(), peerType: peerType, markup: emojiMarkup) -// controller.imageCompletion = imageCompletion -// controller.videoCompletion = videoCompletion -// parentController?.push(controller) -// isFromEditor = true -// } - - if let confirmationTextPhoto, let confirmationAction { - mixin.willFinishWithImage = { [weak self, weak parentController] image, commit in - if let strongSelf = self, let image { - let controller = photoUpdateConfirmationController(context: strongSelf.context, peer: peer, image: image, text: confirmationTextPhoto, doneTitle: confirmationAction, commit: { - commit?() - }) - parentController?.presentInGlobalOverlay(controller) - } - } - } - if let confirmationTextVideo, let confirmationAction { - mixin.willFinishWithVideo = { [weak self, weak parentController] image, commit in - if let strongSelf = self, let image { - let controller = photoUpdateConfirmationController(context: strongSelf.context, peer: peer, image: image, text: confirmationTextVideo, doneTitle: confirmationAction, isDark: !isFromEditor, commit: { - commit?() - }) - parentController?.presentInGlobalOverlay(controller) - } - } - } - mixin.didFinishWithImage = { [weak self] image in - if let image = image { - completion(image) - self?.controller?.updateProfilePhoto(image, mode: mode, uploadStatus: nil) - } - } -// mixin.didFinishWithVideo = { [weak self] image, asset, adjustments, _ in -// if let image = image, let asset = asset { -// completion(image) -// self?.controller?.updateProfileVideo(image, asset: asset, adjustments: adjustments, mode: mode) -// } -// } - mixin.didFinishWithDelete = { - guard let strongSelf = self else { - return - } - - strongSelf.controller?.openAvatarRemoval(mode: mode, peer: peer, item: item) - } - mixin.didDismiss = { [weak legacyController] in - guard let strongSelf = self else { - return - } - let _ = strongSelf.currentAvatarMixin.swap(nil) - legacyController?.dismiss() - } - let menuController = mixin.present() - if let menuController = menuController { - menuController.customRemoveFromParentViewController = { [weak legacyController] in - legacyController?.dismiss() - } - } - }) - } - - private func openAddMember() { + func openAddMember() { guard let controller = self.controller, !controller.presentAccountFrozenInfoIfNeeded() else { return } @@ -10497,7 +4511,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro presentAddMembersImpl(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, parentController: controller, groupPeer: groupPeer, selectAddMemberDisposable: self.selectAddMemberDisposable, addMemberDisposable: self.addMemberDisposable) } - private func openQrCode() { + func openQrCode() { guard let data = self.data, let peer = data.peer, let controller = self.controller else { return } @@ -10515,7 +4529,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro controller.push(qrController) } - private func openPremiumGift() { + func openPremiumGift() { guard let controller = self.controller, !controller.presentAccountFrozenInfoIfNeeded() else { return } @@ -10636,7 +4650,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro switch status { case .available: var cameraTransitionIn: StoryCameraTransitionIn? - if let rightButton = self.headerNode.navigationButtonContainer.rightButtonNodes[.postStory] { + if let rightButton = self.headerNode.navigationButtonContainer.rightButtonNodes.first(where: { $0.key.key == .postStory })?.value { cameraTransitionIn = StoryCameraTransitionIn( sourceView: rightButton.view, sourceRect: rightButton.view.bounds, @@ -10745,287 +4759,6 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } - fileprivate func openSettings(section: PeerInfoSettingsSection) { - let push: (ViewController) -> Void = { [weak self] c in - guard let strongSelf = self, let navigationController = strongSelf.controller?.navigationController as? NavigationController else { - return - } - - if strongSelf.isMyProfile { - navigationController.pushViewController(c) - } else { - var updatedControllers = navigationController.viewControllers - for controller in navigationController.viewControllers.reversed() { - if controller !== strongSelf && !(controller is TabBarController) { - updatedControllers.removeLast() - } else { - break - } - } - updatedControllers.append(c) - - var animated = true - if let validLayout = strongSelf.validLayout?.0, case .regular = validLayout.metrics.widthClass { - animated = false - } - navigationController.setViewControllers(updatedControllers, animated: animated) - } - } - switch section { - case .avatar: - self.controller?.openAvatarForEditing() - case .edit: - self.headerNode.navigationButtonContainer.performAction?(.edit, nil, nil) - case .proxy: - self.controller?.push(proxySettingsController(context: self.context)) - case .profile: - self.controller?.push(PeerInfoScreenImpl( - context: self.context, - updatedPresentationData: self.controller?.updatedPresentationData, - peerId: self.context.account.peerId, - avatarInitiallyExpanded: false, - isOpenedFromChat: false, - nearbyPeerDistance: nil, - reactionSourceMessageId: nil, - callMessages: [], - isMyProfile: true, - profileGiftsContext: self.data?.profileGiftsContext - )) - case .stories: - push(PeerInfoStoryGridScreen(context: self.context, peerId: self.context.account.peerId, scope: .saved)) - case .savedMessages: - let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) - |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in - guard let self, let peer = peer else { - return - } - if let controller = self.controller, let navigationController = controller.navigationController as? NavigationController { - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer))) - } - }) - case .recentCalls: - push(CallListController(context: context, mode: .navigation)) - case .devices: - let _ = (self.activeSessionsContextAndCount.get() - |> take(1) - |> deliverOnMainQueue).startStandalone(next: { [weak self] activeSessionsContextAndCount in - if let strongSelf = self, let activeSessionsContextAndCount = activeSessionsContextAndCount { - let (activeSessionsContext, _, webSessionsContext) = activeSessionsContextAndCount - push(recentSessionsController(context: strongSelf.context, activeSessionsContext: activeSessionsContext, webSessionsContext: webSessionsContext, websitesOnly: false)) - } - }) - case .chatFolders: - let controller = self.context.sharedContext.makeFilterSettingsController(context: self.context, modal: false, scrollToTags: false, dismissed: nil) - push(controller) - case .notificationsAndSounds: - if let settings = self.data?.globalSettings { - push(notificationsAndSoundsController(context: self.context, exceptionsList: settings.notificationExceptions)) - } - case .privacyAndSecurity: - if let settings = self.data?.globalSettings { - let _ = (combineLatest(self.blockedPeers.get(), self.hasTwoStepAuth.get()) - |> take(1) - |> deliverOnMainQueue).startStandalone(next: { [weak self] blockedPeersContext, hasTwoStepAuth in - if let strongSelf = self { - let loginEmailPattern = strongSelf.twoStepAuthData.get() |> map { data -> String? in - return data?.loginEmailPattern - } - push(privacyAndSecurityController(context: strongSelf.context, initialSettings: settings.privacySettings, updatedSettings: { [weak self] settings in - self?.privacySettings.set(.single(settings)) - }, updatedBlockedPeers: { [weak self] blockedPeersContext in - self?.blockedPeers.set(.single(blockedPeersContext)) - }, updatedHasTwoStepAuth: { [weak self] hasTwoStepAuthValue in - self?.hasTwoStepAuth.set(.single(hasTwoStepAuthValue)) - }, focusOnItemTag: nil, activeSessionsContext: settings.activeSessionsContext, webSessionsContext: settings.webSessionsContext, blockedPeersContext: blockedPeersContext, hasTwoStepAuth: hasTwoStepAuth, loginEmailPattern: loginEmailPattern, updatedTwoStepAuthData: { [weak self] in - if let strongSelf = self { - strongSelf.twoStepAuthData.set( - strongSelf.context.engine.auth.twoStepAuthData() - |> map(Optional.init) - |> `catch` { _ -> Signal in - return .single(nil) - } - ) - } - }, requestPublicPhotoSetup: { [weak self] completion in - if let self { - self.controller?.openAvatarForEditing(mode: .fallback, completion: completion) - } - }, requestPublicPhotoRemove: { [weak self] completion in - if let self { - self.controller?.openAvatarRemoval(mode: .fallback, completion: completion) - } - })) - } - }) - } - case .passwordSetup: - DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.6, execute: { [weak self] in - guard let self else { - return - } - let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupPassword.id).startStandalone() - }) - - let controller = self.context.sharedContext.makeSetupTwoFactorAuthController(context: self.context) - push(controller) - case .dataAndStorage: - push(dataAndStorageController(context: self.context)) - case .appearance: - push(themeSettingsController(context: self.context)) - case .language: - push(LocalizationListController(context: self.context)) - case .premium: - let controller = self.context.sharedContext.makePremiumIntroController(context: self.context, source: .settings, forceDark: false, dismissed: nil) - self.controller?.push(controller) - case .premiumGift: - guard let controller = self.controller, !controller.presentAccountFrozenInfoIfNeeded() else { - return - } - let _ = (self.context.account.stateManager.contactBirthdays - |> take(1) - |> deliverOnMainQueue).start(next: { [weak self] birthdays in - guard let self else { - return - } - let giftsController = self.context.sharedContext.makePremiumGiftController(context: self.context, source: .settings(birthdays), completion: nil) - self.controller?.push(giftsController) - }) - case .stickers: - if let settings = self.data?.globalSettings { - push(installedStickerPacksController(context: self.context, mode: .general, archivedPacks: settings.archivedStickerPacks, updatedPacks: { [weak self] packs in - self?.archivedPacks.set(.single(packs)) - })) - } - case .passport: - self.controller?.push(SecureIdAuthController(context: self.context, mode: .list)) - case .watch: - push(watchSettingsController(context: self.context)) - case .support: - let supportPeer = Promise() - supportPeer.set(context.engine.peers.supportPeerId()) - - self.controller?.present(textAlertController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, title: nil, text: self.presentationData.strings.Settings_FAQ_Intro, actions: [ - TextAlertAction(type: .genericAction, title: presentationData.strings.Settings_FAQ_Button, action: { [weak self] in - self?.openFaq() - }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { [weak self] in - guard let self else { - return - } - self.supportPeerDisposable.set((supportPeer.get() |> take(1) |> deliverOnMainQueue).startStrict(next: { [weak self] peerId in - if let strongSelf = self, let peerId = peerId { - push(strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: nil, botStart: nil, mode: .standard(.default), params: nil)) - } - })) - })]), in: .window(.root)) - case .faq: - self.openFaq() - case .tips: - self.openTips() - case .phoneNumber: - guard let controller = self.controller, !controller.presentAccountFrozenInfoIfNeeded() else { - return - } - if let user = self.data?.peer as? TelegramUser, let phoneNumber = user.phone { - let introController = PrivacyIntroController(context: self.context, mode: .changePhoneNumber(phoneNumber), proceedAction: { [weak self] in - if let strongSelf = self, let navigationController = strongSelf.controller?.navigationController as? NavigationController { - navigationController.replaceTopController(ChangePhoneNumberController(context: strongSelf.context), animated: true) - } - }) - push(introController) - } - case .username: - guard let controller = self.controller, !controller.presentAccountFrozenInfoIfNeeded() else { - return - } - push(usernameSetupController(context: self.context)) - case .addAccount: - let _ = (activeAccountsAndPeers(context: context) - |> take(1) - |> deliverOnMainQueue - ).startStandalone(next: { [weak self] accountAndPeer, accountsAndPeers in - guard let strongSelf = self else { - return - } - var maximumAvailableAccounts: Int = 3 - if accountAndPeer?.1.isPremium == true && !strongSelf.context.account.testingEnvironment { - maximumAvailableAccounts = 4 - } - var count: Int = 1 - for (accountContext, peer, _) in accountsAndPeers { - if !accountContext.account.testingEnvironment { - if peer.isPremium { - maximumAvailableAccounts = 4 - } - count += 1 - } - } - - if count >= maximumAvailableAccounts { - var replaceImpl: ((ViewController) -> Void)? - let controller = PremiumLimitScreen(context: strongSelf.context, subject: .accounts, count: Int32(count), action: { - let controller = PremiumIntroScreen(context: strongSelf.context, source: .accounts) - replaceImpl?(controller) - return true - }) - replaceImpl = { [weak controller] c in - controller?.replace(with: c) - } - if let navigationController = strongSelf.context.sharedContext.mainWindow?.viewController as? NavigationController { - navigationController.pushViewController(controller) - } - } else { - strongSelf.context.sharedContext.beginNewAuth(testingEnvironment: strongSelf.context.account.testingEnvironment) - } - }) - case .logout: - if let user = self.data?.peer as? TelegramUser, let phoneNumber = user.phone { - if let controller = self.controller, let navigationController = controller.navigationController as? NavigationController { - self.controller?.push(logoutOptionsController(context: self.context, navigationController: navigationController, canAddAccounts: true, phoneNumber: phoneNumber)) - } - } - case .rememberPassword: - let context = self.context - let controller = TwoFactorDataInputScreen(sharedContext: self.context.sharedContext, engine: .authorized(self.context.engine), mode: .rememberPassword(doneText: self.presentationData.strings.TwoFactorSetup_Done_Action), stateUpdated: { _ in - }, presentation: .modalInLargeLayout) - controller.twoStepAuthSettingsController = { configuration in - return twoStepVerificationUnlockSettingsController(context: context, mode: .access(intro: false, data: .single(TwoStepVerificationUnlockSettingsControllerData.access(configuration: TwoStepVerificationAccessConfiguration(configuration: configuration, password: nil))))) - } - controller.passwordRemembered = { - let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.validatePassword.id).startStandalone() - } - push(controller) - case .emojiStatus: - self.headerNode.invokeDisplayPremiumIntro() - case .profileColor: - self.interaction.editingOpenNameColorSetup() - case .powerSaving: - push(energySavingSettingsScreen(context: self.context)) - case .businessSetup: - guard let controller = self.controller, !controller.presentAccountFrozenInfoIfNeeded() else { - return - } - push(self.context.sharedContext.makeBusinessSetupScreen(context: self.context)) - case .premiumManagement: - guard let controller = self.controller else { - return - } - let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 }) - let url = premiumConfiguration.subscriptionManagementUrl - guard !url.isEmpty else { - return - } - self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: url, forceExternal: !url.hasPrefix("tg://") && !url.contains("?start="), presentationData: self.context.sharedContext.currentPresentationData.with({$0}), navigationController: controller.navigationController as? NavigationController, dismissInput: {}) - case .stars: - if let starsContext = self.controller?.starsContext { - push(self.context.sharedContext.makeStarsTransactionsScreen(context: self.context, starsContext: starsContext)) - } - case .ton: - if let tonContext = self.controller?.tonContext { - push(self.context.sharedContext.makeStarsTransactionsScreen(context: self.context, starsContext: tonContext)) - } - } - } - fileprivate func openPaymentMethod() { self.controller?.push(AddPaymentMethodSheetScreen(context: self.context, action: { [weak self] in guard let strongSelf = self else { @@ -11040,135 +4773,6 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro })) } - private func openFaq(anchor: String? = nil) { - let presentationData = self.presentationData - let progressSignal = Signal { [weak self] subscriber in - let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil)) - self?.controller?.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() - - let _ = (self.cachedFaq.get() - |> take(1) - |> deliverOnMainQueue).start(next: { [weak self] resolvedUrl in - progressDisposable.dispose() - - if let strongSelf = self, let resolvedUrl = resolvedUrl { - var resolvedUrl = resolvedUrl - if case let .instantView(webPage, _) = resolvedUrl, let customAnchor = anchor { - resolvedUrl = .instantView(webPage, customAnchor) - } - strongSelf.context.sharedContext.openResolvedUrl(resolvedUrl, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.controller?.navigationController as? NavigationController, forceExternal: false, forceUpdate: false, openPeer: { peer, navigation in - }, sendFile: nil, sendSticker: nil, sendEmoji: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { [weak self] controller, arguments in - self?.controller?.push(controller) - }, dismissInput: {}, contentContext: nil, progress: nil, completion: nil) - } - }) - } - - private func openTips() { - let controller = OverlayStatusController(theme: self.presentationData.theme, type: .loading(cancelled: nil)) - self.controller?.present(controller, in: .window(.root)) - - let context = self.context - let navigationController = self.controller?.navigationController as? NavigationController - self.tipsPeerDisposable.set((self.context.engine.peers.resolvePeerByName(name: self.presentationData.strings.Settings_TipsUsername, referrer: nil) - |> mapToSignal { result -> Signal in - guard case let .result(result) = result else { - return .complete() - } - return .single(result) - } - |> deliverOnMainQueue).startStrict(next: { [weak controller] peer in - controller?.dismiss() - if let peer = peer, let navigationController = navigationController { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer))) - } - })) - } - - fileprivate func switchToAccount(id: AccountRecordId) { - self.accountsAndPeers.set(.never()) - self.context.sharedContext.switchToAccount(id: id, fromSettingsController: nil, withChatListController: nil) - } - - private func logoutAccount(id: AccountRecordId) { - let controller = ActionSheetController(presentationData: self.presentationData) - let dismissAction: () -> Void = { [weak controller] in - controller?.dismissAnimated() - } - - var items: [ActionSheetItem] = [] - items.append(ActionSheetTextItem(title: self.presentationData.strings.Settings_LogoutConfirmationText.trimmingCharacters(in: .whitespacesAndNewlines))) - items.append(ActionSheetButtonItem(title: self.presentationData.strings.Settings_Logout, color: .destructive, action: { [weak self] in - dismissAction() - if let strongSelf = self { - let _ = logoutFromAccount(id: id, accountManager: strongSelf.context.sharedContext.accountManager, alreadyLoggedOutRemotely: false).startStandalone() - } - })) - controller.setItemGroups([ - ActionSheetItemGroup(items: items), - ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) - ]) - self.controller?.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) - } - - private func accountContextMenuItems(context: AccountContext, logout: @escaping () -> Void) -> Signal<[ContextMenuItem], NoError> { - let strings = context.sharedContext.currentPresentationData.with({ $0 }).strings - return context.engine.messages.unreadChatListPeerIds(groupId: .root, filterPredicate: nil) - |> map { unreadChatListPeerIds -> [ContextMenuItem] in - var items: [ContextMenuItem] = [] - - if !unreadChatListPeerIds.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.engine.messages.markAllChatsAsReadInteractively(items: [(groupId: .root, filterPredicate: nil)]) - |> deliverOnMainQueue).startStandalone(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 - } - } - - private func accountContextMenu(id: AccountRecordId, node: ASDisplayNode, gesture: ContextGesture?) { - var selectedAccount: Account? - let _ = (self.accountsAndPeers.get() - |> take(1) - |> deliverOnMainQueue).startStandalone(next: { accountsAndPeers in - for (account, _, _) in accountsAndPeers { - if account.account.id == id { - selectedAccount = account.account - break - } - } - }) - if let selectedAccount = selectedAccount { - let accountContext = self.context.sharedContext.makeTempAccountContext(account: selectedAccount) - let chatListController = accountContext.sharedContext.makeChatListController(context: accountContext, location: .chatList(groupId: EngineChatList.Group(.root)), controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false) - - let contextController = ContextController(presentationData: self.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node)), items: accountContextMenuItems(context: accountContext, logout: { [weak self] in - self?.logoutAccount(id: id) - }) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) - self.controller?.presentInGlobalOverlay(contextController) - } else { - gesture?.cancel() - } - } - private func updateBio(_ bio: String) { self.state = self.state.withUpdatingBio(bio) if let (layout, navigationHeight) = self.validLayout { @@ -11176,280 +4780,13 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } - private func deleteMessages(messageIds: Set?) { - if let messageIds = messageIds ?? self.state.selectedMessageIds, !messageIds.isEmpty { - self.activeActionDisposable.set((self.context.sharedContext.chatAvailableMessageActions(engine: self.context.engine, accountPeerId: self.context.account.peerId, messageIds: messageIds, keepUpdated: false) - |> deliverOnMainQueue).startStrict(next: { [weak self] actions in - if let strongSelf = self, let peer = strongSelf.data?.peer, !actions.options.isEmpty { - let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData) - var items: [ActionSheetItem] = [] - var personalPeerName: String? - var isChannel = false - if let user = peer as? TelegramUser { - personalPeerName = EnginePeer(user).compactDisplayTitle - } else if let channel = peer as? TelegramChannel, case .broadcast = channel.info { - isChannel = true - } - - if actions.options.contains(.deleteGlobally) { - let globalTitle: String - if isChannel { - globalTitle = strongSelf.presentationData.strings.Conversation_DeleteMessagesForMe - } else if let personalPeerName = personalPeerName { - globalTitle = strongSelf.presentationData.strings.Conversation_DeleteMessagesFor(personalPeerName).string - } else { - globalTitle = strongSelf.presentationData.strings.Conversation_DeleteMessagesForEveryone - } - items.append(ActionSheetButtonItem(title: globalTitle, color: .destructive, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - if let strongSelf = self { - strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone, nil, nil) - let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forEveryone).startStandalone() - } - })) - } - if actions.options.contains(.deleteLocally) { - var localOptionText = strongSelf.presentationData.strings.Conversation_DeleteMessagesForMe - if strongSelf.context.account.peerId == strongSelf.peerId { - if messageIds.count == 1 { - localOptionText = strongSelf.presentationData.strings.Conversation_Moderate_Delete - } else { - localOptionText = strongSelf.presentationData.strings.Conversation_DeleteManyMessages - } - } - items.append(ActionSheetButtonItem(title: localOptionText, color: .destructive, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - if let strongSelf = self { - strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone, nil, nil) - let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forLocalPeer).startStandalone() - } - })) - } - actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ - ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in - actionSheet?.dismissAnimated() - }) - ])]) - strongSelf.view.endEditing(true) - strongSelf.controller?.present(actionSheet, in: .window(.root)) - } - })) - } - } - - func forwardMessages(messageIds: Set?) { - if let messageIds = messageIds ?? self.state.selectedMessageIds, !messageIds.isEmpty { - let peerSelectionController = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, filter: [.onlyWriteable, .excludeDisabled], hasFilters: true, multipleSelection: true, selectForumThreads: true)) - peerSelectionController.multiplePeersSelected = { [weak self, weak peerSelectionController] peers, peerMap, messageText, mode, forwardOptions, _ in - guard let strongSelf = self, let strongController = peerSelectionController else { - return - } - strongController.dismiss() - - var result: [EnqueueMessage] = [] - if messageText.string.count > 0 { - let inputText = convertMarkdownToAttributes(messageText) - for text in breakChatInputText(trimChatInputText(inputText)) { - if text.length != 0 { - var attributes: [MessageAttribute] = [] - let entities = generateTextEntities(text.string, enabledTypes: .all, currentEntities: generateChatInputTextEntities(text)) - if !entities.isEmpty { - attributes.append(TextEntitiesMessageAttribute(entities: entities)) - } - result.append(.message(text: text.string, attributes: attributes, inlineStickers: [:], mediaReference: nil, threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) - } - } - } - - var attributes: [MessageAttribute] = [] - attributes.append(ForwardOptionsMessageAttribute(hideNames: forwardOptions?.hideNames == true, hideCaptions: forwardOptions?.hideCaptions == true)) - - result.append(contentsOf: messageIds.map { messageId -> EnqueueMessage in - return .forward(source: messageId, threadId: nil, grouping: .auto, attributes: attributes, correlationId: nil) - }) - - var displayPeers: [EnginePeer] = [] - for peer in peers { - let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peer.id, messages: result) - |> deliverOnMainQueue).startStandalone(next: { messageIds in - if let strongSelf = self { - let signals: [Signal] = messageIds.compactMap({ id -> Signal? in - guard let id = id else { - return nil - } - return strongSelf.context.account.pendingMessageManager.pendingMessageStatus(id) - |> mapToSignal { status, _ -> Signal in - if status != nil { - return .never() - } else { - return .single(true) - } - } - |> take(1) - }) - if strongSelf.shareStatusDisposable == nil { - strongSelf.shareStatusDisposable = MetaDisposable() - } - strongSelf.shareStatusDisposable?.set((combineLatest(signals) - |> deliverOnMainQueue).startStrict()) - } - }) - - if case let .secretChat(secretPeer) = peer { - if let peer = peerMap[secretPeer.regularPeerId] { - displayPeers.append(peer) - } - } else { - displayPeers.append(peer) - } - } - - let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - let text: String - var savedMessages = false - if displayPeers.count == 1, let peerId = displayPeers.first?.id, peerId == strongSelf.context.account.peerId { - text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many - savedMessages = true - } else { - if displayPeers.count == 1, let peer = displayPeers.first { - var peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - peerName = peerName.replacingOccurrences(of: "**", with: "") - text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string : presentationData.strings.Conversation_ForwardTooltip_Chat_Many(peerName).string - } else if displayPeers.count == 2, let firstPeer = displayPeers.first, let secondPeer = displayPeers.last { - var firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - firstPeerName = firstPeerName.replacingOccurrences(of: "**", with: "") - var secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - secondPeerName = secondPeerName.replacingOccurrences(of: "**", with: "") - text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string : presentationData.strings.Conversation_ForwardTooltip_TwoChats_Many(firstPeerName, secondPeerName).string - } else if let peer = displayPeers.first { - var peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - peerName = peerName.replacingOccurrences(of: "**", with: "") - text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(displayPeers.count - 1)").string : presentationData.strings.Conversation_ForwardTooltip_ManyChats_Many(peerName, "\(displayPeers.count - 1)").string - } else { - text = "" - } - } - - strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in - if savedMessages, let self, action == .info { - let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) - |> deliverOnMainQueue).start(next: { [weak self] peer in - guard let self, let peer else { - return - } - guard let navigationController = self.controller?.navigationController as? NavigationController else { - return - } - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), forceOpenChat: true)) - }) - } - return false - }), in: .current) - } - peerSelectionController.peerSelected = { [weak self, weak peerSelectionController] peer, threadId in - let peerId = peer.id - - if let strongSelf = self, let _ = peerSelectionController { - if peerId == strongSelf.context.account.peerId { - Queue.mainQueue().after(0.88) { - strongSelf.hapticFeedback.success() - } - - let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: true, text: messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many), elevatedLayout: false, animateInAsReplacement: true, action: { action in - if let self, action == .info { - let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) - |> deliverOnMainQueue).start(next: { [weak self] peer in - guard let self, let peer else { - return - } - guard let navigationController = self.controller?.navigationController as? NavigationController else { - return - } - self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer))) - }) - } - return false - }), in: .current) - - strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone, nil, nil) - - let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: messageIds.map { id -> EnqueueMessage in - return .forward(source: id, threadId: nil, grouping: .auto, attributes: [], correlationId: nil) - }) - |> deliverOnMainQueue).startStandalone(next: { [weak self] messageIds in - if let strongSelf = self { - let signals: [Signal] = messageIds.compactMap({ id -> Signal? in - guard let id = id else { - return nil - } - return strongSelf.context.account.pendingMessageManager.pendingMessageStatus(id) - |> mapToSignal { status, _ -> Signal in - if status != nil { - return .never() - } else { - return .single(true) - } - } - |> take(1) - }) - strongSelf.activeActionDisposable.set((combineLatest(signals) - |> deliverOnMainQueue).startStrict()) - } - }) - if let peerSelectionController = peerSelectionController { - peerSelectionController.dismiss() - } - } else { - let _ = (ChatInterfaceState.update(engine: strongSelf.context.engine, peerId: peerId, threadId: threadId, { currentState in - return currentState.withUpdatedForwardMessageIds(Array(messageIds)) - }) - |> deliverOnMainQueue).startStandalone(completed: { - if let strongSelf = self { - let proceed: (ChatController) -> Void = { chatController in - strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone, nil, nil) - - if let navigationController = strongSelf.controller?.navigationController as? NavigationController { - var viewControllers = navigationController.viewControllers - if threadId != nil { - viewControllers.insert(chatController, at: viewControllers.count - 2) - } else { - viewControllers.insert(chatController, at: viewControllers.count - 1) - } - navigationController.setViewControllers(viewControllers, animated: false) - - strongSelf.activeActionDisposable.set((chatController.ready.get() - |> filter { $0 } - |> take(1) - |> deliverOnMainQueue).startStrict(next: { [weak navigationController] _ in - viewControllers.removeAll(where: { $0 is PeerSelectionController }) - navigationController?.setViewControllers(viewControllers, animated: true) - })) - } - } - - if let threadId = threadId { - let _ = (strongSelf.context.sharedContext.chatControllerForForumThread(context: strongSelf.context, peerId: peerId, threadId: threadId) - |> deliverOnMainQueue).startStandalone(next: { chatController in - proceed(chatController) - }) - } else { - let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: .none, botStart: nil, mode: .standard(.default), params: nil) - proceed(chatController) - } - } - }) - } - } - } - self.controller?.push(peerSelectionController) - } - } - - private func activateSearch() { + func activateSearch() { guard let (layout, navigationBarHeight) = self.validLayout, self.searchDisplayController == nil else { return } + guard let controller = self.controller else { + return + } if let currentPaneKey = self.paneContainerNode.currentPaneKey, case .savedMessages = currentPaneKey, let paneNode = self.paneContainerNode.currentPane?.node as? PeerInfoChatPaneNode { paneNode.activateSearch() @@ -11462,10 +4799,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro self.headerNode.navigationButtonContainer.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue) if self.isSettings { - (self.controller?.parent as? TabBarController)?.updateIsTabBarHidden(true, transition: .animated(duration: 0.3, curve: .linear)) - if let settings = self.data?.globalSettings { - self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, mode: .list, placeholder: self.presentationData.strings.Settings_Search, hasBackground: true, hasSeparator: true, contentNode: SettingsSearchContainerNode(context: self.context, openResult: { [weak self] result in + self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, mode: .navigation, placeholder: self.presentationData.strings.Settings_Search, hasBackground: true, hasSeparator: true, contentNode: SettingsSearchContainerNode(context: self.context, openResult: { [weak self] result in if let strongSelf = self, let navigationController = strongSelf.controller?.navigationController as? NavigationController { result.present(strongSelf.context, navigationController, { [weak self] mode, controller in if let strongSelf = self { @@ -11492,17 +4827,17 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } }, resolvedFaqUrl: self.cachedFaq.get(), exceptionsList: .single(settings.notificationExceptions), archivedStickerPacks: .single(settings.archivedStickerPacks), privacySettings: .single(settings.privacySettings), hasTwoStepAuth: self.hasTwoStepAuth.get(), twoStepAuthData: self.twoStepAccessConfiguration.get(), activeSessionsContext: self.activeSessionsContextAndCount.get() |> map { $0?.0 }, webSessionsContext: self.activeSessionsContextAndCount.get() |> map { $0?.2 }), cancel: { [weak self] in self?.deactivateSearch() - }) + }, searchBarIsExternal: true) } } else if let currentPaneKey = self.paneContainerNode.currentPaneKey, case .members = currentPaneKey { - self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, mode: .list, placeholder: self.presentationData.strings.Common_Search, hasBackground: true, hasSeparator: true, contentNode: ChannelMembersSearchContainerNode(context: self.context, forceTheme: nil, peerId: self.peerId, mode: .searchMembers, filters: [], searchContext: self.groupMembersSearchContext, openPeer: { [weak self] peer, participant in + self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, mode: .navigation, placeholder: self.presentationData.strings.Common_Search, hasBackground: true, hasSeparator: true, contentNode: ChannelMembersSearchContainerNode(context: self.context, forceTheme: nil, peerId: self.peerId, mode: .searchMembers, filters: [], searchContext: self.groupMembersSearchContext, openPeer: { [weak self] peer, participant in self?.openPeer(peerId: peer.id, navigation: .info(nil)) }, updateActivity: { _ in }, pushController: { [weak self] c in self?.controller?.push(c) }), cancel: { [weak self] in self?.deactivateSearch() - }) + }, fieldStyle: .glass) } else if let currentPaneKey = self.paneContainerNode.currentPaneKey, case .savedMessagesChats = currentPaneKey { let contentNode = ChatListSearchContainerNode(context: self.context, animationCache: self.context.animationCache, animationRenderer: self.context.animationRenderer, filter: [.removeSearchHeader], requestPeerType: nil, location: .savedMessagesChats(peerId: self.context.account.peerId), displaySearchFilters: false, hasDownloads: false, initialFilter: .chats, openPeer: { [weak self] peer, _, _, _ in guard let self else { @@ -11596,9 +4931,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } } - self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, mode: .list, placeholder: self.presentationData.strings.Common_Search, hasBackground: true, contentNode: ChatHistorySearchContainerNode(context: self.context, peerId: self.peerId, threadId: self.chatLocation.threadId, tagMask: tagMask, interfaceInteraction: self.chatInterfaceInteraction), cancel: { [weak self] in + self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, mode: .navigation, placeholder: self.presentationData.strings.Common_Search, hasBackground: false, contentNode: ChatHistorySearchContainerNode(context: self.context, peerId: self.peerId, threadId: self.chatLocation.threadId, tagMask: tagMask, interfaceInteraction: self.chatInterfaceInteraction), cancel: { [weak self] in self?.deactivateSearch() - }) + }, fieldStyle: .glass) } let transition: ContainedViewLayoutTransition = .animated(duration: 0.2, curve: .easeInOut) @@ -11606,37 +4941,54 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro transition.updateAlpha(node: navigationBar, alpha: 0.0) } - self.searchDisplayController?.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight + 10.0, transition: .immediate) + self.searchDisplayController?.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate) self.searchDisplayController?.activate(insertSubnode: { [weak self] subnode, isSearchBar in - if let strongSelf = self, let navigationBar = strongSelf.controller?.navigationBar { - strongSelf.insertSubnode(subnode, belowSubnode: navigationBar) + guard let self else { + return + } + if isSearchBar { + self.headerNode.searchBarContainer.addSubnode(subnode) + } else { + self.headerNode.searchContainer.addSubnode(subnode) } }, placeholder: nil) + if self.isSettings { + controller.updateTabBarSearchState(ViewController.TabBarSearchState(isActive: true), transition: transition) + if let searchBarNode = controller.currentTabBarSearchNode?() as? SearchBarNode { + self.searchDisplayController?.setSearchBar(searchBarNode) + searchBarNode.activate() + } + } + self.containerLayoutUpdated(layout: layout, navigationHeight: navigationBarHeight, transition: .immediate) } - private func deactivateSearch() { - guard let searchDisplayController = self.searchDisplayController else { + func deactivateSearch() { + guard let controller = self.controller, let searchDisplayController = self.searchDisplayController else { return } self.searchDisplayController = nil searchDisplayController.deactivate(placeholder: nil) if self.isSettings { - (self.controller?.parent as? TabBarController)?.updateIsTabBarHidden(false, transition: .animated(duration: 0.3, curve: .linear)) + (self.controller?.parent as? TabBarController)?.updateIsTabBarHidden(false, transition: .animated(duration: 0.4, curve: .spring)) + controller.updateTabBarSearchState(ViewController.TabBarSearchState(isActive: false), transition: .animated(duration: 0.4, curve: .spring)) } let transition: ContainedViewLayoutTransition = .animated(duration: 0.35, curve: .easeInOut) if let navigationBar = self.controller?.navigationBar { transition.updateAlpha(node: navigationBar, alpha: 1.0) } + if let (layout, navigationHeight) = self.validLayout { + self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.4, curve: .spring), additive: false) + } } - private weak var mediaGalleryContextMenu: ContextController? + weak var mediaGalleryContextMenu: ContextController? func displaySharedMediaFastScrollingTooltip() { - guard let buttonNode = self.headerNode.navigationButtonContainer.rightButtonNodes[.more] else { + guard let buttonNode = self.headerNode.navigationButtonContainer.rightButtonNodes.first(where: { $0.key.key == .more })?.value else { return } guard let controller = self.controller else { @@ -11647,696 +4999,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro return .dismiss(consume: false) }), in: .current) } - - private func displayGiftsContextMenu(source: ContextReferenceContentNode, gesture: ContextGesture?) { - guard let currentPaneKey = self.paneContainerNode.currentPaneKey, case .gifts = currentPaneKey else { - return - } - guard let pane = self.paneContainerNode.currentPane?.node as? PeerInfoGiftsPaneNode else { - return - } - guard let controller = self.controller else { - return - } - guard let data = self.data else { - return - } - - let giftsContext = pane.giftsContext - - var hasVisibility = false - if let channel = data.peer as? TelegramChannel, channel.hasPermission(.sendSomething) { - hasVisibility = true - } else if data.peer?.id == self.context.account.peerId { - hasVisibility = true - } - - let isCollection = giftsContext.collectionId != nil - - let strings = self.presentationData.strings - let items: Signal = giftsContext.state - |> map { state in - var hasPinnedGifts = false - for gift in state.gifts { - if gift.pinnedToTop { - hasPinnedGifts = true - break - } - } - return (state.filter, state.sorting, hasPinnedGifts || isCollection) - } - |> distinctUntilChanged(isEqual: { lhs, rhs -> Bool in - let filterEquals = lhs.0 == rhs.0 - let sortingEquals = lhs.1 == rhs.1 - let canReorderEquals = lhs.2 == rhs.2 - return filterEquals && sortingEquals && canReorderEquals - }) - |> map { [weak pane, weak giftsContext] filter, sorting, canReorder -> ContextController.Items in - var items: [ContextMenuItem] = [] - - if hasVisibility { - if let pane, case .all = pane.currentCollection { - items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_AddCollection, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Peer Info/Gifts/AddCollection"), color: theme.contextMenu.primaryColor) - }, action: { [weak pane] _, f in - f(.default) - - if let pane { - pane.createCollection() - } - }))) - } else { - items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_AddGifts, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Peer Info/Gifts/AddGift"), color: theme.contextMenu.primaryColor) - }, action: { [weak pane] _, f in - f(.default) - - if let pane, case let .collection(id) = pane.currentCollection { - pane.addGiftsToCollection(id: id) - } - }))) - } - if canReorder { - items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Reorder, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.contextMenu.primaryColor) - }, action: { [weak pane] _, f in - f(.default) - - if let pane { - pane.beginReordering() - } - }))) - } - - if let pane, case let .collection(id) = pane.currentCollection { - items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_DeleteCollection, textColor: .destructive, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) - }, action: { [weak pane] _, f in - f(.default) - - if let pane { - pane.deleteCollection(id: id) - } - }))) - } - } - - if let pane, case let .collection(id) = pane.currentCollection, let addressName = data.peer?.addressName, !addressName.isEmpty { - let shareAction: ContextMenuItem = .action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_ShareCollection, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.default) - self?.openShareLink(url: "https://t.me/\(addressName)/c/\(id)") - })) - if items.isEmpty { - items.append(shareAction) - } else { - items.insert(shareAction, at: 1) - } - } - - if !items.isEmpty { - items.append(.separator) - } - - if let pane, case .all = pane.currentCollection { - items.append(.action(ContextMenuActionItem(text: sorting == .date ? strings.PeerInfo_Gifts_SortByValue : strings.PeerInfo_Gifts_SortByDate, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: sorting == .date ? "Peer Info/SortValue" : "Peer Info/SortDate"), color: theme.contextMenu.primaryColor) - }, action: { [weak giftsContext] _, f in - f(.default) - - giftsContext?.updateSorting(sorting == .date ? .value : .date) - }))) - - items.append(.separator) - } - - let toggleFilter: (ProfileGiftsContext.Filters) -> Void = { [weak giftsContext] value in - var updatedFilter = filter - if updatedFilter.contains(value) { - updatedFilter.remove(value) - } else { - updatedFilter.insert(value) - } - if !updatedFilter.contains(.unlimited) && !updatedFilter.contains(.limitedUpgradable) && !updatedFilter.contains(.limitedNonUpgradable) && !updatedFilter.contains(.unique) { - updatedFilter.insert(.unlimited) - } - if !updatedFilter.contains(.displayed) && !updatedFilter.contains(.hidden) { - if value == .displayed { - updatedFilter.insert(.hidden) - } else { - updatedFilter.insert(.displayed) - } - } - giftsContext?.updateFilter(updatedFilter) - } - - let switchToFilter: (ProfileGiftsContext.Filters) -> Void = { [weak giftsContext] value in - var updatedFilter = filter - updatedFilter.remove(.unlimited) - updatedFilter.remove(.limitedUpgradable) - updatedFilter.remove(.limitedNonUpgradable) - updatedFilter.remove(.unique) - updatedFilter.insert(value) - giftsContext?.updateFilter(updatedFilter) - } - - let switchToVisiblityFilter: (ProfileGiftsContext.Filters) -> Void = { [weak giftsContext] value in - var updatedFilter = filter - updatedFilter.remove(.hidden) - updatedFilter.remove(.displayed) - updatedFilter.insert(value) - giftsContext?.updateFilter(updatedFilter) - } - - items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Unlimited, icon: { theme in - return filter.contains(.unlimited) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil - }, action: { _, f in - toggleFilter(.unlimited) - }, longPressAction: { _, f in - switchToFilter(.unlimited) - }))) - items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Limited, icon: { theme in - return filter.contains(.limitedNonUpgradable) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil - }, action: { _, f in - toggleFilter(.limitedNonUpgradable) - }, longPressAction: { _, f in - switchToFilter(.limitedNonUpgradable) - }))) - items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Upgradable, icon: { theme in - return filter.contains(.limitedUpgradable) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil - }, action: { _, f in - toggleFilter(.limitedUpgradable) - }, longPressAction: { _, f in - switchToFilter(.limitedUpgradable) - }))) - items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Unique, icon: { theme in - return filter.contains(.unique) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil - }, action: { _, f in - toggleFilter(.unique) - }, longPressAction: { _, f in - switchToFilter(.unique) - }))) - - if hasVisibility { - items.append(.separator) - - items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Displayed, icon: { theme in - return filter.contains(.displayed) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil - }, action: { _, f in - toggleFilter(.displayed) - }, longPressAction: { _, f in - switchToVisiblityFilter(.displayed) - }))) - items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Hidden, icon: { theme in - return filter.contains(.hidden) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil - }, action: { _, f in - toggleFilter(.hidden) - }, longPressAction: { _, f in - switchToVisiblityFilter(.hidden) - }))) - } - - return ContextController.Items(content: .list(items)) - } - - let contextController = ContextController(presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: source)), items: items, gesture: gesture) - contextController.passthroughTouchEvent = { [weak self] sourceView, point in - guard let strongSelf = self else { - return .ignore - } - - let localPoint = strongSelf.view.convert(sourceView.convert(point, to: nil), from: nil) - guard let localResult = strongSelf.hitTest(localPoint, with: nil) else { - return .dismiss(consume: true, result: nil) - } - - var testView: UIView? = localResult - while true { - if let testViewValue = testView { - if let node = testViewValue.asyncdisplaykit_node as? PeerInfoHeaderNavigationButton { - node.isUserInteractionEnabled = false - DispatchQueue.main.async { - node.isUserInteractionEnabled = true - } - return .dismiss(consume: false, result: nil) - } else { - testView = testViewValue.superview - } - } else { - break - } - } - - return .dismiss(consume: true, result: nil) - } - self.mediaGalleryContextMenu = contextController - controller.presentInGlobalOverlay(contextController) - } - - private func displayMediaGalleryContextMenu(source: ContextReferenceContentNode, gesture: ContextGesture?) { - let peerId = self.peerId - - var isBotPreviewOrStories = false - if let currentPaneKey = self.paneContainerNode.currentPaneKey { - if case .botPreview = currentPaneKey { - isBotPreviewOrStories = true - } else if case .stories = currentPaneKey { - isBotPreviewOrStories = true - } - } - - if isBotPreviewOrStories { - guard let controller = self.controller else { - return - } - guard let pane = self.paneContainerNode.currentPane?.node as? PeerInfoStoryPaneNode else { - return - } - - if case .botPreview = pane.scope { - guard let data = self.data, let user = data.peer as? TelegramUser, let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) else { - return - } - - var items: [ContextMenuItem] = [] - - let strings = self.presentationData.strings - - var ignoreNextActions = false - - if pane.canAddMoreBotPreviews() { - items.append(.action(ContextMenuActionItem(text: strings.BotPreviews_MenuAddPreview, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Add"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, a in - if ignoreNextActions { - return - } - ignoreNextActions = true - a(.default) - - if let self { - self.headerNode.navigationButtonContainer.performAction?(.postStory, nil, nil) - } - }))) - } - - if pane.canReorder() { - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.BotPreviews_MenuReorder, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.contextMenu.primaryColor) - }, action: { [weak pane] _, a in - if ignoreNextActions { - return - } - ignoreNextActions = true - a(.default) - - if let pane { - pane.beginReordering() - } - }))) - } - - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Conversation_ContextMenuSelect, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, a in - if ignoreNextActions { - return - } - ignoreNextActions = true - a(.default) - - if let self { - self.toggleStorySelection(ids: [], isSelected: true) - } - }))) - - if let language = pane.currentBotPreviewLanguage { - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.BotPreviews_MenuDeleteLanguage(language.name).string, textColor: .destructive, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) - }, action: { [weak pane] _, a in - if ignoreNextActions { - return - } - ignoreNextActions = true - a(.default) - - if let pane { - pane.presentDeleteBotPreviewLanguage() - } - }))) - } - - let contextController = ContextController(presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: source)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) - contextController.passthroughTouchEvent = { [weak self] sourceView, point in - guard let strongSelf = self else { - return .ignore - } - - let localPoint = strongSelf.view.convert(sourceView.convert(point, to: nil), from: nil) - guard let localResult = strongSelf.hitTest(localPoint, with: nil) else { - return .dismiss(consume: true, result: nil) - } - - var testView: UIView? = localResult - while true { - if let testViewValue = testView { - if let node = testViewValue.asyncdisplaykit_node as? PeerInfoHeaderNavigationButton { - node.isUserInteractionEnabled = false - DispatchQueue.main.async { - node.isUserInteractionEnabled = true - } - return .dismiss(consume: false, result: nil) - } else if let node = testViewValue.asyncdisplaykit_node as? PeerInfoVisualMediaPaneNode { - node.brieflyDisableTouchActions() - return .dismiss(consume: false, result: nil) - } else if let node = testViewValue.asyncdisplaykit_node as? PeerInfoStoryPaneNode { - node.brieflyDisableTouchActions() - return .dismiss(consume: false, result: nil) - } else { - testView = testViewValue.superview - } - } else { - break - } - } - - return .dismiss(consume: true, result: nil) - } - self.mediaGalleryContextMenu = contextController - controller.presentInGlobalOverlay(contextController) - } else if case .peer = pane.scope { - guard let data = self.data, let user = data.peer as? TelegramUser else { - return - } - let _ = user - - var items: [ContextMenuItem] = [] - - let strings = self.presentationData.strings - - var ignoreNextActions = false - - items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_MenuAddStories, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat List/AddStoryIcon"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, a in - if ignoreNextActions { - return - } - ignoreNextActions = true - a(.default) - - if let self { - self.headerNode.navigationButtonContainer.performAction?(.postStory, nil, nil) - } - }))) - - if let _ = pane.currentStoryFolder { - items.append(.action(ContextMenuActionItem(text: strings.Conversation_ContextMenuShare, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) - }, action: { [weak pane] _, a in - if ignoreNextActions { - return - } - ignoreNextActions = true - a(.default) - - if let pane { - pane.shareCurrentFolder() - } - }))) - } - - if pane.canReorder() { - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.BotPreviews_MenuReorder, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.contextMenu.primaryColor) - }, action: { [weak pane] _, a in - if ignoreNextActions { - return - } - ignoreNextActions = true - a(.default) - - if let pane { - pane.beginReordering() - } - }))) - } - - if let folder = pane.currentStoryFolder { - let _ = folder - - items.append(.action(ContextMenuActionItem(text: strings.Stories_MenuDeleteAlbum, textColor: .destructive, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) - }, action: { [weak pane] _, a in - if ignoreNextActions { - return - } - ignoreNextActions = true - a(.default) - - if let pane { - pane.presentDeleteCurrentStoryFolder() - } - }))) - } - - if let language = pane.currentBotPreviewLanguage { - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.BotPreviews_MenuDeleteLanguage(language.name).string, textColor: .destructive, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) - }, action: { [weak pane] _, a in - if ignoreNextActions { - return - } - ignoreNextActions = true - a(.default) - - if let pane { - pane.presentDeleteBotPreviewLanguage() - } - }))) - } - - let contextController = ContextController(presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: source)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) - contextController.passthroughTouchEvent = { [weak self] sourceView, point in - guard let strongSelf = self else { - return .ignore - } - - let localPoint = strongSelf.view.convert(sourceView.convert(point, to: nil), from: nil) - guard let localResult = strongSelf.hitTest(localPoint, with: nil) else { - return .dismiss(consume: true, result: nil) - } - - var testView: UIView? = localResult - while true { - if let testViewValue = testView { - if let node = testViewValue.asyncdisplaykit_node as? PeerInfoHeaderNavigationButton { - node.isUserInteractionEnabled = false - DispatchQueue.main.async { - node.isUserInteractionEnabled = true - } - return .dismiss(consume: false, result: nil) - } else if let node = testViewValue.asyncdisplaykit_node as? PeerInfoVisualMediaPaneNode { - node.brieflyDisableTouchActions() - return .dismiss(consume: false, result: nil) - } else if let node = testViewValue.asyncdisplaykit_node as? PeerInfoStoryPaneNode { - node.brieflyDisableTouchActions() - return .dismiss(consume: false, result: nil) - } else { - testView = testViewValue.superview - } - } else { - break - } - } - - return .dismiss(consume: true, result: nil) - } - self.mediaGalleryContextMenu = contextController - controller.presentInGlobalOverlay(contextController) - } - } else { - let _ = (self.context.engine.data.get(EngineDataMap([ - TelegramEngine.EngineData.Item.Messages.MessageCount(peerId: peerId, threadId: self.chatLocation.threadId, tag: .photo), - TelegramEngine.EngineData.Item.Messages.MessageCount(peerId: peerId, threadId: self.chatLocation.threadId, tag: .video) - ])) - |> deliverOnMainQueue).startStandalone(next: { [weak self] messageCounts in - guard let strongSelf = self else { - return - } - - var mediaCount: [MessageTags: Int32] = [:] - for (key, count) in messageCounts { - mediaCount[key.tag] = count.flatMap(Int32.init) ?? 0 - } - - let photoCount: Int32 = mediaCount[.photo] ?? 0 - let videoCount: Int32 = mediaCount[.video] ?? 0 - - guard let controller = strongSelf.controller else { - return - } - guard let pane = strongSelf.paneContainerNode.currentPane?.node as? PeerInfoVisualMediaPaneNode else { - return - } - - var items: [ContextMenuItem] = [] - - let strings = strongSelf.presentationData.strings - - var recurseGenerateAction: ((Bool) -> ContextMenuActionItem)? - let generateAction: (Bool) -> ContextMenuActionItem = { [weak pane] isZoomIn in - let nextZoomLevel = isZoomIn ? pane?.availableZoomLevels().increment : pane?.availableZoomLevels().decrement - let canZoom: Bool = nextZoomLevel != nil - - return ContextMenuActionItem(id: isZoomIn ? 0 : 1, text: isZoomIn ? strings.SharedMedia_ZoomIn : strings.SharedMedia_ZoomOut, textColor: canZoom ? .primary : .disabled, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: isZoomIn ? "Chat/Context Menu/ZoomIn" : "Chat/Context Menu/ZoomOut"), color: canZoom ? theme.contextMenu.primaryColor : theme.contextMenu.primaryColor.withMultipliedAlpha(0.4)) - }, action: canZoom ? { action in - guard let pane = pane, let zoomLevel = isZoomIn ? pane.availableZoomLevels().increment : pane.availableZoomLevels().decrement else { - return - } - pane.updateZoomLevel(level: zoomLevel) - if let recurseGenerateAction = recurseGenerateAction { - action.updateAction(0, recurseGenerateAction(true)) - action.updateAction(1, recurseGenerateAction(false)) - } - } : nil) - } - recurseGenerateAction = { isZoomIn in - return generateAction(isZoomIn) - } - - items.append(.action(generateAction(true))) - items.append(.action(generateAction(false))) - - var ignoreNextActions = false - if strongSelf.chatLocation.threadId == nil { - items.append(.action(ContextMenuActionItem(text: strings.SharedMedia_ShowCalendar, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Calendar"), color: theme.contextMenu.primaryColor) - }, action: { _, a in - if ignoreNextActions { - return - } - ignoreNextActions = true - a(.default) - - self?.openMediaCalendar() - }))) - } - - if photoCount != 0 && videoCount != 0 { - items.append(.separator) - - let showPhotos: Bool - switch pane.contentType { - case .photo, .photoOrVideo: - showPhotos = true - default: - showPhotos = false - } - let showVideos: Bool - switch pane.contentType { - case .video, .photoOrVideo: - showVideos = true - default: - showVideos = false - } - - items.append(.action(ContextMenuActionItem(text: strings.SharedMedia_ShowPhotos, icon: { theme in - if !showPhotos { - return nil - } - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) - }, action: { [weak pane] _, a in - a(.default) - - guard let pane = pane else { - return - } - let updatedContentType: PeerInfoVisualMediaPaneNode.ContentType - switch pane.contentType { - case .photoOrVideo: - updatedContentType = .video - case .photo: - updatedContentType = .photo - case .video: - updatedContentType = .photoOrVideo - default: - updatedContentType = pane.contentType - } - pane.updateContentType(contentType: updatedContentType) - }))) - items.append(.action(ContextMenuActionItem(text: strings.SharedMedia_ShowVideos, icon: { theme in - if !showVideos { - return nil - } - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) - }, action: { [weak pane] _, a in - a(.default) - - guard let pane = pane else { - return - } - let updatedContentType: PeerInfoVisualMediaPaneNode.ContentType - switch pane.contentType { - case .photoOrVideo: - updatedContentType = .photo - case .photo: - updatedContentType = .photoOrVideo - case .video: - updatedContentType = .video - default: - updatedContentType = pane.contentType - } - pane.updateContentType(contentType: updatedContentType) - }))) - } - - let contextController = ContextController(presentationData: strongSelf.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: source)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) - contextController.passthroughTouchEvent = { sourceView, point in - guard let strongSelf = self else { - return .ignore - } - - let localPoint = strongSelf.view.convert(sourceView.convert(point, to: nil), from: nil) - guard let localResult = strongSelf.hitTest(localPoint, with: nil) else { - return .dismiss(consume: true, result: nil) - } - - var testView: UIView? = localResult - while true { - if let testViewValue = testView { - if let node = testViewValue.asyncdisplaykit_node as? PeerInfoHeaderNavigationButton { - node.isUserInteractionEnabled = false - DispatchQueue.main.async { - node.isUserInteractionEnabled = true - } - return .dismiss(consume: false, result: nil) - } else if let node = testViewValue.asyncdisplaykit_node as? PeerInfoVisualMediaPaneNode { - node.brieflyDisableTouchActions() - return .dismiss(consume: false, result: nil) - } else if let node = testViewValue.asyncdisplaykit_node as? PeerInfoStoryPaneNode { - node.brieflyDisableTouchActions() - return .dismiss(consume: false, result: nil) - } else { - testView = testViewValue.superview - } - } else { - break - } - } - - return .dismiss(consume: true, result: nil) - } - strongSelf.mediaGalleryContextMenu = contextController - controller.presentInGlobalOverlay(contextController) - }) - } - } - - private func openMediaCalendar() { + func openMediaCalendar() { var initialTimestamp = Int32(Date().timeIntervalSince1970) guard let pane = self.paneContainerNode.currentPane?.node as? PeerInfoVisualMediaPaneNode, let timestamp = pane.currentTopTimestamp(), let calendarSource = pane.calendarSource else { @@ -12514,7 +5178,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro func updatePresentationData(_ presentationData: PresentationData) { self.presentationData = presentationData - self.backgroundColor = self.presentationData.theme.list.blocksBackgroundColor + self.updateBackgroundColor() self.updateNavigationExpansionPresentation(isExpanded: self.headerNode.isAvatarExpanded, animated: false) @@ -12586,8 +5250,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } let headerInset = sectionInset - let headerHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : self.scrollNode.view.contentOffset.y, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.savedMessagesPeer ?? self.data?.peer, cachedData: self.data?.cachedData, threadData: self.data?.threadData, peerNotificationSettings: self.data?.peerNotificationSettings, threadNotificationSettings: self.data?.threadNotificationSettings, globalNotificationSettings: self.data?.globalNotificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, profileGiftsContext: self.data?.profileGiftsContext, screenData: self.data, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, transition: self.headerNode.navigationTransition == nil ? transition : .immediate, additive: additive, animateHeader: transition.isAnimated && self.headerNode.navigationTransition == nil) - let headerFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: layout.size.width, height: headerHeight)) + let headerHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : self.scrollNode.view.contentOffset.y, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.savedMessagesPeer ?? self.data?.peer, cachedData: self.data?.cachedData, threadData: self.data?.threadData, peerNotificationSettings: self.data?.peerNotificationSettings, threadNotificationSettings: self.data?.threadNotificationSettings, globalNotificationSettings: self.data?.globalNotificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, profileGiftsContext: self.data?.profileGiftsContext, screenData: self.data, isSearching: self.searchDisplayController != nil, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, transition: self.headerNode.navigationTransition == nil ? transition : .immediate, additive: additive, animateHeader: transition.isAnimated && self.headerNode.navigationTransition == nil) + + let headerFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: layout.size.width, height: layout.size.height)) if additive { transition.updateFrameAdditive(node: self.headerNode, frame: headerFrame) } else { @@ -12623,7 +5288,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } else { sectionNode = PeerInfoScreenItemSectionContainerNode() self.regularSections[sectionId] = sectionNode - self.scrollNode.addSubnode(sectionNode) + self.scrollNode.insertSubnode(sectionNode, belowSubnode: self.paneContainerNode) wasAdded = true } @@ -12695,7 +5360,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro wasAdded = true sectionNode = PeerInfoScreenItemSectionContainerNode() self.editingSections[sectionId] = sectionNode - self.scrollNode.addSubnode(sectionNode) + self.scrollNode.insertSubnode(sectionNode, belowSubnode: self.paneContainerNode) } let sectionWidth = layout.size.width - insets.left - insets.right @@ -12737,12 +5402,17 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro restoreContentOffset = self.scrollNode.view.contentOffset } + if !self.isMediaOnly { + contentHeight -= 18.0 + } let paneContainerFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: paneContainerSize) if self.state.isEditing || (self.data?.availablePanes ?? []).isEmpty { transition.updateAlpha(node: self.paneContainerNode, alpha: 0.0) + ComponentTransition(transition).setAlpha(view: self.paneContainerNode.headerContainer, alpha: 0.0) } else { contentHeight += layout.size.height - navigationHeight transition.updateAlpha(node: self.paneContainerNode, alpha: 1.0) + ComponentTransition(transition).setAlpha(view: self.paneContainerNode.headerContainer, alpha: 1.0) } if let selectedMessageIds = self.state.selectedMessageIds { @@ -12909,6 +5579,11 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } else { transition.updateFrame(node: self.paneContainerNode, frame: paneContainerFrame) } + if additive { + transition.updateFrameAdditive(view: self.paneContainerNode.headerContainer, frame: paneContainerFrame) + } else { + transition.updateFrame(view: self.paneContainerNode.headerContainer, frame: paneContainerFrame) + } self.ignoreScrolling = false self.updateNavigation(transition: transition, additive: additive, animateHeader: self.controller?.didAppear ?? false) @@ -12929,6 +5604,16 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro |> take(1)) } } + + private func updateBackgroundColor() { + let color: UIColor + if self.paneContainerNode.currentPaneKey == .gifts { + color = self.presentationData.theme.list.blocksBackgroundColor + } else { + color = self.presentationData.theme.list.blocksBackgroundColor.mixedWith(self.presentationData.theme.list.plainBackgroundColor, alpha: self.effectiveAreaExpansionFraction) + } + self.backgroundColor = color + } private var hasQrButton = false fileprivate func updateNavigation(transition: ContainedViewLayoutTransition, additive: Bool, animateHeader: Bool) { @@ -12971,7 +5656,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } let headerInset = sectionInset - let _ = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : offsetY, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.savedMessagesPeer ?? self.data?.peer, cachedData: self.data?.cachedData, threadData: self.data?.threadData, peerNotificationSettings: self.data?.peerNotificationSettings, threadNotificationSettings: self.data?.threadNotificationSettings, globalNotificationSettings: self.data?.globalNotificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, profileGiftsContext: self.data?.profileGiftsContext, screenData: self.data, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, transition: self.headerNode.navigationTransition == nil ? transition : .immediate, additive: additive, animateHeader: animateHeader && self.headerNode.navigationTransition == nil) + let _ = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : offsetY, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.savedMessagesPeer ?? self.data?.peer, cachedData: self.data?.cachedData, threadData: self.data?.threadData, peerNotificationSettings: self.data?.peerNotificationSettings, threadNotificationSettings: self.data?.threadNotificationSettings, globalNotificationSettings: self.data?.globalNotificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, profileGiftsContext: self.data?.profileGiftsContext, screenData: self.data, isSearching: self.searchDisplayController != nil, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, transition: self.headerNode.navigationTransition == nil ? transition : .immediate, additive: additive, animateHeader: animateHeader && self.headerNode.navigationTransition == nil) } let paneAreaExpansionDistance: CGFloat = 32.0 @@ -12979,15 +5664,17 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if self.state.isEditing { effectiveAreaExpansionFraction = 0.0 } else if self.isSettings { - var paneAreaExpansionDelta = (self.headerNode.frame.maxY - navigationHeight) - self.scrollNode.view.contentOffset.y - paneAreaExpansionDelta = max(0.0, min(paneAreaExpansionDelta, paneAreaExpansionDistance)) - effectiveAreaExpansionFraction = 1.0 - paneAreaExpansionDelta / paneAreaExpansionDistance + effectiveAreaExpansionFraction = 0.0 } else { var paneAreaExpansionDelta = (self.paneContainerNode.frame.minY - navigationHeight) - self.scrollNode.view.contentOffset.y paneAreaExpansionDelta = max(0.0, min(paneAreaExpansionDelta, paneAreaExpansionDistance)) effectiveAreaExpansionFraction = 1.0 - paneAreaExpansionDelta / paneAreaExpansionDistance } + self.effectiveAreaExpansionFraction = effectiveAreaExpansionFraction + + self.updateBackgroundColor() + let visibleHeight = self.scrollNode.view.contentOffset.y + self.scrollNode.view.bounds.height - self.paneContainerNode.frame.minY var bottomInset = layout.intrinsicInsets.bottom @@ -13000,11 +5687,15 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro disableTabSwitching = true } - let navigationBarHeight: CGFloat = !self.isSettings && layout.isModalOverlay ? 56.0 : 44.0 - self.paneContainerNode.update(size: self.paneContainerNode.bounds.size, sideInset: layout.safeInsets.left, bottomInset: bottomInset, deviceMetrics: layout.deviceMetrics, visibleHeight: visibleHeight, expansionFraction: effectiveAreaExpansionFraction, presentationData: self.presentationData, data: self.data, areTabsHidden: self.headerNode.customNavigationContentNode != nil, disableTabSwitching: disableTabSwitching, navigationHeight: navigationHeight, transition: transition) + let navigationBarHeight: CGFloat = !self.isSettings && layout.isModalOverlay ? 68.0 : 60.0 + let paneContainerTopInset = navigationBarHeight + (layout.statusBarHeight ?? 0.0) + self.paneContainerNode.update(size: self.paneContainerNode.bounds.size, sideInset: layout.safeInsets.left, topInset: paneContainerTopInset, bottomInset: bottomInset, deviceMetrics: layout.deviceMetrics, visibleHeight: visibleHeight, expansionFraction: effectiveAreaExpansionFraction, presentationData: self.presentationData, data: self.data, areTabsHidden: self.headerNode.customNavigationContentNode != nil, disableTabSwitching: disableTabSwitching, navigationHeight: navigationHeight, transition: transition) transition.updateFrame(node: self.headerNode.navigationButtonContainer, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left, y: layout.statusBarHeight ?? 0.0), size: CGSize(width: layout.size.width - layout.safeInsets.left * 2.0, height: navigationBarHeight))) - + var searchBarContainerY: CGFloat = layout.statusBarHeight ?? 0.0 + searchBarContainerY += floor((navigationBarHeight - 44.0) * 0.5) + 2.0 + transition.updateFrame(node: self.headerNode.searchBarContainer, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left, y: searchBarContainerY), size: CGSize(width: layout.size.width - layout.safeInsets.left * 2.0, height: navigationBarHeight))) + transition.updateFrame(node: self.headerNode.searchContainer, frame: CGRect(origin: CGPoint(), size: layout.size)) var leftNavigationButtons: [PeerInfoHeaderNavigationButtonSpec] = [] var rightNavigationButtons: [PeerInfoHeaderNavigationButtonSpec] = [] @@ -13015,7 +5706,6 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro if self.isSettings { leftNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .qrCode, isForExpandedView: false)) rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .edit, isForExpandedView: false)) - rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .search, isForExpandedView: true)) } else if self.isMyProfile { rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .edit, isForExpandedView: false)) } else if peerInfoCanEdit(peer: self.data?.peer, chatLocation: self.chatLocation, threadData: self.data?.threadData, cachedData: self.data?.cachedData, isContact: self.data?.isContact) { @@ -13078,6 +5768,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro switch previousItem { case .close, .item: leftNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .back, isForExpandedView: false)) + leftNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .back, isForExpandedView: true)) } } self.headerNode.navigationButtonContainer.update(size: CGSize(width: layout.size.width - layout.safeInsets.left * 2.0, height: navigationBarHeight), presentationData: self.presentationData, leftButtons: leftNavigationButtons, rightButtons: rightNavigationButtons, expandFraction: effectiveAreaExpansionFraction, shouldAnimateIn: animateHeader, transition: transition) @@ -13085,7 +5776,21 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro } func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + func cancelContextGestures(view: UIView) { + if let gestureRecognizers = view.gestureRecognizers { + for gesture in gestureRecognizers { + if let gesture = gesture as? ContextGesture { + gesture.cancel() + } + } + } + for subview in view.subviews { + cancelContextGestures(view: subview) + } + } + cancelContextGestures(view: scrollView) + self.canAddVelocity = true self.canOpenAvatarByDragging = self.headerNode.isAvatarExpanded self.paneContainerNode.currentPane?.node.cancelPreviewGestures() @@ -13101,6 +5806,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro guard !self.ignoreScrolling else { return } + + self.headerNode.headerEdgeEffectContainer.center = CGPoint(x: 0.0, y: scrollView.contentOffset.y) if !self.state.isEditing { if self.canAddVelocity { @@ -13454,7 +6161,7 @@ public enum PeerInfoSwitchToGiftsTarget { public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortcutResponder { let context: AccountContext - fileprivate let updatedPresentationData: (initial: PresentationData, signal: Signal)? + let updatedPresentationData: (initial: PresentationData, signal: Signal)? public let peerId: PeerId private let avatarInitiallyExpanded: Bool private let isOpenedFromChat: Bool @@ -13466,8 +6173,8 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc private let hintGroupInCommon: PeerId? private weak var requestsContext: PeerInvitationImportersContext? private weak var profileGiftsContext: ProfileGiftsContext? - fileprivate let starsContext: StarsContext? - fileprivate let tonContext: StarsContext? + let starsContext: StarsContext? + let tonContext: StarsContext? private let switchToRecommendedChannels: Bool private let switchToGiftsTarget: PeerInfoSwitchToGiftsTarget? private let switchToGroupsInCommon: Bool @@ -13490,6 +6197,8 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc private var tabBarItemDisposable: Disposable? + var avatarPickerHolder: Any? + var controllerNode: PeerInfoScreenNode { return self.displayNode as! PeerInfoScreenNode } @@ -13604,6 +6313,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc let baseNavigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData) super.init(navigationBarPresentationData: NavigationBarPresentationData( theme: NavigationBarTheme( + overallDarkAppearance: true, buttonColor: .white, disabledButtonColor: .white, primaryTextColor: .white, @@ -13615,6 +6325,8 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc badgeTextColor: baseNavigationBarPresentationData.theme.badgeTextColor ), strings: baseNavigationBarPresentationData.strings)) + self._hasGlassStyle = true + self.navigationBar?.enableAutomaticBackButton = false if isSettings || isMyProfile { @@ -13786,46 +6498,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc } if self.chatLocation.peerId != nil { - /*self.navigationBar?.shouldTransitionInline = { [weak self] in - guard let strongSelf = self else { - return false - } - if strongSelf.navigationItem.leftBarButtonItem != nil { - return false - } - if strongSelf.controllerNode.scrollNode.view.contentOffset.y > .ulpOfOne { - return false - } - if strongSelf.controllerNode.headerNode.isAvatarExpanded { - return false - } - return false - }*/ - self.navigationBar?.makeCustomTransitionNode = { [weak self] other, isInteractive in - guard let strongSelf = self else { - return nil - } - if strongSelf.navigationItem.leftBarButtonItem != nil { - return nil - } - if other.item?.leftBarButtonItem != nil { - return nil - } - if strongSelf.controllerNode.scrollNode.view.contentOffset.y > .ulpOfOne { - return nil - } - if strongSelf.controllerNode.initialExpandPanes { - return nil - } - if isInteractive && strongSelf.controllerNode.headerNode.isAvatarExpanded { - return nil - } - if let allowsCustomTransition = other.allowsCustomTransition, !allowsCustomTransition() { - return nil - } - if let tag = other.userInfo as? PeerInfoNavigationSourceTag, (tag.peerId == peerId || tag.threadId == peerId.toInt64()) { - return PeerInfoNavigationTransitionNode(screenNode: strongSelf.controllerNode, presentationData: strongSelf.presentationData, headerNode: strongSelf.controllerNode.headerNode) - } + self.navigationBar?.makeCustomTransitionNode = { _, _ in return nil } } @@ -13923,6 +6596,8 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc } self._readyProxy.set(.single(true)) }) + + self.updateTabBarSearchState(ViewController.TabBarSearchState(isActive: false), transition: .immediate) } required init(coder aDecoder: NSCoder) { @@ -14171,9 +6846,9 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc chatNavigationStack = summary.peerNavigationItems.filter({ $0 != ChatNavigationStackItem(peerId: self.peerId, threadId: self.chatLocation.threadId) }) } - if !chatNavigationStack.isEmpty { - self.navigationBar?.backButtonNode.isGestureEnabled = true - self.navigationBar?.backButtonNode.activated = { [weak self] gesture, _ in + if !chatNavigationStack.isEmpty, let backButtonNode = self.navigationBar?.backButtonNode as? ContextControllerSourceNode { + backButtonNode.isGestureEnabled = true + backButtonNode.activated = { [weak self] gesture, _ in guard let strongSelf = self, let backButtonNode = strongSelf.navigationBar?.backButtonNode, let navigationController = strongSelf.navigationController as? NavigationController else { gesture.cancel() return @@ -14294,6 +6969,14 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc self.controllerNode.headerNode.navigationButtonContainer.performAction?(.edit, nil, nil) } + override public func tabBarActivateSearch() { + self.controllerNode.activateSearch() + } + + override public func tabBarDeactivateSearch() { + self.controllerNode.deactivateSearch() + } + public static func openSavedMessagesMoreMenu(context: AccountContext, sourceController: ViewController, isViewingAsTopics: Bool, sourceView: UIView, gesture: ContextGesture?) { let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)) |> deliverOnMainQueue).startStandalone(next: { peer in @@ -14383,7 +7066,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc } } -private final class SettingsTabBarContextReferenceContentSource: ContextReferenceContentSource { +final class SettingsTabBarContextReferenceContentSource: ContextReferenceContentSource { let keepInPlace: Bool = true private let controller: ViewController @@ -14403,7 +7086,7 @@ private final class SettingsTabBarContextReferenceContentSource: ContextReferenc } } -private func getUserPeer(engine: TelegramEngine, peerId: EnginePeer.Id) -> Signal { +func getUserPeer(engine: TelegramEngine, peerId: EnginePeer.Id) -> Signal { return engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> mapToSignal { peer -> Signal in guard let peer = peer else { @@ -14417,349 +7100,7 @@ private func getUserPeer(engine: TelegramEngine, peerId: EnginePeer.Id) -> Signa } } -private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavigationTransitionNode { - private let screenNode: PeerInfoScreenNode - private let presentationData: PresentationData - - private var topNavigationBar: NavigationBar? - private var bottomNavigationBar: NavigationBar? - private var reverseFraction: Bool = false - - private let headerNode: PeerInfoHeaderNode - - private var previousBackButtonArrow: UIView? - private var previousBackButton: UIView? - private var currentBackButtonArrow: ASDisplayNode? - private var previousBackButtonBadge: ASDisplayNode? - private var currentBackButton: ASDisplayNode? - - private var previousRightButton: CALayer? - - private var previousContentNode: ASDisplayNode? - private var previousContentNodeFrame: CGRect? - private var previousContentNodeAlpha: CGFloat = 1.0 - - private var previousSecondaryContentNode: ASDisplayNode? - private var previousSecondaryContentNodeFrame: CGRect? - private var previousSecondaryContentNodeAlpha: CGFloat = 1.0 - - private var previousTitleNode: (ASDisplayNode, PortalView)? - private var previousStatusNode: (ASDisplayNode, ASDisplayNode)? - - private var previousAvatarView: UIView? - - private var didSetup: Bool = false - - init(screenNode: PeerInfoScreenNode, presentationData: PresentationData, headerNode: PeerInfoHeaderNode) { - self.screenNode = screenNode - self.presentationData = presentationData - self.headerNode = headerNode - - super.init() - - self.addSubnode(headerNode) - } - - func setup(topNavigationBar: NavigationBar, bottomNavigationBar: NavigationBar) { - if let _ = bottomNavigationBar.userInfo as? PeerInfoNavigationSourceTag { - self.topNavigationBar = topNavigationBar - self.bottomNavigationBar = bottomNavigationBar - } else { - self.topNavigationBar = bottomNavigationBar - self.bottomNavigationBar = topNavigationBar - self.reverseFraction = true - } - - topNavigationBar.isHidden = true - bottomNavigationBar.isHidden = true - - if let topNavigationBar = self.topNavigationBar, let bottomNavigationBar = self.bottomNavigationBar { - self.addSubnode(bottomNavigationBar.additionalContentNode) - - if let headerView = bottomNavigationBar.customHeaderContentView as? ChatListHeaderComponent.View { - if let previousBackButtonArrow = headerView.makeTransitionBackArrowView(accentColor: self.presentationData.theme.rootController.navigationBar.accentTextColor) { - self.previousBackButtonArrow = previousBackButtonArrow - self.view.addSubview(previousBackButtonArrow) - } - if let previousBackButton = headerView.makeTransitionBackButtonView(accentColor: self.presentationData.theme.rootController.navigationBar.accentTextColor) { - self.previousBackButton = previousBackButton - self.view.addSubview(previousBackButton) - } - } else { - if let previousBackButtonArrow = bottomNavigationBar.makeTransitionBackArrowView(accentColor: self.presentationData.theme.rootController.navigationBar.accentTextColor) { - self.previousBackButtonArrow = previousBackButtonArrow - self.view.addSubview(previousBackButtonArrow) - } - if let previousBackButton = bottomNavigationBar.makeTransitionBackButtonView(accentColor: self.presentationData.theme.rootController.navigationBar.accentTextColor) { - self.previousBackButton = previousBackButton - self.view.addSubview(previousBackButton) - } - } - - if let currentBackButtonArrow = topNavigationBar.makeTransitionBackArrowNode(accentColor: .white) { - self.currentBackButtonArrow = currentBackButtonArrow - //self.addSubnode(currentBackButtonArrow) - } - - if let headerView = bottomNavigationBar.customHeaderContentView as? ChatListHeaderComponent.View { - let _ = headerView - } else { - if let previousBackButtonBadge = bottomNavigationBar.makeTransitionBadgeNode() { - self.previousBackButtonBadge = previousBackButtonBadge - self.addSubnode(previousBackButtonBadge) - } - } - - if let currentBackButton = topNavigationBar.makeTransitionBackButtonNode(accentColor: .white) { - self.currentBackButton = currentBackButton - //self.addSubnode(currentBackButton) - } - - if let headerView = bottomNavigationBar.customHeaderContentView as? ChatListHeaderComponent.View { - if let previousRightButton = headerView.rightButtonView?.layer.snapshotContentTree() { - self.previousRightButton = previousRightButton - self.view.layer.addSublayer(previousRightButton) - } - } else { - if let avatarNavigationNode = bottomNavigationBar.rightButtonNode.singleCustomNode as? ChatAvatarNavigationNode, let previousAvatarView = avatarNavigationNode.view.snapshotContentTree() { - self.previousAvatarView = previousAvatarView - self.view.addSubview(previousAvatarView) - } else if let previousRightButton = bottomNavigationBar.rightButtonNode.view.layer.snapshotContentTree() { - self.previousRightButton = previousRightButton - self.view.layer.addSublayer(previousRightButton) - } - } - - if let contentNode = bottomNavigationBar.contentNode { - self.previousContentNode = contentNode - self.previousContentNodeFrame = contentNode.view.convert(contentNode.view.bounds, to: bottomNavigationBar.view) - self.previousContentNodeAlpha = contentNode.alpha - self.addSubnode(contentNode) - } - - if let secondaryContentNode = bottomNavigationBar.secondaryContentNode { - self.previousSecondaryContentNode = secondaryContentNode - self.previousSecondaryContentNodeFrame = secondaryContentNode.view.convert(secondaryContentNode.view.bounds, to: bottomNavigationBar.view) - self.previousSecondaryContentNodeAlpha = secondaryContentNode.alpha - self.addSubnode(secondaryContentNode) - } - - var previousTitleView: UIView? - if let headerView = bottomNavigationBar.customHeaderContentView as? ChatListHeaderComponent.View { - if let componentView = headerView.titleContentView as? ChatTitleComponent.View { - previousTitleView = componentView.contentView - } - } else { - previousTitleView = bottomNavigationBar.titleView - } - - if let previousTitleView = previousTitleView as? ChatTitleView, let previousTitleNode = PortalView(matchPosition: false) { - previousTitleNode.view.frame = previousTitleView.titleContainerView.frame - previousTitleView.titleContainerView.addPortal(view: previousTitleNode) - let previousTitleContainerNode = ASDisplayNode() - previousTitleContainerNode.view.addSubview(previousTitleNode.view) - previousTitleNode.view.frame = previousTitleNode.view.frame.offsetBy(dx: -previousTitleNode.view.frame.width / 2.0, dy: -previousTitleNode.view.frame.height / 2.0) - self.previousTitleNode = (previousTitleContainerNode, previousTitleNode) - self.addSubnode(previousTitleContainerNode) - - let previousStatusNode = previousTitleView.activityNode.makeCopy() - let previousStatusContainerNode = ASDisplayNode() - previousStatusContainerNode.addSubnode(previousStatusNode) - previousStatusNode.frame = previousStatusNode.frame.offsetBy(dx: -previousStatusNode.frame.width / 2.0, dy: -previousStatusNode.frame.height / 2.0) - self.previousStatusNode = (previousStatusContainerNode, previousStatusNode) - self.addSubnode(previousStatusContainerNode) - } - } - } - - func update(containerSize: CGSize, fraction: CGFloat, transition: ContainedViewLayoutTransition) { - guard let topNavigationBar = self.topNavigationBar, let bottomNavigationBar = self.bottomNavigationBar else { - return - } - - let fraction = self.reverseFraction ? (1.0 - fraction) : fraction - - if let previousBackButtonArrow = self.previousBackButtonArrow { - if let headerView = bottomNavigationBar.customHeaderContentView as? ChatListHeaderComponent.View { - if let backArrowView = headerView.backArrowView { - let previousBackButtonArrowFrame = backArrowView.convert(backArrowView.bounds, to: bottomNavigationBar.view) - previousBackButtonArrow.frame = previousBackButtonArrowFrame - transition.updateAlpha(layer: previousBackButtonArrow.layer, alpha: fraction) - } - } else { - let previousBackButtonArrowFrame = bottomNavigationBar.backButtonArrow.view.convert(bottomNavigationBar.backButtonArrow.view.bounds, to: bottomNavigationBar.view) - previousBackButtonArrow.frame = previousBackButtonArrowFrame - transition.updateAlpha(layer: previousBackButtonArrow.layer, alpha: fraction) - } - } - - if let previousBackButton = self.previousBackButton { - if let headerView = bottomNavigationBar.customHeaderContentView as? ChatListHeaderComponent.View { - if let backButtonTitleView = headerView.backButtonTitleView { - let previousBackButtonFrame = backButtonTitleView.convert(backButtonTitleView.bounds, to: bottomNavigationBar.view) - previousBackButton.frame = previousBackButtonFrame - transition.updateAlpha(layer: previousBackButton.layer, alpha: fraction) - } - } else { - let previousBackButtonFrame = bottomNavigationBar.backButtonNode.view.convert(bottomNavigationBar.backButtonNode.view.bounds, to: bottomNavigationBar.view) - previousBackButton.frame = previousBackButtonFrame - transition.updateAlpha(layer: previousBackButton.layer, alpha: fraction) - } - } - - if let previousRightButton = self.previousRightButton { - if let headerView = bottomNavigationBar.customHeaderContentView as? ChatListHeaderComponent.View { - if let rightButtonView = headerView.rightButtonView { - let previousRightButtonFrame = rightButtonView.convert(rightButtonView.bounds, to: bottomNavigationBar.view) - previousRightButton.frame = previousRightButtonFrame - transition.updateAlpha(layer: previousRightButton, alpha: fraction) - } - } else { - let previousRightButtonFrame = bottomNavigationBar.rightButtonNode.view.convert(bottomNavigationBar.rightButtonNode.view.bounds, to: bottomNavigationBar.view) - previousRightButton.frame = previousRightButtonFrame - transition.updateAlpha(layer: previousRightButton, alpha: fraction) - } - } - - if let currentBackButtonArrow = self.currentBackButtonArrow { - let currentBackButtonArrowFrame = topNavigationBar.backButtonArrow.view.convert(topNavigationBar.backButtonArrow.view.bounds, to: topNavigationBar.view) - currentBackButtonArrow.frame = currentBackButtonArrowFrame - - transition.updateAlpha(node: currentBackButtonArrow, alpha: 1.0 - fraction) - if let previousBackButtonArrow = self.previousBackButtonArrow { - transition.updateAlpha(layer: previousBackButtonArrow.layer, alpha: fraction) - } - } - - if let previousBackButtonBadge = self.previousBackButtonBadge { - let previousBackButtonBadgeFrame = bottomNavigationBar.badgeNode.view.convert(bottomNavigationBar.badgeNode.view.bounds, to: bottomNavigationBar.view) - previousBackButtonBadge.frame = previousBackButtonBadgeFrame - - transition.updateAlpha(node: previousBackButtonBadge, alpha: fraction) - } - - if let currentBackButton = self.currentBackButton { - transition.updateAlpha(node: currentBackButton, alpha: (1.0 - fraction)) - } - - var previousTitleView: UIView? - if let headerView = bottomNavigationBar.customHeaderContentView as? ChatListHeaderComponent.View { - if let componentView = headerView.titleContentView as? ChatTitleComponent.View { - previousTitleView = componentView.contentView - } - } else { - previousTitleView = bottomNavigationBar.titleView - } - - if let previousTitleView = previousTitleView as? ChatTitleView, let (previousTitleContainerNode, previousTitleNode) = self.previousTitleNode, let (previousStatusContainerNode, previousStatusNode) = self.previousStatusNode { - let previousTitleFrame = previousTitleView.titleContainerView.convert(previousTitleView.titleContainerView.bounds, to: bottomNavigationBar.view) - let previousStatusFrame = previousTitleView.activityNode.view.convert(previousTitleView.activityNode.bounds, to: bottomNavigationBar.view) - - self.headerNode.navigationTransition = PeerInfoHeaderNavigationTransition(sourceNavigationBar: bottomNavigationBar, sourceTitleView: previousTitleView, sourceTitleFrame: previousTitleFrame, sourceSubtitleFrame: previousStatusFrame, previousAvatarView: self.previousAvatarView, fraction: fraction) - var topHeight = topNavigationBar.backgroundNode.bounds.height - - if let iconView = previousTitleView.titleCredibilityIconView.componentView { - transition.updateFrame(view: iconView, frame: iconView.bounds.offsetBy(dx: (1.0 - fraction) * 8.0, dy: 0.0)) - } - - if let (layout, _) = self.screenNode.validLayout { - let sectionInset: CGFloat - if layout.size.width >= 375.0 { - sectionInset = max(16.0, floor((layout.size.width - 674.0) / 2.0)) - } else { - sectionInset = 0.0 - } - let headerInset = sectionInset - - topHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: topNavigationBar.bounds.height, isModalOverlay: layout.isModalOverlay, isMediaOnly: false, contentOffset: 0.0, paneContainerY: 0.0, presentationData: self.presentationData, peer: self.screenNode.data?.savedMessagesPeer ?? self.screenNode.data?.peer, cachedData: self.screenNode.data?.cachedData, threadData: self.screenNode.data?.threadData, peerNotificationSettings: self.screenNode.data?.peerNotificationSettings, threadNotificationSettings: self.screenNode.data?.threadNotificationSettings, globalNotificationSettings: self.screenNode.data?.globalNotificationSettings, statusData: self.screenNode.data?.status, panelStatusData: (nil, nil, nil), isSecretChat: self.screenNode.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.screenNode.data?.isContact ?? false, isSettings: self.screenNode.isSettings, state: self.screenNode.state, profileGiftsContext: self.screenNode.data?.profileGiftsContext, screenData: self.screenNode.data, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, transition: transition, additive: false, animateHeader: false) - } - - let titleScale = (fraction * previousTitleNode.view.bounds.height + (1.0 - fraction) * self.headerNode.titleNodeRawContainer.bounds.height) / previousTitleNode.view.bounds.height - let subtitleScale = max(0.01, min(10.0, (fraction * previousStatusNode.bounds.height + (1.0 - fraction) * self.headerNode.subtitleNodeRawContainer.bounds.height) / previousStatusNode.bounds.height)) - - transition.updateFrame(node: previousTitleContainerNode, frame: CGRect(origin: self.headerNode.titleNodeRawContainer.frame.center, size: CGSize())) - transition.updateFrame(view: previousTitleNode.view, frame: CGRect(origin: CGPoint(x: -previousTitleFrame.width / 2.0, y: -previousTitleFrame.height / 2.0), size: previousTitleFrame.size)) - transition.updateFrame(node: previousStatusContainerNode, frame: CGRect(origin: self.headerNode.subtitleNodeRawContainer.frame.center, size: CGSize())) - transition.updateFrame(node: previousStatusNode, frame: CGRect(origin: CGPoint(x: -previousStatusFrame.size.width / 2.0, y: -previousStatusFrame.size.height / 2.0), size: previousStatusFrame.size)) - - transition.updateSublayerTransformScale(node: previousTitleContainerNode, scale: titleScale) - transition.updateSublayerTransformScale(node: previousStatusContainerNode, scale: subtitleScale) - - transition.updateAlpha(node: self.headerNode.titleNode, alpha: (1.0 - fraction)) - transition.updateAlpha(layer: previousTitleNode.view.layer, alpha: fraction) - transition.updateAlpha(node: self.headerNode.subtitleNode, alpha: (1.0 - fraction)) - transition.updateAlpha(node: previousStatusNode, alpha: fraction) - - transition.updateAlpha(node: self.headerNode.navigationButtonContainer, alpha: (1.0 - fraction)) - - if case .animated = transition, (bottomNavigationBar.additionalContentNode.alpha.isZero || bottomNavigationBar.additionalContentNode.alpha == 1.0) { - bottomNavigationBar.additionalContentNode.alpha = fraction - if fraction.isZero { - bottomNavigationBar.additionalContentNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15) - } else { - transition.updateAlpha(node: bottomNavigationBar.additionalContentNode, alpha: fraction) - } - } else { - transition.updateAlpha(node: bottomNavigationBar.additionalContentNode, alpha: fraction) - } - - let bottomHeight = bottomNavigationBar.backgroundNode.bounds.height - - transition.updateSublayerTransformOffset(layer: bottomNavigationBar.additionalContentNode.layer, offset: CGPoint(x: 0.0, y: (1.0 - fraction) * (topHeight - bottomHeight))) - - if let previousContentNode = self.previousContentNode, let previousContentNodeFrame = self.previousContentNodeFrame { - var updatedPreviousContentNodeFrame = bottomNavigationBar.view.convert(previousContentNodeFrame, to: bottomNavigationBar.view) - updatedPreviousContentNodeFrame.origin.y += (1.0 - fraction) * (topHeight - bottomHeight) - transition.updateFrame(node: previousContentNode, frame: updatedPreviousContentNodeFrame) - transition.updateAlpha(node: previousContentNode, alpha: fraction) - } - - if let previousSecondaryContentNode = self.previousSecondaryContentNode, let previousSecondaryContentNodeFrame = self.previousSecondaryContentNodeFrame { - var updatedPreviousSecondaryContentNodeFrame = bottomNavigationBar.view.convert(previousSecondaryContentNodeFrame, to: bottomNavigationBar.view) - updatedPreviousSecondaryContentNodeFrame.origin.y += (1.0 - fraction) * (topHeight - bottomHeight) - transition.updateFrame(node: previousSecondaryContentNode, frame: updatedPreviousSecondaryContentNodeFrame) - transition.updateAlpha(node: previousSecondaryContentNode, alpha: fraction) - } - } - } - - func restore() { - guard let topNavigationBar = self.topNavigationBar, let bottomNavigationBar = self.bottomNavigationBar else { - return - } - - topNavigationBar.additionalContentNode.alpha = 1.0 - ContainedViewLayoutTransition.immediate.updateSublayerTransformOffset(layer: topNavigationBar.additionalContentNode.layer, offset: CGPoint()) - topNavigationBar.reattachAdditionalContentNode() - - bottomNavigationBar.additionalContentNode.alpha = 1.0 - ContainedViewLayoutTransition.immediate.updateSublayerTransformOffset(layer: bottomNavigationBar.additionalContentNode.layer, offset: CGPoint()) - bottomNavigationBar.reattachAdditionalContentNode() - - topNavigationBar.isHidden = false - bottomNavigationBar.isHidden = false - self.headerNode.navigationTransition = nil - self.screenNode.insertSubnode(self.headerNode, aboveSubnode: self.screenNode.scrollNode) - - if let previousContentNode = self.previousContentNode, let previousContentNodeFrame = self.previousContentNodeFrame { - previousContentNode.frame = previousContentNodeFrame - previousContentNode.alpha = self.previousContentNodeAlpha - bottomNavigationBar.insertSubnode(previousContentNode, belowSubnode: bottomNavigationBar.stripeNode) - } - - if let previousSecondaryContentNode = self.previousSecondaryContentNode, let previousSecondaryContentNodeFrame = self.previousSecondaryContentNodeFrame { - previousSecondaryContentNode.frame = previousSecondaryContentNodeFrame - previousSecondaryContentNode.alpha = self.previousSecondaryContentNodeAlpha - bottomNavigationBar.clippingNode.addSubnode(previousSecondaryContentNode) - } - - if let previousTitleView = bottomNavigationBar.titleView as? ChatTitleView, let iconView = previousTitleView.titleCredibilityIconView.componentView { - iconView.frame = iconView.bounds - } - } -} - -private final class ContextControllerContentSourceImpl: ContextControllerContentSource { +final class ContextControllerContentSourceImpl: ContextControllerContentSource { let controller: ViewController weak var sourceNode: ASDisplayNode? let sourceRect: CGRect @@ -14793,7 +7134,7 @@ private final class ContextControllerContentSourceImpl: ContextControllerContent } } -private final class MessageContextExtractedContentSource: ContextExtractedContentSource { +final class MessageContextExtractedContentSource: ContextExtractedContentSource { let keepInPlace: Bool = false let ignoreContentTouches: Bool = true let blurBackground: Bool = true @@ -14813,7 +7154,7 @@ private final class MessageContextExtractedContentSource: ContextExtractedConten } } -private final class PeerInfoContextExtractedContentSource: ContextExtractedContentSource { +final class PeerInfoContextExtractedContentSource: ContextExtractedContentSource { var keepInPlace: Bool = false let ignoreContentTouches: Bool = true let blurBackground: Bool = true @@ -14835,7 +7176,7 @@ private final class PeerInfoContextExtractedContentSource: ContextExtractedConte } } -private final class PeerInfoContextReferenceContentSource: ContextReferenceContentSource { +final class PeerInfoContextReferenceContentSource: ContextReferenceContentSource { private let controller: ViewController private let sourceNode: ContextReferenceContentNode @@ -14849,214 +7190,6 @@ private final class PeerInfoContextReferenceContentSource: ContextReferenceConte } } -public func presentAddMembersImpl(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, parentController: ViewController, groupPeer: Peer, selectAddMemberDisposable: MetaDisposable, addMemberDisposable: MetaDisposable) { - let members: Promise<[PeerId]> = Promise() - if groupPeer.id.namespace == Namespaces.Peer.CloudChannel { - /*var membersDisposable: Disposable? - let (disposable, _) = context.peerChannelMemberCategoriesContextsManager.recent(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerView.peerId, updated: { listState in - members.set(.single(listState.list.map {$0.peer.id})) - membersDisposable?.dispose() - }) - membersDisposable = disposable*/ - members.set(.single([])) - } else { - members.set(.single([])) - } - - let _ = (members.get() - |> take(1) - |> deliverOnMainQueue).startStandalone(next: { [weak parentController] recentIds in - var createInviteLinkImpl: (() -> Void)? - var confirmationImpl: ((PeerId) -> Signal)? - let _ = confirmationImpl - var options: [ContactListAdditionalOption] = [] - let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } - - var canCreateInviteLink = false - if let group = groupPeer as? TelegramGroup { - switch group.role { - case .creator: - canCreateInviteLink = true - case let .admin(rights, _): - canCreateInviteLink = rights.rights.contains(.canInviteUsers) - default: - break - } - } else if let channel = groupPeer as? TelegramChannel, (channel.addressName?.isEmpty ?? true) { - if channel.flags.contains(.isCreator) || (channel.adminRights?.rights.contains(.canInviteUsers) == true) { - canCreateInviteLink = true - } - } - - if canCreateInviteLink { - options.append(ContactListAdditionalOption(title: presentationData.strings.GroupInfo_InviteByLink, icon: .generic(UIImage(bundleImageName: "Contact List/LinkActionIcon")!), action: { - createInviteLinkImpl?() - }, clearHighlightAutomatically: true)) - } - - let contactsController = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, updatedPresentationData: updatedPresentationData, mode: .peerSelection(searchChatList: false, searchGroups: false, searchChannels: false), options: .single(options), filters: [.excludeSelf, .disable(recentIds)], onlyWriteable: true, isGroupInvitation: true)) - contactsController.navigationPresentation = .modal - - confirmationImpl = { [weak contactsController] peerId in - return context.account.postbox.loadedPeerWithId(peerId) - |> deliverOnMainQueue - |> mapToSignal { peer in - let result = ValuePromise() - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - if let contactsController = contactsController { - let alertController = textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.GroupInfo_AddParticipantConfirmation(EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string, actions: [ - TextAlertAction(type: .genericAction, title: presentationData.strings.Common_No, action: { - result.set(false) - }), - TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Yes, action: { - result.set(true) - }) - ]) - contactsController.present(alertController, in: .window(.root)) - } - - return result.get() - } - } - - let addMembers: ([ContactListPeerId]) -> Signal<[(PeerId, AddChannelMemberError)], NoError> = { members -> Signal<[(PeerId, AddChannelMemberError)], NoError> in - let memberIds = members.compactMap { contact -> PeerId? in - switch contact { - case let .peer(peerId): - return peerId - default: - return nil - } - } - return context.account.postbox.multiplePeersView(memberIds) - |> take(1) - |> deliverOnMainQueue - |> mapToSignal { view -> Signal<[(PeerId, AddChannelMemberError)], NoError> in - if groupPeer.id.namespace == Namespaces.Peer.CloudChannel { - if memberIds.count == 1 { - return context.peerChannelMemberCategoriesContextsManager.addMember(engine: context.engine, peerId: groupPeer.id, memberId: memberIds[0]) - |> map { _ -> [(PeerId, AddChannelMemberError)] in - } - |> then(Signal<[(PeerId, AddChannelMemberError)], AddChannelMemberError>.single([])) - |> `catch` { error -> Signal<[(PeerId, AddChannelMemberError)], NoError> in - return .single([(memberIds[0], error)]) - } - } else { - return context.peerChannelMemberCategoriesContextsManager.addMembersAllowPartial(engine: context.engine, peerId: groupPeer.id, memberIds: memberIds) - } - } else { - var signals: [Signal<(PeerId, AddChannelMemberError)?, NoError>] = [] - for memberId in memberIds { - let signal: Signal<(PeerId, AddChannelMemberError)?, NoError> = context.engine.peers.addGroupMember(peerId: groupPeer.id, memberId: memberId) - |> mapError { error -> AddChannelMemberError in - switch error { - case .generic: - return .generic - case .groupFull: - return .limitExceeded - case let .privacy(privacy): - return .restricted(privacy?.forbiddenPeers.first) - case .notMutualContact: - return .notMutualContact - case .tooManyChannels: - return .generic - } - } - |> ignoreValues - |> map { _ -> (PeerId, AddChannelMemberError)? in - } - |> then(Signal<(PeerId, AddChannelMemberError)?, AddChannelMemberError>.single(nil)) - |> `catch` { error -> Signal<(PeerId, AddChannelMemberError)?, NoError> in - return .single((memberId, error)) - } - signals.append(signal) - } - return combineLatest(signals) - |> map { values -> [(PeerId, AddChannelMemberError)] in - return values.compactMap { $0 } - } - } - } - } - - createInviteLinkImpl = { [weak contactsController] in - contactsController?.view.window?.endEditing(true) - contactsController?.present(InviteLinkInviteController(context: context, updatedPresentationData: updatedPresentationData, mode: .groupOrChannel(peerId: groupPeer.id), initialInvite: nil, parentNavigationController: contactsController?.navigationController as? NavigationController), in: .window(.root)) - } - - parentController?.push(contactsController) - do { - selectAddMemberDisposable.set(( - combineLatest(queue: .mainQueue(), - context.engine.data.get(TelegramEngine.EngineData.Item.Peer.ExportedInvitation(id: groupPeer.id)), - contactsController.result - ) - |> deliverOnMainQueue).start(next: { [weak contactsController] exportedInvitation, result in - var peers: [ContactListPeerId] = [] - if case let .result(peerIdsValue, _) = result { - peers = peerIdsValue - } - - contactsController?.displayProgress = true - addMemberDisposable.set((addMembers(peers) - |> deliverOnMainQueue).start(next: { failedPeerIds in - if failedPeerIds.isEmpty { - contactsController?.dismiss() - - let mappedPeerIds: [EnginePeer.Id] = peers.compactMap { peer -> EnginePeer.Id? in - switch peer { - case let .peer(id): - return id - default: - return nil - } - } - if !mappedPeerIds.isEmpty { - let _ = (context.engine.data.get(EngineDataMap(mappedPeerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:)))) - |> deliverOnMainQueue).startStandalone(next: { maybePeers in - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let peers = maybePeers.compactMap { $0.value } - - let text: String - if peers.count == 1 { - text = presentationData.strings.PeerInfo_NotificationMemberAdded(peers[0].displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string - } else { - text = presentationData.strings.PeerInfo_NotificationMultipleMembersAdded(Int32(peers.count)) - } - parentController?.present(UndoOverlayController(presentationData: presentationData, content: .peers(context: context, peers: peers, title: nil, text: text, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) - }) - } - } else { - let failedPeers = failedPeerIds.compactMap { _, error -> TelegramForbiddenInvitePeer? in - if case let .restricted(peer) = error { - return peer - } else { - return nil - } - } - - if !failedPeers.isEmpty, let contactsController, let navigationController = contactsController.navigationController as? NavigationController { - var viewControllers = navigationController.viewControllers - if let index = viewControllers.firstIndex(where: { $0 === contactsController }) { - let inviteScreen = SendInviteLinkScreen(context: context, subject: .chat(peer: EnginePeer(groupPeer), link: exportedInvitation?.link), peers: failedPeers) - viewControllers.remove(at: index) - viewControllers.append(inviteScreen) - navigationController.setViewControllers(viewControllers, animated: true) - } - } else { - contactsController?.dismiss() - } - } - })) - })) - contactsController.dismissed = { - selectAddMemberDisposable.set(nil) - addMemberDisposable.set(nil) - } - } - }) -} - struct ClearPeerHistory { enum ClearType { case savedMessages @@ -15126,235 +7259,3 @@ struct ClearPeerHistory { } } } - -private final class AccountPeerContextItem: ContextMenuCustomItem { - let context: AccountContext - let account: Account - let peer: EnginePeer - let action: (ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void - - init(context: AccountContext, account: Account, peer: EnginePeer, action: @escaping (ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void) { - self.context = context - self.account = account - self.peer = peer - self.action = action - } - - public func node(presentationData: PresentationData, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void) -> ContextMenuCustomNode { - return AccountPeerContextItemNode(presentationData: presentationData, item: self, getController: getController, actionSelected: actionSelected) - } -} - -private final class AccountPeerContextItemNode: ASDisplayNode, ContextMenuCustomNode { - private let item: AccountPeerContextItem - private let presentationData: PresentationData - private let getController: () -> ContextControllerProtocol? - private let actionSelected: (ContextMenuActionResult) -> Void - - private let highlightedBackgroundNode: ASDisplayNode - private let buttonNode: HighlightTrackingButtonNode - private let textNode: ImmediateTextNode - private let avatarNode: AvatarNode - private let emojiStatusView: ComponentView - - init(presentationData: PresentationData, item: AccountPeerContextItem, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void) { - self.item = item - self.presentationData = presentationData - self.getController = getController - self.actionSelected = actionSelected - - let textFont = Font.regular(presentationData.listsFontSize.baseDisplaySize * 17.0 / 17.0) - - self.highlightedBackgroundNode = ASDisplayNode() - self.highlightedBackgroundNode.isAccessibilityElement = false - self.highlightedBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor - self.highlightedBackgroundNode.alpha = 0.0 - - self.textNode = ImmediateTextNode() - self.textNode.isAccessibilityElement = false - self.textNode.isUserInteractionEnabled = false - self.textNode.displaysAsynchronously = false - let peerTitle = item.peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) - self.textNode.attributedText = NSAttributedString(string: peerTitle, font: textFont, textColor: presentationData.theme.contextMenu.primaryColor) - self.textNode.maximumNumberOfLines = 1 - - self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 14.0)) - - self.emojiStatusView = ComponentView() - - self.buttonNode = HighlightTrackingButtonNode() - self.buttonNode.isAccessibilityElement = true - self.buttonNode.accessibilityLabel = peerTitle - - super.init() - - self.addSubnode(self.highlightedBackgroundNode) - self.addSubnode(self.textNode) - self.addSubnode(self.avatarNode) - self.addSubnode(self.buttonNode) - - self.buttonNode.highligthedChanged = { [weak self] highligted in - guard let strongSelf = self else { - return - } - if highligted { - strongSelf.highlightedBackgroundNode.alpha = 1.0 - } else { - strongSelf.highlightedBackgroundNode.alpha = 0.0 - strongSelf.highlightedBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3) - } - } - self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside) - } - - func updateLayout(constrainedWidth: CGFloat, constrainedHeight: CGFloat) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) { - let sideInset: CGFloat = 16.0 - let iconSideInset: CGFloat = 12.0 - let verticalInset: CGFloat = 12.0 - - let iconSize = CGSize(width: 28.0, height: 28.0) - - let standardIconWidth: CGFloat = 32.0 - var rightTextInset: CGFloat = sideInset - if !iconSize.width.isZero { - rightTextInset = max(iconSize.width, standardIconWidth) + iconSideInset + sideInset - 12.0 - } - - self.avatarNode.setPeer(context: self.item.context, account: self.item.account, theme: self.presentationData.theme, peer: self.item.peer) - - if self.item.peer.emojiStatus != nil { - rightTextInset += 32.0 - } - - let textSize = self.textNode.updateLayout(CGSize(width: constrainedWidth - sideInset - rightTextInset, height: .greatestFiniteMagnitude)) - - return (CGSize(width: textSize.width + sideInset + rightTextInset, height: verticalInset * 2.0 + textSize.height), { size, transition in - let verticalOrigin = floor((size.height - textSize.height) / 2.0) - let textFrame = CGRect(origin: CGPoint(x: sideInset, y: verticalOrigin), size: textSize) - transition.updateFrameAdditive(node: self.textNode, frame: textFrame) - - var iconContent: EmojiStatusComponent.Content? - if case let .user(user) = self.item.peer { - if let emojiStatus = user.emojiStatus { - iconContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 28.0, height: 28.0), placeholderColor: self.presentationData.theme.list.mediaPlaceholderColor, themeColor: self.presentationData.theme.list.itemAccentColor, loopMode: .forever) - } else if user.isPremium { - iconContent = .premium(color: self.presentationData.theme.list.itemAccentColor) - } - } else if case let .channel(channel) = self.item.peer { - if let emojiStatus = channel.emojiStatus { - iconContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 28.0, height: 28.0), placeholderColor: self.presentationData.theme.list.mediaPlaceholderColor, themeColor: self.presentationData.theme.list.itemAccentColor, loopMode: .forever) - } - } - if let iconContent { - let emojiStatusSize = self.emojiStatusView.update( - transition: .immediate, - component: AnyComponent(EmojiStatusComponent( - context: self.item.context, - animationCache: self.item.context.animationCache, - animationRenderer: self.item.context.animationRenderer, - content: iconContent, - isVisibleForAnimations: true, - action: nil - )), - environment: {}, - containerSize: CGSize(width: 24.0, height: 24.0) - ) - if let view = self.emojiStatusView.view { - if view.superview == nil { - self.view.addSubview(view) - } - transition.updateFrame(view: view, frame: CGRect(origin: CGPoint(x: textFrame.maxX + 2.0, y: textFrame.minY + floor((textFrame.height - emojiStatusSize.height) / 2.0)), size: emojiStatusSize)) - } - } - - transition.updateFrame(node: self.avatarNode, frame: CGRect(origin: CGPoint(x: size.width - standardIconWidth - iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize)) - - transition.updateFrame(node: self.highlightedBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))) - transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))) - }) - } - - func updateTheme(presentationData: PresentationData) { - self.highlightedBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor - - if let attributedText = self.textNode.attributedText { - let updatedAttributedText = NSMutableAttributedString(attributedString: attributedText) - updatedAttributedText.addAttribute(.foregroundColor, value: presentationData.theme.contextMenu.primaryColor.cgColor, range: NSRange(location: 0, length: updatedAttributedText.length)) - self.textNode.attributedText = updatedAttributedText - } - } - - @objc private func buttonPressed() { - self.performAction() - } - - func canBeHighlighted() -> Bool { - return true - } - - func setIsHighlighted(_ value: Bool) { - if value { - self.highlightedBackgroundNode.alpha = 1.0 - } else { - self.highlightedBackgroundNode.alpha = 0.0 - } - } - - func updateIsHighlighted(isHighlighted: Bool) { - self.setIsHighlighted(isHighlighted) - } - - func performAction() { - guard let controller = self.getController() else { - return - } - self.item.action(controller, { [weak self] result in - self?.actionSelected(result) - }) - } -} - -private final class PeerInfoControllerContextReferenceContentSource: ContextReferenceContentSource { - let controller: ViewController - let sourceView: UIView - let insets: UIEdgeInsets - let contentInsets: UIEdgeInsets - - init(controller: ViewController, sourceView: UIView, insets: UIEdgeInsets, contentInsets: UIEdgeInsets = UIEdgeInsets()) { - self.controller = controller - self.sourceView = sourceView - self.insets = insets - self.contentInsets = contentInsets - } - - func transitionInfo() -> ContextControllerReferenceViewInfo? { - return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: UIScreen.main.bounds.inset(by: self.insets), insets: self.contentInsets) - } -} - -private final class HeaderContextReferenceContentSource: ContextReferenceContentSource { - private let controller: ViewController - private let sourceView: UIView - - init(controller: ViewController, sourceView: UIView) { - self.controller = controller - self.sourceView = sourceView - } - - func transitionInfo() -> ContextControllerReferenceViewInfo? { - return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: UIScreen.main.bounds) - } -} - -private func cancelContextGestures(view: UIView) { - if let gestureRecognizers = view.gestureRecognizers { - for gesture in gestureRecognizers { - if let gesture = gesture as? ContextGesture { - gesture.cancel() - } - } - } - for subview in view.subviews { - cancelContextGestures(view: subview) - } -} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenAvatarSetup.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenAvatarSetup.swift index 88d95df3..4705dc95 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenAvatarSetup.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenAvatarSetup.swift @@ -103,9 +103,14 @@ extension PeerInfoScreenImpl { let parentController = (self.context.sharedContext.mainWindow?.viewController as? NavigationController)?.topViewController as? ViewController var dismissImpl: (() -> Void)? - let mainController = self.context.sharedContext.makeAvatarMediaPickerScreen(context: self.context, getSourceRect: { return nil }, canDelete: hasDeleteButton, performDelete: { [weak self] in + let (mainController, pickerHolder) = self.context.sharedContext.makeAvatarMediaPickerScreen(context: self.context, getSourceRect: { return nil }, canDelete: hasDeleteButton, performDelete: { [weak self] in self?.openAvatarRemoval(mode: mode, peer: peer, item: item) - }, completion: { result, transitionView, transitionRect, transitionImage, fromCamera, transitionOut, cancelled in + }, completion: { [weak self] result, transitionView, transitionRect, transitionImage, fromCamera, transitionOut, cancelled in + guard let self else { + return + } + self.avatarPickerHolder = nil + var resultImage: UIImage? let uploadStatusPromise = Promise(.progress(0.0)) @@ -155,7 +160,6 @@ extension PeerInfoScreenImpl { commit() } parentController?.push(controller) - //isFromEditor = true return } @@ -232,31 +236,37 @@ extension PeerInfoScreenImpl { self.parentController?.pushViewController(editorController) } }, dismissed: { - }) - dismissImpl = { [weak self, weak mainController] in - if let mainController, let navigationController = mainController.navigationController { - var viewControllers = navigationController.viewControllers - viewControllers = viewControllers.filter { c in - return !(c is CameraScreen) && c !== mainController + self.avatarPickerHolder = pickerHolder + if let mainController { + dismissImpl = { [weak self, weak mainController] in + if let mainController, let navigationController = mainController.navigationController { + var viewControllers = navigationController.viewControllers + viewControllers = viewControllers.filter { c in + return !(c is CameraScreen) && c !== mainController + } + navigationController.setViewControllers(viewControllers, animated: false) } - navigationController.setViewControllers(viewControllers, animated: false) - } - if let self, let navigationController = self.parentController, let mainController { - var viewControllers = navigationController.viewControllers - viewControllers = viewControllers.filter { c in - return !(c is CameraScreen) && c !== mainController + if let self, let navigationController = self.parentController, let mainController { + var viewControllers = navigationController.viewControllers + viewControllers = viewControllers.filter { c in + return !(c is CameraScreen) && c !== mainController + } + navigationController.setViewControllers(viewControllers, animated: false) + } + + } + if mainController is ActionSheetController { + self.present(mainController, in: .window(.root)) + } else { + mainController.navigationPresentation = .flatModal + mainController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) + if self.navigationController != nil { + self.push(mainController) + } else { + self.parentController?.pushViewController(mainController) } - navigationController.setViewControllers(viewControllers, animated: false) } - - } - mainController.navigationPresentation = .flatModal - mainController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) - if self.navigationController != nil { - self.push(mainController) - } else { - self.parentController?.pushViewController(mainController) } }) } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenBusinessActions.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenBusinessActions.swift new file mode 100644 index 00000000..fe797392 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenBusinessActions.swift @@ -0,0 +1,182 @@ +import Foundation +import UIKit +import Display +import AccountContext +import SwiftSignalKit +import Postbox +import TelegramCore +import AsyncDisplayKit +import ContextUI +import UndoUI + +extension PeerInfoScreenNode { + func openWorkingHoursContextMenu(node: ASDisplayNode, gesture: ContextGesture?) { + guard let sourceNode = node as? ContextExtractedContentContainingNode else { + return + } + guard let cachedData = self.data?.cachedData else { + return + } + + var businessHours: TelegramBusinessHours? + if let cachedData = cachedData as? CachedUserData { + businessHours = cachedData.businessHours + } + + guard let businessHours else { + return + } + + let copyAction = { [weak self] in + guard let self else { + return + } + UIPasteboard.general.string = businessHoursTextToCopy(businessHours: businessHours, presentationData: self.presentationData, displayLocalTimezone: false) + + self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .copy(text: self.presentationData.strings.MyProfile_ToastHoursCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) + } + + var items: [ContextMenuItem] = [] + + if self.isMyProfile { + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_HoursActionEdit, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in + c?.dismiss { + guard let self else { + return + } + let businessHoursSetupScreen = self.context.sharedContext.makeBusinessHoursSetupScreen(context: self.context, initialValue: businessHours, completion: { _ in }) + self.controller?.push(businessHoursSetupScreen) + } + }))) + } + + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_HoursActionCopy, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in + c?.dismiss { + copyAction() + } + }))) + + if self.isMyProfile { + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_HoursActionRemove, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, _ + in + guard let self else { + return + } + + var subItems: [ContextMenuItem] = [] + let noAction: ((ContextMenuActionItem.Action) -> Void)? = nil + subItems.append(.action(ContextMenuActionItem( + text: self.presentationData.strings.MyProfile_HoursRemoveConfirmation_Title, + textLayout: .multiline, + textFont: .small, + icon: { _ in nil }, + action: noAction + ))) + subItems.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_HoursRemoveConfirmation_Action, textColor: .destructive, icon: { _ in nil }, action: { [weak self] c, _ in + c?.dismiss { + guard let self else { + return + } + let _ = self.context.engine.accountData.updateAccountBusinessHours(businessHours: nil).startStandalone() + } + }))) + c?.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) + }))) + } + + let actions = ContextController.Items(content: .list(items)) + + let contextController = ContextController(presentationData: self.presentationData, source: .extracted(PeerInfoContextExtractedContentSource(sourceNode: sourceNode)), items: .single(actions), gesture: gesture) + self.controller?.present(contextController, in: .window(.root)) + } + + func openBusinessLocationContextMenu(node: ASDisplayNode, gesture: ContextGesture?) { + guard let sourceNode = node as? ContextExtractedContentContainingNode else { + return + } + guard let cachedData = self.data?.cachedData else { + return + } + + var businessLocation: TelegramBusinessLocation? + if let cachedData = cachedData as? CachedUserData { + businessLocation = cachedData.businessLocation + } + + guard let businessLocation else { + return + } + + let copyAction = { [weak self] in + guard let self else { + return + } + UIPasteboard.general.string = businessLocation.address + + self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .copy(text: self.presentationData.strings.MyProfile_ToastLocationCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) + } + + var items: [ContextMenuItem] = [] + + if businessLocation.coordinates != nil { + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_LocationActionOpen, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Media Editor/LocationSmall"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in + c?.dismiss(completion: { + guard let self else { + return + } + self.interaction.openLocation() + }) + }))) + } + + if !businessLocation.address.isEmpty { + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_LocationActionCopy, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in + c?.dismiss { + copyAction() + } + }))) + } + + if self.isMyProfile { + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_LocationActionEdit, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in + c?.dismiss { + guard let self else { + return + } + let businessLocationSetupScreen = self.context.sharedContext.makeBusinessLocationSetupScreen(context: self.context, initialValue: businessLocation, completion: { _ in }) + self.controller?.push(businessLocationSetupScreen) + } + }))) + + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_LocationActionRemove, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, _ in + guard let self else { + return + } + + var subItems: [ContextMenuItem] = [] + let noAction: ((ContextMenuActionItem.Action) -> Void)? = nil + subItems.append(.action(ContextMenuActionItem( + text: self.presentationData.strings.MyProfile_LocationRemoveConfirmation_Title, + textLayout: .multiline, + textFont: .small, + icon: { _ in nil }, + action: noAction + ))) + subItems.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_LocationRemoveConfirmation_Action, textColor: .destructive, icon: { _ in nil }, action: { [weak self] c, _ in + c?.dismiss { + guard let self else { + return + } + let _ = self.context.engine.accountData.updateAccountBusinessLocation(businessLocation: nil).startStandalone() + } + }))) + c?.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) + }))) + } + + let actions = ContextController.Items(content: .list(items)) + + let contextController = ContextController(presentationData: self.presentationData, source: .extracted(PeerInfoContextExtractedContentSource(sourceNode: sourceNode)), items: .single(actions), gesture: gesture) + self.controller?.present(contextController, in: .window(.root)) + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenCallActions.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenCallActions.swift new file mode 100644 index 00000000..c3b58b33 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenCallActions.swift @@ -0,0 +1,361 @@ +import Foundation +import UIKit +import Display +import AccountContext +import SwiftSignalKit +import Postbox +import TelegramCore +import AsyncDisplayKit +import ContextUI +import CreateExternalMediaStreamScreen +import OverlayStatusController +import TelegramPresentationData +import PresentationDataUtils +import TelegramCallsUI +import AvatarNode + +extension PeerInfoScreenNode { + func requestCall(isVideo: Bool, gesture: ContextGesture? = nil, contextController: ContextControllerProtocol? = nil, result: ((ContextMenuActionResult) -> Void)? = nil, backAction: ((ContextControllerProtocol) -> Void)? = nil) { + guard let controller = self.controller, !controller.presentAccountFrozenInfoIfNeeded() else { + return + } + let peerId = self.peerId + let requestCall: (PeerId?, EngineGroupCallDescription?) -> Void = { [weak self] defaultJoinAsPeerId, activeCall in + if let activeCall = activeCall { + self?.context.joinGroupCall(peerId: peerId, invite: nil, requestJoinAsPeerId: { completion in + if let defaultJoinAsPeerId = defaultJoinAsPeerId { + result?(.dismissWithoutContent) + completion(defaultJoinAsPeerId) + } else { + self?.openVoiceChatDisplayAsPeerSelection(completion: { joinAsPeerId in + completion(joinAsPeerId) + }, gesture: gesture, contextController: contextController, result: result, backAction: backAction) + } + }, activeCall: activeCall) + } else { + self?.openVoiceChatOptions(defaultJoinAsPeerId: defaultJoinAsPeerId, gesture: gesture, contextController: contextController) + } + } + + if let cachedChannelData = self.data?.cachedData as? CachedChannelData { + requestCall(cachedChannelData.callJoinPeerId, cachedChannelData.activeCall.flatMap(EngineGroupCallDescription.init)) + return + } else if let cachedGroupData = self.data?.cachedData as? CachedGroupData { + requestCall(cachedGroupData.callJoinPeerId, cachedGroupData.activeCall.flatMap(EngineGroupCallDescription.init)) + return + } + + guard let peer = self.data?.peer as? TelegramUser, let cachedUserData = self.data?.cachedData as? CachedUserData else { + return + } + if cachedUserData.callsPrivate { + self.controller?.push(self.context.sharedContext.makeSendInviteLinkScreen(context: self.context, subject: .groupCall(.create), peers: [TelegramForbiddenInvitePeer( + peer: EnginePeer(peer), + canInviteWithPremium: false, + premiumRequiredToContact: false + )], theme: self.presentationData.theme)) + return + } + + self.context.requestCall(peerId: peer.id, isVideo: isVideo, completion: {}) + } + + func scheduleGroupCall() { + guard let controller = self.controller, !controller.presentAccountFrozenInfoIfNeeded() else { + return + } + self.context.scheduleGroupCall(peerId: self.peerId, parentController: controller) + } + + func createExternalStream(credentialsPromise: Promise?) { + guard let controller = self.controller, !controller.presentAccountFrozenInfoIfNeeded() else { + return + } + self.controller?.push(CreateExternalMediaStreamScreen(context: self.context, peerId: self.peerId, credentialsPromise: credentialsPromise, mode: .create(liveStream: false))) + } + + func createAndJoinGroupCall(peerId: PeerId, joinAsPeerId: PeerId?) { + guard let controller = self.controller, !controller.presentAccountFrozenInfoIfNeeded() else { + return + } + if let _ = self.context.sharedContext.callManager { + let startCall: (Bool) -> Void = { [weak self] endCurrentIfAny in + guard let strongSelf = self else { + return + } + + var cancelImpl: (() -> Void)? + let presentationData = strongSelf.presentationData + let progressSignal = Signal { [weak self] subscriber in + let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: { + cancelImpl?() + })) + self?.controller?.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() + let createSignal = strongSelf.context.engine.calls.createGroupCall(peerId: peerId, title: nil, scheduleDate: nil, isExternalStream: false) + |> afterDisposed { + Queue.mainQueue().async { + progressDisposable.dispose() + } + } + cancelImpl = { [weak self] in + self?.activeActionDisposable.set(nil) + } + strongSelf.activeActionDisposable.set((createSignal + |> deliverOnMainQueue).start(next: { [weak self] info in + guard let strongSelf = self else { + return + } + strongSelf.context.joinGroupCall(peerId: peerId, invite: nil, requestJoinAsPeerId: { result in + result(joinAsPeerId) + }, activeCall: EngineGroupCallDescription(id: info.id, accessHash: info.accessHash, title: info.title, scheduleTimestamp: nil, subscribedToScheduled: false, isStream: info.isStream)) + }, error: { [weak self] error in + guard let strongSelf = self else { + return + } + strongSelf.headerNode.navigationButtonContainer.performAction?(.cancel, nil, nil) + + let text: String + switch error { + case .generic, .scheduledTooLate: + text = strongSelf.presentationData.strings.Login_UnknownError + case .anonymousNotAllowed: + if let channel = strongSelf.data?.peer as? TelegramChannel, case .broadcast = channel.info { + text = strongSelf.presentationData.strings.LiveStream_AnonymousDisabledAlertText + } else { + text = strongSelf.presentationData.strings.VoiceChat_AnonymousDisabledAlertText + } + } + strongSelf.controller?.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.controller?.updatedPresentationData, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + })) + } + + startCall(true) + } + } + + func openVoiceChatDisplayAsPeerSelection(completion: @escaping (PeerId) -> Void, gesture: ContextGesture? = nil, contextController: ContextControllerProtocol? = nil, result: ((ContextMenuActionResult) -> Void)? = nil, backAction: ((ContextControllerProtocol) -> Void)? = nil) { + let dismissOnSelection = contextController == nil + let currentAccountPeer = self.context.account.postbox.loadedPeerWithId(context.account.peerId) + |> map { peer in + return [FoundPeer(peer: peer, subscribers: nil)] + } + let _ = (combineLatest(queue: Queue.mainQueue(), currentAccountPeer, self.displayAsPeersPromise.get() |> take(1)) + |> map { currentAccountPeer, availablePeers -> [FoundPeer] in + var result = currentAccountPeer + result.append(contentsOf: availablePeers) + return result + }).startStandalone(next: { [weak self] peers in + guard let strongSelf = self else { + return + } + if peers.count == 1, let peer = peers.first { + result?(.dismissWithoutContent) + completion(peer.peer.id) + } else { + var items: [ContextMenuItem] = [] + + var isGroup = false + for peer in peers { + if peer.peer is TelegramGroup { + isGroup = true + break + } else if let peer = peer.peer as? TelegramChannel, case .group = peer.info { + isGroup = true + break + } + } + + items.append(.custom(VoiceChatInfoContextItem(text: isGroup ? strongSelf.presentationData.strings.VoiceChat_DisplayAsInfoGroup : strongSelf.presentationData.strings.VoiceChat_DisplayAsInfo, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Call/Context Menu/Accounts"), color: theme.actionSheet.primaryTextColor) + }), true)) + + for peer in peers { + var subtitle: String? + if peer.peer.id.namespace == Namespaces.Peer.CloudUser { + subtitle = strongSelf.presentationData.strings.VoiceChat_PersonalAccount + } else if let subscribers = peer.subscribers { + if let peer = peer.peer as? TelegramChannel, case .broadcast = peer.info { + subtitle = strongSelf.presentationData.strings.Conversation_StatusSubscribers(subscribers) + } else { + subtitle = strongSelf.presentationData.strings.Conversation_StatusMembers(subscribers) + } + } + + let avatarSize = CGSize(width: 28.0, height: 28.0) + let avatarSignal = peerAvatarCompleteImage(account: strongSelf.context.account, peer: EnginePeer(peer.peer), size: avatarSize) + items.append(.action(ContextMenuActionItem(text: EnginePeer(peer.peer).displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder), textLayout: subtitle.flatMap { .secondLineWithValue($0) } ?? .singleLine, icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: avatarSize, signal: avatarSignal), action: { _, f in + if dismissOnSelection { + f(.dismissWithoutContent) + } + completion(peer.peer.id) + }))) + + if peer.peer.id.namespace == Namespaces.Peer.CloudUser { + items.append(.separator) + } + } + if backAction != nil { + items.append(.separator) + items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Common_Back, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.actionSheet.primaryTextColor) + }, iconPosition: .left, action: { (c, _) in + if let c, let backAction = backAction { + backAction(c) + } + }))) + } + + if let contextController = contextController { + contextController.setItems(.single(ContextController.Items(content: .list(items))), minHeight: nil, animated: true) + } else { + strongSelf.state = strongSelf.state.withHighlightedButton(.voiceChat) + if let (layout, navigationHeight) = strongSelf.validLayout { + strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) + } + + if let sourceNode = strongSelf.headerNode.buttonNodes[.voiceChat]?.referenceNode, let controller = strongSelf.controller { + let contextController = ContextController(presentationData: strongSelf.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + contextController.dismissed = { [weak self] in + if let strongSelf = self { + strongSelf.state = strongSelf.state.withHighlightedButton(nil) + if let (layout, navigationHeight) = strongSelf.validLayout { + strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) + } + } + } + controller.presentInGlobalOverlay(contextController) + } + } + } + }) + } + + func openVoiceChatOptions(defaultJoinAsPeerId: PeerId?, gesture: ContextGesture? = nil, contextController: ContextControllerProtocol? = nil) { + guard let chatPeer = self.data?.peer else { + return + } + let context = self.context + let peerId = self.peerId + let defaultJoinAsPeerId = defaultJoinAsPeerId ?? self.context.account.peerId + let currentAccountPeer = self.context.account.postbox.loadedPeerWithId(self.context.account.peerId) + |> map { peer in + return [FoundPeer(peer: peer, subscribers: nil)] + } + let _ = (combineLatest(queue: Queue.mainQueue(), currentAccountPeer, self.displayAsPeersPromise.get() |> take(1)) + |> map { currentAccountPeer, availablePeers -> [FoundPeer] in + var result = currentAccountPeer + result.append(contentsOf: availablePeers) + return result + }).startStandalone(next: { [weak self] peers in + guard let strongSelf = self else { + return + } + + var items: [ContextMenuItem] = [] + + if peers.count > 1 { + var selectedPeer: FoundPeer? + for peer in peers { + if peer.peer.id == defaultJoinAsPeerId { + selectedPeer = peer + } + } + if let peer = selectedPeer { + let avatarSize = CGSize(width: 28.0, height: 28.0) + items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.VoiceChat_DisplayAs, textLayout: .secondLineWithValue(EnginePeer(peer.peer).displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)), icon: { _ in nil }, iconSource: ContextMenuActionItemIconSource(size: avatarSize, signal: peerAvatarCompleteImage(account: strongSelf.context.account, peer: EnginePeer(peer.peer), size: avatarSize)), action: { c, f in + guard let strongSelf = self else { + return + } + + strongSelf.openVoiceChatDisplayAsPeerSelection(completion: { joinAsPeerId in + let _ = context.engine.calls.updateGroupCallJoinAsPeer(peerId: peerId, joinAs: joinAsPeerId).startStandalone() + self?.openVoiceChatOptions(defaultJoinAsPeerId: joinAsPeerId, gesture: nil, contextController: c) + }, gesture: gesture, contextController: c, result: f, backAction: { [weak self] c in + self?.openVoiceChatOptions(defaultJoinAsPeerId: defaultJoinAsPeerId, gesture: nil, contextController: c) + }) + + }))) + items.append(.separator) + } + } + + let createVoiceChatTitle: String + let scheduleVoiceChatTitle: String + if let channel = strongSelf.data?.peer as? TelegramChannel, case .broadcast = channel.info { + createVoiceChatTitle = strongSelf.presentationData.strings.ChannelInfo_CreateLiveStream + scheduleVoiceChatTitle = strongSelf.presentationData.strings.ChannelInfo_ScheduleLiveStream + } else { + createVoiceChatTitle = strongSelf.presentationData.strings.ChannelInfo_CreateVoiceChat + scheduleVoiceChatTitle = strongSelf.presentationData.strings.ChannelInfo_ScheduleVoiceChat + } + + items.append(.action(ContextMenuActionItem(text: createVoiceChatTitle, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/VoiceChat"), color: theme.contextMenu.primaryColor) }, action: { _, f in + f(.dismissWithoutContent) + + self?.createAndJoinGroupCall(peerId: peerId, joinAsPeerId: defaultJoinAsPeerId) + }))) + + items.append(.action(ContextMenuActionItem(text: scheduleVoiceChatTitle, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Schedule"), color: theme.contextMenu.primaryColor) }, action: { _, f in + f(.dismissWithoutContent) + + self?.scheduleGroupCall() + }))) + + var credentialsPromise: Promise? + var canCreateStream = false + switch chatPeer { + case let group as TelegramGroup: + if case .creator = group.role { + canCreateStream = true + } + case let channel as TelegramChannel: + if channel.hasPermission(.manageCalls) { + canCreateStream = true + credentialsPromise = Promise() + credentialsPromise?.set(context.engine.calls.getGroupCallStreamCredentials(peerId: peerId, isLiveStream: false, revokePreviousCredentials: false) |> `catch` { _ -> Signal in return .never() }) + } + default: + break + } + + if canCreateStream { + items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.ChannelInfo_CreateExternalStream, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/VoiceChat"), color: theme.contextMenu.primaryColor) }, action: { _, f in + f(.dismissWithoutContent) + + self?.createExternalStream(credentialsPromise: credentialsPromise) + }))) + } + + if let contextController = contextController { + contextController.setItems(.single(ContextController.Items(content: .list(items))), minHeight: nil, animated: true) + } else { + strongSelf.state = strongSelf.state.withHighlightedButton(.voiceChat) + if let (layout, navigationHeight) = strongSelf.validLayout { + strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) + } + + if let sourceNode = strongSelf.headerNode.buttonNodes[.voiceChat]?.referenceNode, let controller = strongSelf.controller { + let contextController = ContextController(presentationData: strongSelf.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + contextController.dismissed = { [weak self] in + if let strongSelf = self { + strongSelf.state = strongSelf.state.withHighlightedButton(nil) + if let (layout, navigationHeight) = strongSelf.validLayout { + strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) + } + } + } + controller.presentInGlobalOverlay(contextController) + } + } + }) + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenDisplayGiftsContextMenu.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenDisplayGiftsContextMenu.swift new file mode 100644 index 00000000..6d1d859c --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenDisplayGiftsContextMenu.swift @@ -0,0 +1,258 @@ +import Foundation +import UIKit +import Display +import AccountContext +import SwiftSignalKit +import Postbox +import TelegramCore +import ContextUI +import PeerInfoVisualMediaPaneNode + +extension PeerInfoScreenNode { + func displayGiftsContextMenu(source: ContextReferenceContentNode, gesture: ContextGesture?) { + guard let currentPaneKey = self.paneContainerNode.currentPaneKey, case .gifts = currentPaneKey else { + return + } + guard let pane = self.paneContainerNode.currentPane?.node as? PeerInfoGiftsPaneNode else { + return + } + guard let controller = self.controller else { + return + } + guard let data = self.data else { + return + } + + let giftsContext = pane.giftsContext + + var hasVisibility = false + if let channel = data.peer as? TelegramChannel, channel.hasPermission(.sendSomething) { + hasVisibility = true + } else if data.peer?.id == self.context.account.peerId { + hasVisibility = true + } + + let isCollection = giftsContext.collectionId != nil + + let strings = self.presentationData.strings + let items: Signal = giftsContext.state + |> map { state in + var hasPinnedGifts = false + for gift in state.gifts { + if gift.pinnedToTop { + hasPinnedGifts = true + break + } + } + return (state.filter, state.sorting, hasPinnedGifts || isCollection) + } + |> distinctUntilChanged(isEqual: { lhs, rhs -> Bool in + let filterEquals = lhs.0 == rhs.0 + let sortingEquals = lhs.1 == rhs.1 + let canReorderEquals = lhs.2 == rhs.2 + return filterEquals && sortingEquals && canReorderEquals + }) + |> map { [weak pane, weak giftsContext] filter, sorting, canReorder -> ContextController.Items in + var items: [ContextMenuItem] = [] + + if hasVisibility { + if let pane, case .all = pane.currentCollection { + items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_AddCollection, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Peer Info/Gifts/AddCollection"), color: theme.contextMenu.primaryColor) + }, action: { [weak pane] _, f in + f(.default) + + if let pane { + pane.createCollection() + } + }))) + } else { + items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_AddGifts, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Peer Info/Gifts/AddGift"), color: theme.contextMenu.primaryColor) + }, action: { [weak pane] _, f in + f(.default) + + if let pane, case let .collection(id) = pane.currentCollection { + pane.addGiftsToCollection(id: id) + } + }))) + } + + if canReorder { + items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Reorder, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.contextMenu.primaryColor) + }, action: { [weak pane] _, f in + f(.default) + + if let pane { + pane.beginReordering() + } + }))) + } + + if let pane, case let .collection(id) = pane.currentCollection { + items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_DeleteCollection, textColor: .destructive, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) + }, action: { [weak pane] _, f in + f(.default) + + if let pane { + pane.deleteCollection(id: id) + } + }))) + } + } + + if let pane, case let .collection(id) = pane.currentCollection, let addressName = data.peer?.addressName, !addressName.isEmpty { + let shareAction: ContextMenuItem = .action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_ShareCollection, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.default) + self?.openShareLink(url: "https://t.me/\(addressName)/c/\(id)") + })) + if items.isEmpty { + items.append(shareAction) + } else { + items.insert(shareAction, at: 1) + } + } + + if !items.isEmpty { + items.append(.separator) + } + + if let pane, case .all = pane.currentCollection { + items.append(.action(ContextMenuActionItem(text: sorting == .date ? strings.PeerInfo_Gifts_SortByValue : strings.PeerInfo_Gifts_SortByDate, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: sorting == .date ? "Peer Info/SortValue" : "Peer Info/SortDate"), color: theme.contextMenu.primaryColor) + }, action: { [weak giftsContext] _, f in + f(.default) + + giftsContext?.updateSorting(sorting == .date ? .value : .date) + }))) + + items.append(.separator) + } + + let toggleFilter: (ProfileGiftsContext.Filters) -> Void = { [weak giftsContext] value in + var updatedFilter = filter + if updatedFilter.contains(value) { + updatedFilter.remove(value) + } else { + updatedFilter.insert(value) + } + if !updatedFilter.contains(.unlimited) && !updatedFilter.contains(.limitedUpgradable) && !updatedFilter.contains(.limitedNonUpgradable) && !updatedFilter.contains(.unique) { + updatedFilter.insert(.unlimited) + } + if !updatedFilter.contains(.displayed) && !updatedFilter.contains(.hidden) { + if value == .displayed { + updatedFilter.insert(.hidden) + } else { + updatedFilter.insert(.displayed) + } + } + giftsContext?.updateFilter(updatedFilter) + } + + let switchToFilter: (ProfileGiftsContext.Filters) -> Void = { [weak giftsContext] value in + var updatedFilter = filter + updatedFilter.remove(.unlimited) + updatedFilter.remove(.limitedUpgradable) + updatedFilter.remove(.limitedNonUpgradable) + updatedFilter.remove(.unique) + updatedFilter.insert(value) + giftsContext?.updateFilter(updatedFilter) + } + + let switchToVisiblityFilter: (ProfileGiftsContext.Filters) -> Void = { [weak giftsContext] value in + var updatedFilter = filter + updatedFilter.remove(.hidden) + updatedFilter.remove(.displayed) + updatedFilter.insert(value) + giftsContext?.updateFilter(updatedFilter) + } + + items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Unlimited, icon: { theme in + return filter.contains(.unlimited) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + }, action: { _, f in + toggleFilter(.unlimited) + }, longPressAction: { _, f in + switchToFilter(.unlimited) + }))) + items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Limited, icon: { theme in + return filter.contains(.limitedNonUpgradable) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + }, action: { _, f in + toggleFilter(.limitedNonUpgradable) + }, longPressAction: { _, f in + switchToFilter(.limitedNonUpgradable) + }))) + items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Upgradable, icon: { theme in + return filter.contains(.limitedUpgradable) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + }, action: { _, f in + toggleFilter(.limitedUpgradable) + }, longPressAction: { _, f in + switchToFilter(.limitedUpgradable) + }))) + items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Unique, icon: { theme in + return filter.contains(.unique) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + }, action: { _, f in + toggleFilter(.unique) + }, longPressAction: { _, f in + switchToFilter(.unique) + }))) + + if hasVisibility { + items.append(.separator) + + items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Displayed, icon: { theme in + return filter.contains(.displayed) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + }, action: { _, f in + toggleFilter(.displayed) + }, longPressAction: { _, f in + switchToVisiblityFilter(.displayed) + }))) + items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Hidden, icon: { theme in + return filter.contains(.hidden) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil + }, action: { _, f in + toggleFilter(.hidden) + }, longPressAction: { _, f in + switchToVisiblityFilter(.hidden) + }))) + } + + return ContextController.Items(content: .list(items)) + } + + let contextController = ContextController(presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: source)), items: items, gesture: gesture) + contextController.passthroughTouchEvent = { [weak self] sourceView, point in + guard let strongSelf = self else { + return .ignore + } + + let localPoint = strongSelf.view.convert(sourceView.convert(point, to: nil), from: nil) + guard let localResult = strongSelf.hitTest(localPoint, with: nil) else { + return .dismiss(consume: true, result: nil) + } + + var testView: UIView? = localResult + while true { + if let testViewValue = testView { + if let node = testViewValue.asyncdisplaykit_node as? PeerInfoHeaderNavigationButton { + node.isUserInteractionEnabled = false + DispatchQueue.main.async { + node.isUserInteractionEnabled = true + } + return .dismiss(consume: false, result: nil) + } else { + testView = testViewValue.superview + } + } else { + break + } + } + + return .dismiss(consume: true, result: nil) + } + self.mediaGalleryContextMenu = contextController + controller.presentInGlobalOverlay(contextController) + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenDisplayMediaGalleryContextMenu.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenDisplayMediaGalleryContextMenu.swift new file mode 100644 index 00000000..280fd113 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenDisplayMediaGalleryContextMenu.swift @@ -0,0 +1,452 @@ +import Foundation +import UIKit +import Display +import AccountContext +import SwiftSignalKit +import Postbox +import TelegramCore +import ContextUI +import PeerInfoVisualMediaPaneNode + +extension PeerInfoScreenNode { + func displayMediaGalleryContextMenu(source: ContextReferenceContentNode, gesture: ContextGesture?) { + let peerId = self.peerId + + var isBotPreviewOrStories = false + if let currentPaneKey = self.paneContainerNode.currentPaneKey { + if case .botPreview = currentPaneKey { + isBotPreviewOrStories = true + } else if case .stories = currentPaneKey { + isBotPreviewOrStories = true + } + } + + if isBotPreviewOrStories { + guard let controller = self.controller else { + return + } + guard let pane = self.paneContainerNode.currentPane?.node as? PeerInfoStoryPaneNode else { + return + } + + if case .botPreview = pane.scope { + guard let data = self.data, let user = data.peer as? TelegramUser, let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) else { + return + } + + var items: [ContextMenuItem] = [] + + let strings = self.presentationData.strings + + var ignoreNextActions = false + + if pane.canAddMoreBotPreviews() { + items.append(.action(ContextMenuActionItem(text: strings.BotPreviews_MenuAddPreview, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Add"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, a in + if ignoreNextActions { + return + } + ignoreNextActions = true + a(.default) + + if let self { + self.headerNode.navigationButtonContainer.performAction?(.postStory, nil, nil) + } + }))) + } + + if pane.canReorder() { + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.BotPreviews_MenuReorder, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.contextMenu.primaryColor) + }, action: { [weak pane] _, a in + if ignoreNextActions { + return + } + ignoreNextActions = true + a(.default) + + if let pane { + pane.beginReordering() + } + }))) + } + + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Conversation_ContextMenuSelect, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Select"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, a in + if ignoreNextActions { + return + } + ignoreNextActions = true + a(.default) + + if let self { + self.toggleStorySelection(ids: [], isSelected: true) + } + }))) + + if let language = pane.currentBotPreviewLanguage { + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.BotPreviews_MenuDeleteLanguage(language.name).string, textColor: .destructive, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) + }, action: { [weak pane] _, a in + if ignoreNextActions { + return + } + ignoreNextActions = true + a(.default) + + if let pane { + pane.presentDeleteBotPreviewLanguage() + } + }))) + } + + let contextController = ContextController(presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: source)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + contextController.passthroughTouchEvent = { [weak self] sourceView, point in + guard let strongSelf = self else { + return .ignore + } + + let localPoint = strongSelf.view.convert(sourceView.convert(point, to: nil), from: nil) + guard let localResult = strongSelf.hitTest(localPoint, with: nil) else { + return .dismiss(consume: true, result: nil) + } + + var testView: UIView? = localResult + while true { + if let testViewValue = testView { + if let node = testViewValue.asyncdisplaykit_node as? PeerInfoHeaderNavigationButton { + node.isUserInteractionEnabled = false + DispatchQueue.main.async { + node.isUserInteractionEnabled = true + } + return .dismiss(consume: false, result: nil) + } else if let node = testViewValue.asyncdisplaykit_node as? PeerInfoVisualMediaPaneNode { + node.brieflyDisableTouchActions() + return .dismiss(consume: false, result: nil) + } else if let node = testViewValue.asyncdisplaykit_node as? PeerInfoStoryPaneNode { + node.brieflyDisableTouchActions() + return .dismiss(consume: false, result: nil) + } else { + testView = testViewValue.superview + } + } else { + break + } + } + + return .dismiss(consume: true, result: nil) + } + self.mediaGalleryContextMenu = contextController + controller.presentInGlobalOverlay(contextController) + } else if case .peer = pane.scope { + guard let data = self.data, let user = data.peer as? TelegramUser else { + return + } + let _ = user + + var items: [ContextMenuItem] = [] + + let strings = self.presentationData.strings + + var ignoreNextActions = false + + items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_MenuAddStories, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat List/AddStoryIcon"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, a in + if ignoreNextActions { + return + } + ignoreNextActions = true + a(.default) + + if let self { + self.headerNode.navigationButtonContainer.performAction?(.postStory, nil, nil) + } + }))) + + if let _ = pane.currentStoryFolder { + items.append(.action(ContextMenuActionItem(text: strings.Conversation_ContextMenuShare, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) + }, action: { [weak pane] _, a in + if ignoreNextActions { + return + } + ignoreNextActions = true + a(.default) + + if let pane { + pane.shareCurrentFolder() + } + }))) + } + + if pane.canReorder() { + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.BotPreviews_MenuReorder, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.contextMenu.primaryColor) + }, action: { [weak pane] _, a in + if ignoreNextActions { + return + } + ignoreNextActions = true + a(.default) + + if let pane { + pane.beginReordering() + } + }))) + } + + if let folder = pane.currentStoryFolder { + let _ = folder + + items.append(.action(ContextMenuActionItem(text: strings.Stories_MenuDeleteAlbum, textColor: .destructive, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) + }, action: { [weak pane] _, a in + if ignoreNextActions { + return + } + ignoreNextActions = true + a(.default) + + if let pane { + pane.presentDeleteCurrentStoryFolder() + } + }))) + } + + if let language = pane.currentBotPreviewLanguage { + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.BotPreviews_MenuDeleteLanguage(language.name).string, textColor: .destructive, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) + }, action: { [weak pane] _, a in + if ignoreNextActions { + return + } + ignoreNextActions = true + a(.default) + + if let pane { + pane.presentDeleteBotPreviewLanguage() + } + }))) + } + + let contextController = ContextController(presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: source)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + contextController.passthroughTouchEvent = { [weak self] sourceView, point in + guard let strongSelf = self else { + return .ignore + } + + let localPoint = strongSelf.view.convert(sourceView.convert(point, to: nil), from: nil) + guard let localResult = strongSelf.hitTest(localPoint, with: nil) else { + return .dismiss(consume: true, result: nil) + } + + var testView: UIView? = localResult + while true { + if let testViewValue = testView { + if let node = testViewValue.asyncdisplaykit_node as? PeerInfoHeaderNavigationButton { + node.isUserInteractionEnabled = false + DispatchQueue.main.async { + node.isUserInteractionEnabled = true + } + return .dismiss(consume: false, result: nil) + } else if let node = testViewValue.asyncdisplaykit_node as? PeerInfoVisualMediaPaneNode { + node.brieflyDisableTouchActions() + return .dismiss(consume: false, result: nil) + } else if let node = testViewValue.asyncdisplaykit_node as? PeerInfoStoryPaneNode { + node.brieflyDisableTouchActions() + return .dismiss(consume: false, result: nil) + } else { + testView = testViewValue.superview + } + } else { + break + } + } + + return .dismiss(consume: true, result: nil) + } + self.mediaGalleryContextMenu = contextController + controller.presentInGlobalOverlay(contextController) + } + } else { + let _ = (self.context.engine.data.get(EngineDataMap([ + TelegramEngine.EngineData.Item.Messages.MessageCount(peerId: peerId, threadId: self.chatLocation.threadId, tag: .photo), + TelegramEngine.EngineData.Item.Messages.MessageCount(peerId: peerId, threadId: self.chatLocation.threadId, tag: .video) + ])) + |> deliverOnMainQueue).startStandalone(next: { [weak self] messageCounts in + guard let strongSelf = self else { + return + } + + var mediaCount: [MessageTags: Int32] = [:] + for (key, count) in messageCounts { + mediaCount[key.tag] = count.flatMap(Int32.init) ?? 0 + } + + let photoCount: Int32 = mediaCount[.photo] ?? 0 + let videoCount: Int32 = mediaCount[.video] ?? 0 + + guard let controller = strongSelf.controller else { + return + } + guard let pane = strongSelf.paneContainerNode.currentPane?.node as? PeerInfoVisualMediaPaneNode else { + return + } + + var items: [ContextMenuItem] = [] + + let strings = strongSelf.presentationData.strings + + var recurseGenerateAction: ((Bool) -> ContextMenuActionItem)? + let generateAction: (Bool) -> ContextMenuActionItem = { [weak pane] isZoomIn in + let nextZoomLevel = isZoomIn ? pane?.availableZoomLevels().increment : pane?.availableZoomLevels().decrement + let canZoom: Bool = nextZoomLevel != nil + + return ContextMenuActionItem(id: isZoomIn ? 0 : 1, text: isZoomIn ? strings.SharedMedia_ZoomIn : strings.SharedMedia_ZoomOut, textColor: canZoom ? .primary : .disabled, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: isZoomIn ? "Chat/Context Menu/ZoomIn" : "Chat/Context Menu/ZoomOut"), color: canZoom ? theme.contextMenu.primaryColor : theme.contextMenu.primaryColor.withMultipliedAlpha(0.4)) + }, action: canZoom ? { action in + guard let pane = pane, let zoomLevel = isZoomIn ? pane.availableZoomLevels().increment : pane.availableZoomLevels().decrement else { + return + } + pane.updateZoomLevel(level: zoomLevel) + if let recurseGenerateAction = recurseGenerateAction { + action.updateAction(0, recurseGenerateAction(true)) + action.updateAction(1, recurseGenerateAction(false)) + } + } : nil) + } + recurseGenerateAction = { isZoomIn in + return generateAction(isZoomIn) + } + + items.append(.action(generateAction(true))) + items.append(.action(generateAction(false))) + + var ignoreNextActions = false + if strongSelf.chatLocation.threadId == nil { + items.append(.action(ContextMenuActionItem(text: strings.SharedMedia_ShowCalendar, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Calendar"), color: theme.contextMenu.primaryColor) + }, action: { _, a in + if ignoreNextActions { + return + } + ignoreNextActions = true + a(.default) + + self?.openMediaCalendar() + }))) + } + + if photoCount != 0 && videoCount != 0 { + items.append(.separator) + + let showPhotos: Bool + switch pane.contentType { + case .photo, .photoOrVideo: + showPhotos = true + default: + showPhotos = false + } + let showVideos: Bool + switch pane.contentType { + case .video, .photoOrVideo: + showVideos = true + default: + showVideos = false + } + + items.append(.action(ContextMenuActionItem(text: strings.SharedMedia_ShowPhotos, icon: { theme in + if !showPhotos { + return nil + } + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) + }, action: { [weak pane] _, a in + a(.default) + + guard let pane = pane else { + return + } + let updatedContentType: PeerInfoVisualMediaPaneNode.ContentType + switch pane.contentType { + case .photoOrVideo: + updatedContentType = .video + case .photo: + updatedContentType = .photo + case .video: + updatedContentType = .photoOrVideo + default: + updatedContentType = pane.contentType + } + pane.updateContentType(contentType: updatedContentType) + }))) + items.append(.action(ContextMenuActionItem(text: strings.SharedMedia_ShowVideos, icon: { theme in + if !showVideos { + return nil + } + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) + }, action: { [weak pane] _, a in + a(.default) + + guard let pane = pane else { + return + } + let updatedContentType: PeerInfoVisualMediaPaneNode.ContentType + switch pane.contentType { + case .photoOrVideo: + updatedContentType = .photo + case .photo: + updatedContentType = .photoOrVideo + case .video: + updatedContentType = .video + default: + updatedContentType = pane.contentType + } + pane.updateContentType(contentType: updatedContentType) + }))) + } + + let contextController = ContextController(presentationData: strongSelf.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: source)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) + contextController.passthroughTouchEvent = { sourceView, point in + guard let strongSelf = self else { + return .ignore + } + + let localPoint = strongSelf.view.convert(sourceView.convert(point, to: nil), from: nil) + guard let localResult = strongSelf.hitTest(localPoint, with: nil) else { + return .dismiss(consume: true, result: nil) + } + + var testView: UIView? = localResult + while true { + if let testViewValue = testView { + if let node = testViewValue.asyncdisplaykit_node as? PeerInfoHeaderNavigationButton { + node.isUserInteractionEnabled = false + DispatchQueue.main.async { + node.isUserInteractionEnabled = true + } + return .dismiss(consume: false, result: nil) + } else if let node = testViewValue.asyncdisplaykit_node as? PeerInfoVisualMediaPaneNode { + node.brieflyDisableTouchActions() + return .dismiss(consume: false, result: nil) + } else if let node = testViewValue.asyncdisplaykit_node as? PeerInfoStoryPaneNode { + node.brieflyDisableTouchActions() + return .dismiss(consume: false, result: nil) + } else { + testView = testViewValue.superview + } + } else { + break + } + } + + return .dismiss(consume: true, result: nil) + } + strongSelf.mediaGalleryContextMenu = contextController + controller.presentInGlobalOverlay(contextController) + }) + } + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenItem.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenItem.swift new file mode 100644 index 00000000..f4d1a0af --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenItem.swift @@ -0,0 +1,23 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import AccountContext +import TelegramPresentationData + +protocol PeerInfoScreenItem: AnyObject { + var id: AnyHashable { get } + func node() -> PeerInfoScreenItemNode +} + +class PeerInfoScreenItemNode: ASDisplayNode, AccessibilityFocusableNode { + var bringToFrontForHighlight: (() -> Void)? + + func update(context: AccountContext, width: CGFloat, safeInsets: UIEdgeInsets, presentationData: PresentationData, item: PeerInfoScreenItem, topItem: PeerInfoScreenItem?, bottomItem: PeerInfoScreenItem?, hasCorners: Bool, transition: ContainedViewLayoutTransition) -> CGFloat { + preconditionFailure() + } + + override open func accessibilityElementDidBecomeFocused() { +// (self.supernode as? ListView)?.ensureItemNodeVisible(self, animated: false, overflow: 22.0) + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenItemSectionContainerNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenItemSectionContainerNode.swift new file mode 100644 index 00000000..7a821b3c --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenItemSectionContainerNode.swift @@ -0,0 +1,148 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import AccountContext +import TelegramPresentationData + +final class PeerInfoScreenItemSectionContainerNode: ASDisplayNode { + private let backgroundNode: ASDisplayNode + private let topSeparatorNode: ASDisplayNode + private let bottomSeparatorNode: ASDisplayNode + private let itemContainerNode: ASDisplayNode + + private var currentItems: [PeerInfoScreenItem] = [] + var itemNodes: [AnyHashable: PeerInfoScreenItemNode] = [:] + + override init() { + self.backgroundNode = ASDisplayNode() + self.backgroundNode.isLayerBacked = true + + self.topSeparatorNode = ASDisplayNode() + self.topSeparatorNode.isLayerBacked = true + + self.bottomSeparatorNode = ASDisplayNode() + self.bottomSeparatorNode.isLayerBacked = true + + self.itemContainerNode = ASDisplayNode() + self.itemContainerNode.clipsToBounds = true + + super.init() + + self.addSubnode(self.backgroundNode) + self.addSubnode(self.itemContainerNode) + self.addSubnode(self.topSeparatorNode) + self.addSubnode(self.bottomSeparatorNode) + } + + func update(context: AccountContext, width: CGFloat, safeInsets: UIEdgeInsets, hasCorners: Bool, presentationData: PresentationData, items: [PeerInfoScreenItem], transition: ContainedViewLayoutTransition) -> CGFloat { + self.backgroundNode.backgroundColor = presentationData.theme.list.itemBlocksBackgroundColor + self.topSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor + self.bottomSeparatorNode.backgroundColor = presentationData.theme.list.itemBlocksSeparatorColor + + self.topSeparatorNode.isHidden = hasCorners + self.bottomSeparatorNode.isHidden = hasCorners + + var contentHeight: CGFloat = 0.0 + var contentWithBackgroundHeight: CGFloat = 0.0 + var contentWithBackgroundOffset: CGFloat = 0.0 + + for i in 0 ..< items.count { + let item = items[i] + + let itemNode: PeerInfoScreenItemNode + var wasAdded = false + if let current = self.itemNodes[item.id] { + itemNode = current + } else { + wasAdded = true + itemNode = item.node() + self.itemNodes[item.id] = itemNode + self.itemContainerNode.addSubnode(itemNode) + itemNode.bringToFrontForHighlight = { [weak self, weak itemNode] in + guard let strongSelf = self, let itemNode = itemNode else { + return + } + strongSelf.view.bringSubviewToFront(itemNode.view) + } + } + + let itemTransition: ContainedViewLayoutTransition = wasAdded ? .immediate : transition + + let topItem: PeerInfoScreenItem? + if i == 0 { + topItem = nil + } else if items[i - 1] is PeerInfoScreenHeaderItem { + topItem = nil + } else { + topItem = items[i - 1] + } + + let bottomItem: PeerInfoScreenItem? + if i == items.count - 1 { + bottomItem = nil + } else if items[i + 1] is PeerInfoScreenCommentItem { + bottomItem = nil + } else { + bottomItem = items[i + 1] + } + + let itemHeight = itemNode.update(context: context, width: width, safeInsets: safeInsets, presentationData: presentationData, item: item, topItem: topItem, bottomItem: bottomItem, hasCorners: hasCorners, transition: itemTransition) + let itemFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: width, height: itemHeight)) + itemTransition.updateFrame(node: itemNode, frame: itemFrame) + if wasAdded { + itemNode.alpha = 0.0 + let alphaTransition: ContainedViewLayoutTransition = transition.isAnimated ? .animated(duration: 0.35, curve: .linear) : .immediate + alphaTransition.updateAlpha(node: itemNode, alpha: 1.0) + } + + if item is PeerInfoScreenCommentItem { + } else { + contentWithBackgroundHeight += itemHeight + } + contentHeight += itemHeight + + if item is PeerInfoScreenHeaderItem { + contentWithBackgroundOffset = contentHeight + } + } + + var removeIds: [AnyHashable] = [] + for (id, _) in self.itemNodes { + if !items.contains(where: { $0.id == id }) { + removeIds.append(id) + } + } + for id in removeIds { + if let itemNode = self.itemNodes.removeValue(forKey: id) { + itemNode.view.superview?.sendSubviewToBack(itemNode.view) + transition.updateAlpha(node: itemNode, alpha: 0.0, completion: { [weak itemNode] _ in + itemNode?.removeFromSupernode() + }) + } + } + + transition.updateFrame(node: self.itemContainerNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: width, height: contentHeight))) + transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: contentWithBackgroundOffset), size: CGSize(width: width, height: max(0.0, contentWithBackgroundHeight - contentWithBackgroundOffset)))) + transition.updateFrame(node: self.topSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: contentWithBackgroundOffset - UIScreenPixel), size: CGSize(width: width, height: UIScreenPixel))) + transition.updateFrame(node: self.bottomSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: contentWithBackgroundHeight), size: CGSize(width: width, height: UIScreenPixel))) + + if contentHeight.isZero { + transition.updateAlpha(node: self.topSeparatorNode, alpha: 0.0) + transition.updateAlpha(node: self.bottomSeparatorNode, alpha: 0.0) + } else { + transition.updateAlpha(node: self.topSeparatorNode, alpha: 1.0) + transition.updateAlpha(node: self.bottomSeparatorNode, alpha: 1.0) + } + + return contentHeight + } + + func animateErrorIfNeeded() { + for (_, itemNode) in self.itemNodes { + if let itemNode = itemNode as? PeerInfoScreenMultilineInputItemNode { + itemNode.animateErrorIfNeeded() + } + } + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenMessageActions.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenMessageActions.swift new file mode 100644 index 00000000..54143e7d --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenMessageActions.swift @@ -0,0 +1,282 @@ +import Foundation +import UIKit +import Display +import AccountContext +import SwiftSignalKit +import Postbox +import TelegramCore +import TextFormat +import UndoUI +import ChatInterfaceState + +extension PeerInfoScreenNode { + func deleteMessages(messageIds: Set?) { + if let messageIds = messageIds ?? self.state.selectedMessageIds, !messageIds.isEmpty { + self.activeActionDisposable.set((self.context.sharedContext.chatAvailableMessageActions(engine: self.context.engine, accountPeerId: self.context.account.peerId, messageIds: messageIds, keepUpdated: false) + |> deliverOnMainQueue).startStrict(next: { [weak self] actions in + if let strongSelf = self, let peer = strongSelf.data?.peer, !actions.options.isEmpty { + let actionSheet = ActionSheetController(presentationData: strongSelf.presentationData) + var items: [ActionSheetItem] = [] + var personalPeerName: String? + var isChannel = false + if let user = peer as? TelegramUser { + personalPeerName = EnginePeer(user).compactDisplayTitle + } else if let channel = peer as? TelegramChannel, case .broadcast = channel.info { + isChannel = true + } + + if actions.options.contains(.deleteGlobally) { + let globalTitle: String + if isChannel { + globalTitle = strongSelf.presentationData.strings.Conversation_DeleteMessagesForMe + } else if let personalPeerName = personalPeerName { + globalTitle = strongSelf.presentationData.strings.Conversation_DeleteMessagesFor(personalPeerName).string + } else { + globalTitle = strongSelf.presentationData.strings.Conversation_DeleteMessagesForEveryone + } + items.append(ActionSheetButtonItem(title: globalTitle, color: .destructive, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + if let strongSelf = self { + strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone, nil, nil) + let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forEveryone).startStandalone() + } + })) + } + if actions.options.contains(.deleteLocally) { + var localOptionText = strongSelf.presentationData.strings.Conversation_DeleteMessagesForMe + if strongSelf.context.account.peerId == strongSelf.peerId { + if messageIds.count == 1 { + localOptionText = strongSelf.presentationData.strings.Conversation_Moderate_Delete + } else { + localOptionText = strongSelf.presentationData.strings.Conversation_DeleteManyMessages + } + } + items.append(ActionSheetButtonItem(title: localOptionText, color: .destructive, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + if let strongSelf = self { + strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone, nil, nil) + let _ = strongSelf.context.engine.messages.deleteMessagesInteractively(messageIds: Array(messageIds), type: .forLocalPeer).startStandalone() + } + })) + } + actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ + ActionSheetButtonItem(title: strongSelf.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in + actionSheet?.dismissAnimated() + }) + ])]) + strongSelf.view.endEditing(true) + strongSelf.controller?.present(actionSheet, in: .window(.root)) + } + })) + } + } + + func forwardMessages(messageIds: Set?) { + if let messageIds = messageIds ?? self.state.selectedMessageIds, !messageIds.isEmpty { + let peerSelectionController = self.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, filter: [.onlyWriteable, .excludeDisabled], hasFilters: true, multipleSelection: true, selectForumThreads: true)) + peerSelectionController.multiplePeersSelected = { [weak self, weak peerSelectionController] peers, peerMap, messageText, mode, forwardOptions, _ in + guard let strongSelf = self, let strongController = peerSelectionController else { + return + } + strongController.dismiss() + + var result: [EnqueueMessage] = [] + if messageText.string.count > 0 { + let inputText = convertMarkdownToAttributes(messageText) + for text in breakChatInputText(trimChatInputText(inputText)) { + if text.length != 0 { + var attributes: [MessageAttribute] = [] + let entities = generateTextEntities(text.string, enabledTypes: .all, currentEntities: generateChatInputTextEntities(text)) + if !entities.isEmpty { + attributes.append(TextEntitiesMessageAttribute(entities: entities)) + } + result.append(.message(text: text.string, attributes: attributes, inlineStickers: [:], mediaReference: nil, threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + } + } + } + + var attributes: [MessageAttribute] = [] + attributes.append(ForwardOptionsMessageAttribute(hideNames: forwardOptions?.hideNames == true, hideCaptions: forwardOptions?.hideCaptions == true)) + + result.append(contentsOf: messageIds.map { messageId -> EnqueueMessage in + return .forward(source: messageId, threadId: nil, grouping: .auto, attributes: attributes, correlationId: nil) + }) + + var displayPeers: [EnginePeer] = [] + for peer in peers { + let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peer.id, messages: result) + |> deliverOnMainQueue).startStandalone(next: { messageIds in + if let strongSelf = self { + let signals: [Signal] = messageIds.compactMap({ id -> Signal? in + guard let id = id else { + return nil + } + return strongSelf.context.account.pendingMessageManager.pendingMessageStatus(id) + |> mapToSignal { status, _ -> Signal in + if status != nil { + return .never() + } else { + return .single(true) + } + } + |> take(1) + }) + if strongSelf.shareStatusDisposable == nil { + strongSelf.shareStatusDisposable = MetaDisposable() + } + strongSelf.shareStatusDisposable?.set((combineLatest(signals) + |> deliverOnMainQueue).startStrict()) + } + }) + + if case let .secretChat(secretPeer) = peer { + if let peer = peerMap[secretPeer.regularPeerId] { + displayPeers.append(peer) + } + } else { + displayPeers.append(peer) + } + } + + let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } + let text: String + var savedMessages = false + if displayPeers.count == 1, let peerId = displayPeers.first?.id, peerId == strongSelf.context.account.peerId { + text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many + savedMessages = true + } else { + if displayPeers.count == 1, let peer = displayPeers.first { + var peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + peerName = peerName.replacingOccurrences(of: "**", with: "") + text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_Chat_One(peerName).string : presentationData.strings.Conversation_ForwardTooltip_Chat_Many(peerName).string + } else if displayPeers.count == 2, let firstPeer = displayPeers.first, let secondPeer = displayPeers.last { + var firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + firstPeerName = firstPeerName.replacingOccurrences(of: "**", with: "") + var secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + secondPeerName = secondPeerName.replacingOccurrences(of: "**", with: "") + text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string : presentationData.strings.Conversation_ForwardTooltip_TwoChats_Many(firstPeerName, secondPeerName).string + } else if let peer = displayPeers.first { + var peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + peerName = peerName.replacingOccurrences(of: "**", with: "") + text = messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_ManyChats_One(peerName, "\(displayPeers.count - 1)").string : presentationData.strings.Conversation_ForwardTooltip_ManyChats_Many(peerName, "\(displayPeers.count - 1)").string + } else { + text = "" + } + } + + strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in + if savedMessages, let self, action == .info { + let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) + |> deliverOnMainQueue).start(next: { [weak self] peer in + guard let self, let peer else { + return + } + guard let navigationController = self.controller?.navigationController as? NavigationController else { + return + } + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), forceOpenChat: true)) + }) + } + return false + }), in: .current) + } + peerSelectionController.peerSelected = { [weak self, weak peerSelectionController] peer, threadId in + let peerId = peer.id + + if let strongSelf = self, let _ = peerSelectionController { + if peerId == strongSelf.context.account.peerId { + Queue.mainQueue().after(0.88) { + strongSelf.hapticFeedback.success() + } + + let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } + strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: true, text: messageIds.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many), elevatedLayout: false, animateInAsReplacement: true, action: { action in + if let self, action == .info { + let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) + |> deliverOnMainQueue).start(next: { [weak self] peer in + guard let self, let peer else { + return + } + guard let navigationController = self.controller?.navigationController as? NavigationController else { + return + } + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer))) + }) + } + return false + }), in: .current) + + strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone, nil, nil) + + let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: messageIds.map { id -> EnqueueMessage in + return .forward(source: id, threadId: nil, grouping: .auto, attributes: [], correlationId: nil) + }) + |> deliverOnMainQueue).startStandalone(next: { [weak self] messageIds in + if let strongSelf = self { + let signals: [Signal] = messageIds.compactMap({ id -> Signal? in + guard let id = id else { + return nil + } + return strongSelf.context.account.pendingMessageManager.pendingMessageStatus(id) + |> mapToSignal { status, _ -> Signal in + if status != nil { + return .never() + } else { + return .single(true) + } + } + |> take(1) + }) + strongSelf.activeActionDisposable.set((combineLatest(signals) + |> deliverOnMainQueue).startStrict()) + } + }) + if let peerSelectionController = peerSelectionController { + peerSelectionController.dismiss() + } + } else { + let _ = (ChatInterfaceState.update(engine: strongSelf.context.engine, peerId: peerId, threadId: threadId, { currentState in + return currentState.withUpdatedForwardMessageIds(Array(messageIds)) + }) + |> deliverOnMainQueue).startStandalone(completed: { + if let strongSelf = self { + let proceed: (ChatController) -> Void = { chatController in + strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone, nil, nil) + + if let navigationController = strongSelf.controller?.navigationController as? NavigationController { + var viewControllers = navigationController.viewControllers + if threadId != nil { + viewControllers.insert(chatController, at: viewControllers.count - 2) + } else { + viewControllers.insert(chatController, at: viewControllers.count - 1) + } + navigationController.setViewControllers(viewControllers, animated: false) + + strongSelf.activeActionDisposable.set((chatController.ready.get() + |> filter { $0 } + |> take(1) + |> deliverOnMainQueue).startStrict(next: { [weak navigationController] _ in + viewControllers.removeAll(where: { $0 is PeerSelectionController }) + navigationController?.setViewControllers(viewControllers, animated: true) + })) + } + } + + if let threadId = threadId { + let _ = (strongSelf.context.sharedContext.chatControllerForForumThread(context: strongSelf.context, peerId: peerId, threadId: threadId) + |> deliverOnMainQueue).startStandalone(next: { chatController in + proceed(chatController) + }) + } else { + let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: .none, botStart: nil, mode: .standard(.default), params: nil) + proceed(chatController) + } + } + }) + } + } + } + self.controller?.push(peerSelectionController) + } + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenBio.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenBio.swift new file mode 100644 index 00000000..a273ef43 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenBio.swift @@ -0,0 +1,129 @@ +import Foundation +import UIKit +import Display +import AccountContext +import SwiftSignalKit +import Postbox +import TelegramCore +import AsyncDisplayKit +import TelegramUIPreferences +import ContextUI +import TranslateUI +import UndoUI + +extension PeerInfoScreenNode { + func openBioContextMenu(node: ASDisplayNode, gesture: ContextGesture?) { + let _ = (self.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.translationSettings]) + |> take(1) + |> deliverOnMainQueue).startStandalone(next: { [weak self] sharedData in + guard let self else { + return + } + + let translationSettings: TranslationSettings + if let current = sharedData.entries[ApplicationSpecificSharedDataKeys.translationSettings]?.get(TranslationSettings.self) { + translationSettings = current + } else { + translationSettings = TranslationSettings.defaultSettings + } + + guard let sourceNode = node as? ContextExtractedContentContainingNode else { + return + } + guard let cachedData = self.data?.cachedData else { + return + } + + var bioText: String? + if let cachedData = cachedData as? CachedUserData { + bioText = cachedData.about + } else if let cachedData = cachedData as? CachedChannelData { + bioText = cachedData.about + } else if let cachedData = cachedData as? CachedGroupData { + bioText = cachedData.about + } + + guard let bioText, !bioText.isEmpty else { + return + } + + let copyAction = { [weak self] in + guard let self else { + return + } + UIPasteboard.general.string = bioText + + let toastText: String + if let _ = self.data?.peer as? TelegramUser { + toastText = self.presentationData.strings.MyProfile_ToastBioCopied + } else { + toastText = self.presentationData.strings.ChannelProfile_ToastAboutCopied + } + + self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .copy(text: toastText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) + } + + var items: [ContextMenuItem] = [] + + if self.isMyProfile { + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_BioActionEdit, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in + c?.dismiss { + guard let self else { + return + } + self.headerNode.navigationButtonContainer.performAction?(.edit, nil, nil) + + for (_, section) in self.editingSections { + for (id, itemNode) in section.itemNodes { + if id == AnyHashable("bio_edit") { + if let itemNode = itemNode as? PeerInfoScreenMultilineInputItemNode { + itemNode.focus() + } + break + } + } + } + } + }))) + } + + let copyText: String + if let _ = self.data?.peer as? TelegramUser { + copyText = self.presentationData.strings.MyProfile_BioActionCopy + } else { + copyText = self.presentationData.strings.ChannelProfile_AboutActionCopy + } + items.append(.action(ContextMenuActionItem(text: copyText, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in + c?.dismiss { + copyAction() + } + }))) + + let (canTranslate, language) = canTranslateText(context: self.context, text: bioText, showTranslate: translationSettings.showTranslate, showTranslateIfTopical: false, ignoredLanguages: translationSettings.ignoredLanguages) + if canTranslate { + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Conversation_ContextMenuTranslate, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Translate"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in + c?.dismiss { + guard let self else { + return + } + + let controller = TranslateScreen(context: self.context, text: bioText, canCopy: true, fromLanguage: language, ignoredLanguages: translationSettings.ignoredLanguages) + controller.pushController = { [weak self] c in + (self?.controller?.navigationController as? NavigationController)?._keepModalDismissProgress = true + self?.controller?.push(c) + } + controller.presentController = { [weak self] c in + self?.controller?.present(c, in: .window(.root)) + } + self.controller?.present(controller, in: .window(.root)) + } + }))) + } + + let actions = ContextController.Items(content: .list(items)) + + let contextController = ContextController(presentationData: self.presentationData, source: .extracted(PeerInfoContextExtractedContentSource(sourceNode: sourceNode)), items: .single(actions), gesture: gesture) + self.controller?.present(contextController, in: .window(.root)) + }) + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenBirthday.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenBirthday.swift new file mode 100644 index 00000000..95c79937 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenBirthday.swift @@ -0,0 +1,69 @@ +import Foundation +import UIKit +import Display +import AccountContext +import SwiftSignalKit +import Postbox +import TelegramCore +import AsyncDisplayKit +import TelegramStringFormatting +import UndoUI +import ContextUI + +extension PeerInfoScreenNode { + func openBirthdayContextMenu(node: ASDisplayNode, gesture: ContextGesture?) { + guard let sourceNode = node as? ContextExtractedContentContainingNode else { + return + } + guard let cachedData = self.data?.cachedData else { + return + } + + var birthday: TelegramBirthday? + if let cachedData = cachedData as? CachedUserData { + birthday = cachedData.birthday + } + + guard let birthday else { + return + } + + let copyAction = { [weak self] in + guard let self else { + return + } + let presentationData = self.presentationData + let text = stringForCompactBirthday(birthday, strings: presentationData.strings) + + UIPasteboard.general.string = text + + self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .copy(text: self.presentationData.strings.MyProfile_ToastBirthdayCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) + } + + var items: [ContextMenuItem] = [] + + if self.isMyProfile { + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_BirthdayActionEdit, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in + c?.dismiss { + guard let self else { + return + } + + self.state = self.state.withIsEditingBirthDate(true) + self.headerNode.navigationButtonContainer.performAction?(.edit, nil, nil) + } + }))) + } + + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_BirthdayActionCopy, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in + c?.dismiss { + copyAction() + } + }))) + + let actions = ContextController.Items(content: .list(items)) + + let contextController = ContextController(presentationData: self.presentationData, source: .extracted(PeerInfoContextExtractedContentSource(sourceNode: sourceNode)), items: .single(actions), gesture: gesture) + self.controller?.present(contextController, in: .window(.root)) + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenChat.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenChat.swift new file mode 100644 index 00000000..824bd0ce --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenChat.swift @@ -0,0 +1,156 @@ +import Foundation +import UIKit +import Display +import AccountContext +import SwiftSignalKit +import Postbox +import TelegramCore + +extension PeerInfoScreenNode { + func openChatWithMessageSearch() { + if let navigationController = (self.controller?.navigationController as? NavigationController) { + if case let .replyThread(currentMessage) = self.chatLocation, let current = navigationController.viewControllers.first(where: { controller in + if let controller = controller as? ChatController, case let .replyThread(message) = controller.chatLocation, message.peerId == currentMessage.peerId, message.threadId == currentMessage.threadId { + return true + } + return false + }) as? ChatController { + var viewControllers = navigationController.viewControllers + if let index = viewControllers.firstIndex(of: current) { + viewControllers.removeSubrange(index + 1 ..< viewControllers.count) + } + navigationController.setViewControllers(viewControllers, animated: true) + current.activateSearch(domain: .everything, query: "") + } else if let peer = self.data?.chatPeer { + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(EnginePeer(peer)), keepStack: self.nearbyPeerDistance != nil ? .always : .default, activateMessageSearch: (.everything, ""), peerNearbyData: self.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0) }), completion: { [weak self] _ in + if let strongSelf = self, strongSelf.nearbyPeerDistance != nil { + var viewControllers = navigationController.viewControllers + viewControllers = viewControllers.filter { controller in + if controller is PeerInfoScreen { + return false + } + return true + } + navigationController.setViewControllers(viewControllers, animated: false) + } + })) + } + } + } + + func openChatForReporting(title: String, option: Data, message: String?) { + if let peer = self.data?.peer, let navigationController = (self.controller?.navigationController as? NavigationController) { + if let channel = peer as? TelegramChannel, channel.isForumOrMonoForum { + //let _ = self.context.engine.peers.reportPeer(peerId: peer.id, reason: reason, message: "").startStandalone() + //self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .emoji(name: "PoliceCar", text: self.presentationData.strings.Report_Succeed), elevatedLayout: false, action: { _ in return false }), in: .current) + } else { + self.context.sharedContext.navigateToChatController( + NavigateToChatControllerParams( + navigationController: navigationController, + context: self.context, + chatLocation: .peer(EnginePeer(peer)), + keepStack: .default, + reportReason: NavigateToChatControllerParams.ReportReason(title: title, option: option, message: message), + completion: { _ in + } + ) + ) + } + } + } + + func openChatForThemeChange() { + if let peer = self.data?.peer, let navigationController = (self.controller?.navigationController as? NavigationController) { + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(EnginePeer(peer)), keepStack: .default, changeColors: true, completion: { _ in + })) + } + } + + func openChatForTranslation() { + if let peer = self.data?.peer, let navigationController = (self.controller?.navigationController as? NavigationController) { + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(EnginePeer(peer)), keepStack: .default, changeColors: false, completion: { _ in + })) + } + } + + func openChat(peerId: EnginePeer.Id?) { + if let peerId { + let _ = (self.context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.Peer(id: peerId) + ) + |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in + guard let self, let peer else { + return + } + guard let navigationController = self.controller?.navigationController as? NavigationController else { + return + } + + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), keepStack: .always)) + }) + return + } + + if let peer = self.data?.peer, let navigationController = self.controller?.navigationController as? NavigationController { + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(EnginePeer(peer)), keepStack: self.nearbyPeerDistance != nil ? .always : .default, peerNearbyData: self.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0) }), completion: { [weak self] _ in + if let strongSelf = self, strongSelf.nearbyPeerDistance != nil { + var viewControllers = navigationController.viewControllers + viewControllers = viewControllers.filter { controller in + if controller is PeerInfoScreen { + return false + } + return true + } + navigationController.setViewControllers(viewControllers, animated: false) + } + })) + } + } + + func openChatWithClearedHistory(type: InteractiveHistoryClearingType) { + guard let peer = self.data?.chatPeer, let navigationController = self.controller?.navigationController as? NavigationController else { + return + } + + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(EnginePeer(peer)), keepStack: self.nearbyPeerDistance != nil ? .always : .default, peerNearbyData: self.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0) }), setupController: { controller in + controller.beginClearHistory(type: type) + }, completion: { [weak self] _ in + if let strongSelf = self, strongSelf.nearbyPeerDistance != nil { + var viewControllers = navigationController.viewControllers + viewControllers = viewControllers.filter { controller in + if controller is PeerInfoScreen { + return false + } + return true + } + + navigationController.setViewControllers(viewControllers, animated: false) + } + })) + } + + func openChannelMessages() { + guard let channel = self.data?.peer as? TelegramChannel, let linkedMonoforumId = channel.linkedMonoforumId else { + return + } + let _ = (self.context.engine.data.get( + TelegramEngine.EngineData.Item.Peer.Peer(id: linkedMonoforumId) + ) + |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in + guard let self, let peer else { + return + } + if let controller = self.controller, let navigationController = controller.navigationController as? NavigationController { + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer))) + } + }) + } + + func openRecentActions() { + guard let peer = self.data?.peer else { + return + } + let controller = self.context.sharedContext.makeChatRecentActionsController(context: self.context, peer: peer, adminPeerId: nil, starsState: self.data?.starsRevenueStatsState) + self.controller?.push(controller) + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenMessage.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenMessage.swift new file mode 100644 index 00000000..2bac605c --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenMessage.swift @@ -0,0 +1,141 @@ +import Foundation +import UIKit +import Display +import AccountContext +import SwiftSignalKit +import Postbox +import TelegramCore +import LegacyMediaPickerUI +import ChatHistorySearchContainerNode +import MediaResources +import TelegramUIPreferences + +extension PeerInfoScreenNode { + func openMessage(id: MessageId) -> Bool { + guard let controller = self.controller, let navigationController = controller.navigationController as? NavigationController else { + return false + } + var foundGalleryMessage: Message? + if let searchContentNode = self.searchDisplayController?.contentNode as? ChatHistorySearchContainerNode { + if let galleryMessage = searchContentNode.messageForGallery(id) { + self.context.engine.messages.ensureMessagesAreLocallyAvailable(messages: [EngineMessage(galleryMessage)]) + foundGalleryMessage = galleryMessage + } + } + if foundGalleryMessage == nil, let galleryMessage = self.paneContainerNode.findLoadedMessage(id: id) { + foundGalleryMessage = galleryMessage + } + + guard let galleryMessage = foundGalleryMessage else { + return false + } + self.view.endEditing(true) + + return self.context.sharedContext.openChatMessage(OpenChatMessageParams(context: self.context, chatLocation: self.chatLocation, chatFilterTag: nil, chatLocationContextHolder: self.chatLocationContextHolder, message: galleryMessage, standalone: false, reverseMessageGalleryOrder: true, navigationController: navigationController, dismissInput: { [weak self] in + self?.view.endEditing(true) + }, present: { [weak self] c, a, _ in + self?.controller?.present(c, in: .window(.root), with: a, blockInteraction: true) + }, transitionNode: { [weak self] messageId, media, _ in + guard let strongSelf = self else { + return nil + } + return strongSelf.paneContainerNode.transitionNodeForGallery(messageId: messageId, media: media) + }, addToTransitionSurface: { [weak self] view in + guard let strongSelf = self else { + return + } + strongSelf.paneContainerNode.currentPane?.node.addToTransitionSurface(view: view) + }, openUrl: { [weak self] url in + self?.openUrl(url: url, concealed: false, external: false) + }, openPeer: { [weak self] peer, navigation in + self?.openPeer(peerId: peer.id, navigation: navigation) + }, callPeer: { peerId, isVideo in + }, openConferenceCall: { _ in + }, enqueueMessage: { _ in + }, sendSticker: nil, sendEmoji: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, actionInteraction: GalleryControllerActionInteraction(openUrl: { [weak self] url, concealed, forceExternal in + if let strongSelf = self { + strongSelf.openUrl(url: url, concealed: false, external: forceExternal) + } + }, openUrlIn: { [weak self] url in + if let strongSelf = self { + strongSelf.openUrlIn(url) + } + }, openPeerMention: { [weak self] mention in + if let strongSelf = self { + strongSelf.openPeerMention(mention) + } + }, openPeer: { [weak self] peer in + if let strongSelf = self { + strongSelf.openPeer(peerId: peer.id, navigation: .default) + } + }, openHashtag: { [weak self] peerName, hashtag in + if let strongSelf = self { + strongSelf.openHashtag(hashtag, peerName: peerName) + } + }, openBotCommand: { _ in + }, openAd: { _ in + }, addContact: { [weak self] phoneNumber in + if let strongSelf = self { + strongSelf.context.sharedContext.openAddContact(context: strongSelf.context, firstName: "", lastName: "", phoneNumber: phoneNumber, label: defaultContactLabel, present: { [weak self] controller, arguments in + self?.controller?.present(controller, in: .window(.root), with: arguments) + }, pushController: { [weak self] controller in + if let strongSelf = self { + strongSelf.controller?.push(controller) + } + }, completed: {}) + } + }, storeMediaPlaybackState: { [weak self] messageId, timestamp, playbackRate in + guard let strongSelf = self else { + return + } + var storedState: MediaPlaybackStoredState? + if let timestamp = timestamp { + storedState = MediaPlaybackStoredState(timestamp: timestamp, playbackRate: AudioPlaybackRate(playbackRate)) + } + let _ = updateMediaPlaybackStoredStateInteractively(engine: strongSelf.context.engine, messageId: messageId, state: storedState).startStandalone() + }, editMedia: { [weak self] messageId, snapshots, transitionCompletion in + guard let strongSelf = self else { + return + } + + let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: messageId)) + |> deliverOnMainQueue).startStandalone(next: { [weak self] message in + guard let strongSelf = self, let message = message else { + return + } + + var mediaReference: AnyMediaReference? + for media in message.media { + if let image = media as? TelegramMediaImage { + mediaReference = AnyMediaReference.standalone(media: image) + } else if let file = media as? TelegramMediaFile { + mediaReference = AnyMediaReference.standalone(media: file) + } + } + + if let mediaReference = mediaReference, let peer = message.peers[message.id.peerId] { + legacyMediaEditor(context: strongSelf.context, peer: peer, threadTitle: message.associatedThreadInfo?.title, media: mediaReference, mode: .draw, initialCaption: NSAttributedString(), snapshots: snapshots, transitionCompletion: { + transitionCompletion() + }, getCaptionPanelView: { + return nil + }, sendMessagesWithSignals: { [weak self] signals, _, _, _ in + if let strongSelf = self { + strongSelf.enqueueMediaMessageDisposable.set((legacyAssetPickerEnqueueMessages(context: strongSelf.context, account: strongSelf.context.account, signals: signals!) + |> deliverOnMainQueue).startStrict(next: { [weak self] messages in + if let strongSelf = self { + let _ = enqueueMessages(account: strongSelf.context.account, peerId: strongSelf.peerId, messages: messages.map { $0.message }).startStandalone() + } + })) + } + }, present: { [weak self] c, a in + self?.controller?.present(c, in: .window(.root), with: a) + }) + } + }) + }, updateCanReadHistory: { _ in + }), centralItemUpdated: { [weak self] messageId in + let _ = self?.paneContainerNode.requestExpandTabs?() + self?.paneContainerNode.currentPane?.node.ensureMessageIsVisible(id: messageId) + })) + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenNote.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenNote.swift new file mode 100644 index 00000000..9d2ff549 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenNote.swift @@ -0,0 +1,76 @@ +import Foundation +import UIKit +import Display +import AccountContext +import SwiftSignalKit +import Postbox +import TelegramCore +import AsyncDisplayKit +import ContextUI +import Pasteboard +import UndoUI + +extension PeerInfoScreenNode { + func openNoteContextMenu(node: ASDisplayNode, gesture: ContextGesture?) { + guard let sourceNode = node as? ContextExtractedContentContainingNode else { + return + } + guard let cachedData = self.data?.cachedData else { + return + } + + var noteText: String? + var noteEntities: [MessageTextEntity]? + if let cachedData = cachedData as? CachedUserData { + noteText = cachedData.note?.text + noteEntities = cachedData.note?.entities + } + + guard let noteText, !noteText.isEmpty else { + return + } + + let copyAction = { [weak self] in + guard let self else { + return + } + storeMessageTextInPasteboard(noteText, entities: noteEntities ?? []) + + let toastText = self.presentationData.strings.PeerInfo_ToastNoteCopied + self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .copy(text: toastText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) + } + + var items: [ContextMenuItem] = [] + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_NoteActionEdit, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in + c?.dismiss { + guard let self else { + return + } + self.headerNode.navigationButtonContainer.performAction?(.edit, nil, nil) + + for (_, section) in self.editingSections { + for (id, itemNode) in section.itemNodes { + if id == AnyHashable("note_edit") { + if let itemNode = itemNode as? PeerInfoScreenNoteListItemNode { + itemNode.focus() + } + break + } + } + } + } + }))) + + let copyText = self.presentationData.strings.PeerInfo_NoteActionCopy + items.append(.action(ContextMenuActionItem(text: copyText, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in + c?.dismiss { + copyAction() + } + }))) + + let actions = ContextController.Items(content: .list(items)) + + let contextController = ContextController(presentationData: self.presentationData, source: .extracted(PeerInfoContextExtractedContentSource(sourceNode: sourceNode)), items: .single(actions), gesture: gesture) + self.controller?.present(contextController, in: .window(.root)) + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenPeerInfoContextMenu.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenPeerInfoContextMenu.swift new file mode 100644 index 00000000..b810ae58 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenPeerInfoContextMenu.swift @@ -0,0 +1,178 @@ +import Foundation +import UIKit +import Display +import AccountContext +import SwiftSignalKit +import Postbox +import TelegramCore +import AsyncDisplayKit +import UndoUI +import TranslateUI +import TelegramStringFormatting +import TelegramUIPreferences + +extension PeerInfoScreenNode { + func openPeerInfoContextMenu(subject: PeerInfoContextSubject, sourceNode: ASDisplayNode, sourceRect: CGRect?) { + guard let data = self.data, let peer = data.peer, let controller = self.controller else { + return + } + let context = self.context + switch subject { + case .birthday: + if let cachedData = data.cachedData as? CachedUserData, let birthday = cachedData.birthday { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let text = stringForCompactBirthday(birthday, strings: presentationData.strings) + + let actions: [ContextMenuAction] = [ContextMenuAction(content: .text(title: presentationData.strings.Conversation_ContextMenuCopy, accessibilityLabel: presentationData.strings.Conversation_ContextMenuCopy), action: { [weak self] in + UIPasteboard.general.string = text + + self?.controller?.present(UndoOverlayController(presentationData: presentationData, content: .copy(text: presentationData.strings.Conversation_TextCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) + })] + let contextMenuController = makeContextMenuController(actions: actions) + controller.present(contextMenuController, in: .window(.root), with: ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self, weak sourceNode] in + if let controller = self?.controller, let sourceNode = sourceNode { + var rect = sourceNode.bounds.insetBy(dx: 0.0, dy: 2.0) + if let sourceRect = sourceRect { + rect = sourceRect.insetBy(dx: 0.0, dy: 2.0) + } + return (sourceNode, rect, controller.displayNode, controller.view.bounds) + } else { + return nil + } + })) + } + case .bio: + var text: String? + if let cachedData = data.cachedData as? CachedUserData { + text = cachedData.about + } else if let cachedData = data.cachedData as? CachedGroupData { + text = cachedData.about + } else if let cachedData = data.cachedData as? CachedChannelData { + text = cachedData.about + } + if let text = text, !text.isEmpty { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let _ = (self.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.translationSettings]) + |> take(1) + |> deliverOnMainQueue).startStandalone(next: { [weak self] sharedData in + let translationSettings: TranslationSettings + if let current = sharedData.entries[ApplicationSpecificSharedDataKeys.translationSettings]?.get(TranslationSettings.self) { + translationSettings = current + } else { + translationSettings = TranslationSettings.defaultSettings + } + + var actions: [ContextMenuAction] = [ContextMenuAction(content: .text(title: presentationData.strings.Conversation_ContextMenuCopy, accessibilityLabel: presentationData.strings.Conversation_ContextMenuCopy), action: { [weak self] in + UIPasteboard.general.string = text + + self?.controller?.present(UndoOverlayController(presentationData: presentationData, content: .copy(text: presentationData.strings.Conversation_TextCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) + })] + + let (canTranslate, language) = canTranslateText(context: context, text: text, showTranslate: translationSettings.showTranslate, showTranslateIfTopical: false, ignoredLanguages: translationSettings.ignoredLanguages) + if canTranslate { + actions.append(ContextMenuAction(content: .text(title: presentationData.strings.Conversation_ContextMenuTranslate, accessibilityLabel: presentationData.strings.Conversation_ContextMenuTranslate), action: { [weak self] in + + let controller = TranslateScreen(context: context, text: text, canCopy: true, fromLanguage: language, ignoredLanguages: translationSettings.ignoredLanguages) + controller.pushController = { [weak self] c in + (self?.controller?.navigationController as? NavigationController)?._keepModalDismissProgress = true + self?.controller?.push(c) + } + controller.presentController = { [weak self] c in + self?.controller?.present(c, in: .window(.root)) + } + self?.controller?.present(controller, in: .window(.root)) + })) + } + + let contextMenuController = makeContextMenuController(actions: actions) + controller.present(contextMenuController, in: .window(.root), with: ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self, weak sourceNode] in + if let controller = self?.controller, let sourceNode = sourceNode { + var rect = sourceNode.bounds.insetBy(dx: 0.0, dy: 2.0) + if let sourceRect = sourceRect { + rect = sourceRect.insetBy(dx: 0.0, dy: 2.0) + } + return (sourceNode, rect, controller.displayNode, controller.view.bounds) + } else { + return nil + } + })) + }) + } + case let .phone(phone): + let contextMenuController = makeContextMenuController(actions: [ContextMenuAction(content: .text(title: self.presentationData.strings.Conversation_ContextMenuCopy, accessibilityLabel: self.presentationData.strings.Conversation_ContextMenuCopy), action: { [weak self] in + UIPasteboard.general.string = phone + + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + self?.controller?.present(UndoOverlayController(presentationData: presentationData, content: .copy(text: presentationData.strings.Conversation_PhoneCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) + })]) + controller.present(contextMenuController, in: .window(.root), with: ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self, weak sourceNode] in + if let controller = self?.controller, let sourceNode = sourceNode { + var rect = sourceNode.bounds.insetBy(dx: 0.0, dy: 2.0) + if let sourceRect = sourceRect { + rect = sourceRect.insetBy(dx: 0.0, dy: 2.0) + } + return (sourceNode, rect, controller.displayNode, controller.view.bounds) + } else { + return nil + } + })) + case let .link(customLink): + let text: String + let content: UndoOverlayContent + if let customLink = customLink { + text = customLink + content = .linkCopied(title: nil, text: self.presentationData.strings.Conversation_LinkCopied) + } else if let addressName = peer.addressName { + if peer is TelegramChannel { + text = "https://t.me/\(addressName)" + content = .linkCopied(title: nil, text: self.presentationData.strings.Conversation_LinkCopied) + } else { + text = "@" + addressName + content = .copy(text: self.presentationData.strings.Conversation_UsernameCopied) + } + } else { + text = "https://t.me/@id\(peer.id.id._internalGetInt64Value())" + content = .linkCopied(title: nil, text: self.presentationData.strings.Conversation_LinkCopied) + } + + let contextMenuController = makeContextMenuController(actions: [ContextMenuAction(content: .text(title: self.presentationData.strings.Conversation_ContextMenuCopy, accessibilityLabel: self.presentationData.strings.Conversation_ContextMenuCopy), action: { [weak self] in + UIPasteboard.general.string = text + + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + self?.controller?.present(UndoOverlayController(presentationData: presentationData, content: content, elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) + })]) + controller.present(contextMenuController, in: .window(.root), with: ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self, weak sourceNode] in + if let controller = self?.controller, let sourceNode = sourceNode { + var rect = sourceNode.bounds.insetBy(dx: 0.0, dy: 2.0) + if let sourceRect = sourceRect { + rect = sourceRect.insetBy(dx: 0.0, dy: 2.0) + } + return (sourceNode, rect, controller.displayNode, controller.view.bounds) + } else { + return nil + } + })) + case .businessHours(let text), .businessLocation(let text): + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + + let actions: [ContextMenuAction] = [ContextMenuAction(content: .text(title: presentationData.strings.Conversation_ContextMenuCopy, accessibilityLabel: presentationData.strings.Conversation_ContextMenuCopy), action: { [weak self] in + UIPasteboard.general.string = text + + self?.controller?.present(UndoOverlayController(presentationData: presentationData, content: .copy(text: presentationData.strings.Conversation_TextCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) + })] + + let contextMenuController = makeContextMenuController(actions: actions) + controller.present(contextMenuController, in: .window(.root), with: ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self, weak sourceNode] in + if let controller = self?.controller, let sourceNode = sourceNode { + var rect = sourceNode.bounds.insetBy(dx: 0.0, dy: 2.0) + if let sourceRect = sourceRect { + rect = sourceRect.insetBy(dx: 0.0, dy: 2.0) + } + return (sourceNode, rect, controller.displayNode, controller.view.bounds) + } else { + return nil + } + })) + } + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenPhone.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenPhone.swift new file mode 100644 index 00000000..8219c2df --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenPhone.swift @@ -0,0 +1,179 @@ +import Foundation +import UIKit +import Display +import AccountContext +import SwiftSignalKit +import Postbox +import TelegramCore +import AsyncDisplayKit +import ContextUI +import PhoneNumberFormat +import UndoUI + +extension PeerInfoScreenNode { + func openPhone(value: String, node: ASDisplayNode, gesture: ContextGesture?, progress: Promise?) { + guard let sourceNode = node as? ContextExtractedContentContainingNode else { + return + } + + let formattedPhoneNumber = formatPhoneNumber(context: self.context, number: value) + if gesture == nil, formattedPhoneNumber.hasPrefix("+888") { + let collectibleInfo = Promise() + collectibleInfo.set(self.context.sharedContext.makeCollectibleItemInfoScreenInitialData(context: self.context, peerId: self.peerId, subject: .phoneNumber(value))) + + progress?.set(.single(true)) + let _ = (collectibleInfo.get() + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] initialData in + progress?.set(.single(false)) + + guard let self else { + return + } + if let initialData { + self.view.endEditing(true) + self.controller?.push(self.context.sharedContext.makeCollectibleItemInfoScreen(context: self.context, initialData: initialData)) + } else { + self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: "https://fragment.com/numbers", forceExternal: true, presentationData: self.presentationData, navigationController: nil, dismissInput: {}) + } + }) + + return + } + + let _ = (combineLatest( + getUserPeer(engine: self.context.engine, peerId: self.peerId), + getUserPeer(engine: self.context.engine, peerId: self.context.account.peerId) + ) |> deliverOnMainQueue).startStandalone(next: { [weak self] peer, accountPeer in + guard let strongSelf = self else { + return + } + let presentationData = strongSelf.presentationData + + let telegramCallAction: (Bool) -> Void = { [weak self] isVideo in + guard let strongSelf = self else { + return + } + strongSelf.requestCall(isVideo: isVideo) + } + + let phoneCallAction = { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.context.sharedContext.applicationBindings.openUrl("tel:\(formatPhoneNumber(context: strongSelf.context, number: value).replacingOccurrences(of: " ", with: ""))") + } + + let copyAction = { [weak self] in + guard let strongSelf = self else { + return + } + UIPasteboard.general.string = formatPhoneNumber(context: strongSelf.context, number: value) + + strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .copy(text: presentationData.strings.Conversation_PhoneCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) + } + + var accountIsFromUS = false + if let accountPeer, case let .user(user) = accountPeer, let phone = user.phone { + if let (country, _) = lookupCountryIdByNumber(phone, configuration: strongSelf.context.currentCountriesConfiguration.with { $0 }) { + if country.id == "US" { + accountIsFromUS = true + } + } + } + + var isAnonymousNumber = false + var items: [ContextMenuItem] = [] + + if strongSelf.isMyProfile { + items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.MyProfile_PhoneActionEdit, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in + c?.dismiss { + guard let self else { + return + } + self.openSettings(section: .phoneNumber) + } + }))) + } + + if case let .user(peer) = peer, let peerPhoneNumber = peer.phone, formattedPhoneNumber == formatPhoneNumber(context: strongSelf.context, number: peerPhoneNumber) { + if !strongSelf.isMyProfile { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_TelegramCall, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Call"), color: theme.contextMenu.primaryColor) }, action: { c, _ in + c?.dismiss { + telegramCallAction(false) + } + }))) + items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_TelegramVideoCall, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/VideoCall"), color: theme.contextMenu.primaryColor) }, action: { c, _ in + c?.dismiss { + telegramCallAction(true) + } + }))) + } + if !formattedPhoneNumber.hasPrefix("+888") { + if !strongSelf.isMyProfile { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_PhoneCall, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/PhoneCall"), color: theme.contextMenu.primaryColor) }, action: { c, _ in + c?.dismiss { + phoneCallAction() + } + }))) + } + } else { + isAnonymousNumber = true + } + items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.MyProfile_PhoneActionCopy, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in + c?.dismiss { + copyAction() + } + }))) + } else { + if !formattedPhoneNumber.hasPrefix("+888") { + if !strongSelf.isMyProfile { + items.append( + .action(ContextMenuActionItem(text: presentationData.strings.UserInfo_PhoneCall, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/PhoneCall"), color: theme.contextMenu.primaryColor) }, action: { c, _ in + c?.dismiss { + phoneCallAction() + } + })) + ) + } + } else { + isAnonymousNumber = true + } + items.append( + .action(ContextMenuActionItem(text: strongSelf.presentationData.strings.MyProfile_PhoneActionCopy, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in + c?.dismiss { + copyAction() + } + })) + ) + } + var actions = ContextController.Items(content: .list(items)) + if isAnonymousNumber && !accountIsFromUS { + let collectibleInfo = Promise() + collectibleInfo.set(strongSelf.context.sharedContext.makeCollectibleItemInfoScreenInitialData(context: strongSelf.context, peerId: strongSelf.peerId, subject: .phoneNumber(value))) + + actions.tip = .animatedEmoji(text: strongSelf.presentationData.strings.UserInfo_AnonymousNumberInfo, arguments: nil, file: nil, action: { [weak self] in + guard let self else { + return + } + + let _ = (collectibleInfo.get() + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] initialData in + guard let self else { + return + } + if let initialData { + self.view.endEditing(true) + self.controller?.push(self.context.sharedContext.makeCollectibleItemInfoScreen(context: self.context, initialData: initialData)) + } else { + self.context.sharedContext.openExternalUrl(context: strongSelf.context, urlContext: .generic, url: "https://fragment.com/numbers", forceExternal: true, presentationData: self.presentationData, navigationController: nil, dismissInput: {}) + } + }) + }) + } + let contextController = ContextController(presentationData: strongSelf.presentationData, source: .extracted(PeerInfoContextExtractedContentSource(sourceNode: sourceNode)), items: .single(actions), gesture: gesture) + strongSelf.controller?.present(contextController, in: .window(.root)) + }) + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenStories.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenStories.swift new file mode 100644 index 00000000..ba736db8 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenStories.swift @@ -0,0 +1,138 @@ +import Foundation +import UIKit +import Display +import AccountContext +import StoryContainerScreen +import SwiftSignalKit + +extension PeerInfoScreenNode { + func openStories(fromAvatar: Bool) { + guard let controller = self.controller else { + return + } + if let expiringStoryList = self.expiringStoryList, let expiringStoryListState = self.expiringStoryListState, !expiringStoryListState.items.isEmpty { + if fromAvatar { + StoryContainerScreen.openPeerStories(context: self.context, peerId: self.peerId, parentController: controller, avatarNode: self.headerNode.avatarListNode.avatarContainerNode.avatarNode) + return + } + + let _ = expiringStoryList + let storyContent = StoryContentContextImpl(context: self.context, isHidden: false, focusedPeerId: self.peerId, singlePeer: true) + let _ = (storyContent.state + |> take(1) + |> deliverOnMainQueue).startStandalone(next: { [weak self] storyContentState in + guard let self else { + return + } + var transitionIn: StoryContainerScreen.TransitionIn? + + if fromAvatar { + let transitionView = self.headerNode.avatarListNode.avatarContainerNode.avatarNode.view + transitionIn = StoryContainerScreen.TransitionIn( + sourceView: transitionView, + sourceRect: transitionView.bounds, + sourceCornerRadius: transitionView.bounds.height * 0.5, + sourceIsAvatar: true + ) + self.headerNode.avatarListNode.avatarContainerNode.avatarNode.isHidden = true + } else if let (expandedStorySetIndicatorTransitionView, subRect) = self.headerNode.avatarListNode.listContainerNode.expandedStorySetIndicatorTransitionView { + transitionIn = StoryContainerScreen.TransitionIn( + sourceView: expandedStorySetIndicatorTransitionView, + sourceRect: subRect, + sourceCornerRadius: expandedStorySetIndicatorTransitionView.bounds.height * 0.5, + sourceIsAvatar: false + ) + expandedStorySetIndicatorTransitionView.isHidden = true + } + + let storyContainerScreen = StoryContainerScreen( + context: self.context, + content: storyContent, + transitionIn: transitionIn, + transitionOut: { [weak self] peerId, _ in + guard let self else { + return nil + } + if !fromAvatar { + self.headerNode.avatarListNode.avatarContainerNode.avatarNode.isHidden = false + + if let (expandedStorySetIndicatorTransitionView, subRect) = self.headerNode.avatarListNode.listContainerNode.expandedStorySetIndicatorTransitionView { + return StoryContainerScreen.TransitionOut( + destinationView: expandedStorySetIndicatorTransitionView, + transitionView: StoryContainerScreen.TransitionView( + makeView: { [weak expandedStorySetIndicatorTransitionView] in + let parentView = UIView() + if let copyView = expandedStorySetIndicatorTransitionView?.snapshotContentTree(unhide: true) { + copyView.layer.anchorPoint = CGPoint() + parentView.addSubview(copyView) + } + return parentView + }, + updateView: { copyView, state, transition in + guard let view = copyView.subviews.first else { + return + } + let size = state.sourceSize.interpolate(to: state.destinationSize, amount: state.progress) + transition.setPosition(view: view, position: CGPoint(x: 0.0, y: 0.0)) + transition.setScale(view: view, scale: size.width / state.destinationSize.width) + }, + insertCloneTransitionView: nil + ), + destinationRect: subRect, + destinationCornerRadius: expandedStorySetIndicatorTransitionView.bounds.height * 0.5, + destinationIsAvatar: false, + completed: { [weak self] in + guard let self else { + return + } + + if let (expandedStorySetIndicatorTransitionView, _) = self.headerNode.avatarListNode.listContainerNode.expandedStorySetIndicatorTransitionView { + expandedStorySetIndicatorTransitionView.isHidden = false + } + } + ) + } + + return nil + } + + let transitionView = self.headerNode.avatarListNode.avatarContainerNode.avatarNode.view + return StoryContainerScreen.TransitionOut( + destinationView: transitionView, + transitionView: StoryContainerScreen.TransitionView( + makeView: { [weak transitionView] in + let parentView = UIView() + if let copyView = transitionView?.snapshotContentTree(unhide: true) { + parentView.addSubview(copyView) + } + return parentView + }, + updateView: { copyView, state, transition in + guard let view = copyView.subviews.first else { + return + } + let size = state.sourceSize.interpolate(to: state.destinationSize, amount: state.progress) + transition.setPosition(view: view, position: CGPoint(x: size.width * 0.5, y: size.height * 0.5)) + transition.setScale(view: view, scale: size.width / state.destinationSize.width) + }, + insertCloneTransitionView: nil + ), + destinationRect: transitionView.bounds, + destinationCornerRadius: transitionView.bounds.height * 0.5, + destinationIsAvatar: true, + completed: { [weak self] in + guard let self else { + return + } + self.headerNode.avatarListNode.avatarContainerNode.avatarNode.isHidden = false + } + ) + } + ) + self.controller?.push(storyContainerScreen) + }) + + return + } + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenURL.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenURL.swift new file mode 100644 index 00000000..476ce5a7 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenURL.swift @@ -0,0 +1,317 @@ +import Foundation +import UIKit +import Display +import AccountContext +import SwiftSignalKit +import Postbox +import TelegramCore +import OpenInExternalAppUI +import PresentationDataUtils +import OverlayStatusController +import HashtagSearchUI +import ShareController +import UndoUI + +extension PeerInfoScreenNode { + func openResolved(_ result: ResolvedUrl) { + guard let navigationController = self.controller?.navigationController as? NavigationController else { + return + } + self.context.sharedContext.openResolvedUrl(result, context: self.context, urlContext: .chat(peerId: self.peerId, message: nil, updatedPresentationData: self.controller?.updatedPresentationData), navigationController: navigationController, forceExternal: false, forceUpdate: false, openPeer: { [weak self] peer, navigation in + guard let strongSelf = self else { + return + } + switch navigation { + case let .chat(inputState, subject, peekData): + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), subject: subject, updateTextInputState: inputState, activateInput: inputState != nil ? .text : nil, keepStack: .always, peekData: peekData)) + case .info: + if let strongSelf = self, peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil { + if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, updatedPresentationData: nil, peer: peer._asPeer(), mode: .generic, avatarInitiallyExpanded: false, fromChat: false, requestsContext: nil) { + strongSelf.controller?.push(infoController) + } + } + case let .withBotStartPayload(startPayload): + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), botStart: startPayload, keepStack: .always)) + case let .withAttachBot(attachBotStart): + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), attachBotStart: attachBotStart)) + case let .withBotApp(botAppStart): + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer), botAppStart: botAppStart)) + default: + break + } + }, + sendFile: nil, + sendSticker: nil, + sendEmoji: nil, + requestMessageActionUrlAuth: nil, + joinVoiceChat: { peerId, invite, call in + + }, present: { [weak self] c, a in + self?.controller?.present(c, in: .window(.root), with: a) + }, dismissInput: { [weak self] in + self?.view.endEditing(true) + }, contentContext: nil, progress: nil, completion: nil) + } + + func openUrl(url: String, concealed: Bool, external: Bool, forceExternal: Bool = false, commit: @escaping () -> Void = {}) { + let _ = openUserGeneratedUrl(context: self.context, peerId: self.peerId, url: url, concealed: concealed, present: { [weak self] c in + self?.controller?.present(c, in: .window(.root)) + }, openResolved: { [weak self] tempResolved in + guard let strongSelf = self else { + return + } + + let result: ResolvedUrl = external ? .externalUrl(url) : tempResolved + + strongSelf.context.sharedContext.openResolvedUrl(result, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.controller?.navigationController as? NavigationController, forceExternal: forceExternal, forceUpdate: false, openPeer: { peer, navigation in + self?.openPeer(peerId: peer.id, navigation: navigation) + commit() + }, sendFile: nil, + sendSticker: nil, + sendEmoji: nil, + requestMessageActionUrlAuth: nil, + joinVoiceChat: { peerId, invite, call in + + }, + present: { c, a in + self?.controller?.present(c, in: .window(.root), with: a) + }, dismissInput: { + self?.view.endEditing(true) + }, contentContext: nil, progress: nil, completion: nil) + }) + } + + func openUrlIn(_ url: String) { + let actionSheet = OpenInActionSheetController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, item: .url(url: url), openUrl: { [weak self] url in + if let strongSelf = self, let navigationController = strongSelf.controller?.navigationController as? NavigationController { + strongSelf.context.sharedContext.openExternalUrl(context: strongSelf.context, urlContext: .generic, url: url, forceExternal: true, presentationData: strongSelf.presentationData, navigationController: navigationController, dismissInput: { + }) + } + }) + self.controller?.present(actionSheet, in: .window(.root)) + } + + func openPeerMention(_ name: String, navigation: ChatControllerInteractionNavigateToPeer = .default) { + let disposable: MetaDisposable + if let resolvePeerByNameDisposable = self.resolvePeerByNameDisposable { + disposable = resolvePeerByNameDisposable + } else { + disposable = MetaDisposable() + self.resolvePeerByNameDisposable = disposable + } + var resolveSignal = self.context.engine.peers.resolvePeerByName(name: name, referrer: nil, ageLimit: 10) + |> mapToSignal { result -> Signal in + guard case let .result(result) = result else { + return .complete() + } + return .single(result) + } + + var cancelImpl: (() -> Void)? + let presentationData = self.presentationData + let progressSignal = Signal { [weak self] subscriber in + let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: { + cancelImpl?() + })) + self?.controller?.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() + + resolveSignal = resolveSignal + |> afterDisposed { + Queue.mainQueue().async { + progressDisposable.dispose() + } + } + cancelImpl = { [weak self] in + self?.resolvePeerByNameDisposable?.set(nil) + } + disposable.set((resolveSignal + |> take(1) + |> mapToSignal { peer -> Signal in + return .single(peer?._asPeer()) + } + |> deliverOnMainQueue).start(next: { [weak self] peer in + if let strongSelf = self { + if let peer = peer { + var navigation = navigation + if case .default = navigation { + if let peer = peer as? TelegramUser, peer.botInfo != nil { + navigation = .chat(textInputState: nil, subject: nil, peekData: nil) + } + } + strongSelf.openResolved(.peer(peer, navigation)) + } else { + strongSelf.controller?.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.controller?.updatedPresentationData, title: nil, text: strongSelf.presentationData.strings.Resolve_ErrorNotFound, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + } + } + })) + } + + func openHashtag(_ hashtag: String, peerName: String?) { + if self.resolvePeerByNameDisposable == nil { + self.resolvePeerByNameDisposable = MetaDisposable() + } + var resolveSignal: Signal + if let peerName = peerName { + resolveSignal = self.context.engine.peers.resolvePeerByName(name: peerName, referrer: nil) + |> mapToSignal { result -> Signal in + guard case let .result(result) = result else { + return .complete() + } + return .single(result) + } + |> mapToSignal { peer -> Signal in + return .single(peer?._asPeer()) + } + } else { + resolveSignal = self.context.account.postbox.loadedPeerWithId(self.peerId) + |> map(Optional.init) + } + var cancelImpl: (() -> Void)? + let presentationData = self.presentationData + let progressSignal = Signal { [weak self] subscriber in + let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: { + cancelImpl?() + })) + self?.controller?.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() + + resolveSignal = resolveSignal + |> afterDisposed { + Queue.mainQueue().async { + progressDisposable.dispose() + } + } + cancelImpl = { [weak self] in + self?.resolvePeerByNameDisposable?.set(nil) + } + self.resolvePeerByNameDisposable?.set((resolveSignal + |> deliverOnMainQueue).start(next: { [weak self] peer in + if let strongSelf = self, !hashtag.isEmpty { + let searchController = HashtagSearchController(context: strongSelf.context, peer: peer.flatMap(EnginePeer.init), query: hashtag) + strongSelf.controller?.push(searchController) + } + })) + } + + func openUsername(value: String, isMainUsername: Bool, progress: Promise?) { + let url: String + if value.hasPrefix("https://") { + url = value + } else { + url = "https://t.me/\(value)" + } + + let openShare: (TelegramCollectibleItemInfo?) -> Void = { [weak self] collectibleItemInfo in + guard let self else { + return + } + let shareController = ShareController(context: self.context, subject: .url(url), updatedPresentationData: self.controller?.updatedPresentationData, collectibleItemInfo: collectibleItemInfo) + shareController.completed = { [weak self] peerIds in + guard let strongSelf = self else { + return + } + let _ = (strongSelf.context.engine.data.get( + EngineDataList( + peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init) + ) + ) + |> deliverOnMainQueue).startStandalone(next: { [weak self] peerList in + guard let strongSelf = self else { + return + } + + let peers = peerList.compactMap { $0 } + let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } + + let text: String + var savedMessages = false + if peerIds.count == 1, let peerId = peerIds.first, peerId == strongSelf.context.account.peerId { + text = presentationData.strings.UserInfo_LinkForwardTooltip_SavedMessages_One + savedMessages = true + } else { + if peers.count == 1, let peer = peers.first { + let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = presentationData.strings.UserInfo_LinkForwardTooltip_Chat_One(peerName).string + } else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last { + let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = presentationData.strings.UserInfo_LinkForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string + } else if let peer = peers.first { + let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = presentationData.strings.UserInfo_LinkForwardTooltip_ManyChats_One(peerName, "\(peers.count - 1)").string + } else { + text = "" + } + } + + strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in + if savedMessages, let self, action == .info { + let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) + |> deliverOnMainQueue).start(next: { [weak self] peer in + guard let self, let peer else { + return + } + guard let navigationController = self.controller?.navigationController as? NavigationController else { + return + } + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), forceOpenChat: true)) + }) + } + return false + }), in: .current) + }) + } + shareController.actionCompleted = { [weak self] in + if let strongSelf = self { + let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } + strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) + } + } + self.view.endEditing(true) + self.controller?.present(shareController, in: .window(.root)) + } + + if let pathComponents = URL(string: url)?.pathComponents, pathComponents.count >= 2, !pathComponents[1].isEmpty { + let namePart = pathComponents[1] + progress?.set(.single(true)) + let _ = (self.context.sharedContext.makeCollectibleItemInfoScreenInitialData(context: self.context, peerId: self.peerId, subject: .username(namePart)) + |> deliverOnMainQueue).start(next: { [weak self] initialData in + guard let self else { + return + } + + progress?.set(.single(false)) + + if let initialData { + if isMainUsername { + openShare(initialData.collectibleItemInfo) + } else { + self.view.endEditing(true) + self.controller?.push(self.context.sharedContext.makeCollectibleItemInfoScreen(context: self.context, initialData: initialData)) + } + } else { + openShare(nil) + } + }) + } else { + openShare(nil) + } + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenUsername.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenUsername.swift new file mode 100644 index 00000000..b9ee8fea --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenOpenUsername.swift @@ -0,0 +1,57 @@ +import Foundation +import UIKit +import Display +import AccountContext +import SwiftSignalKit +import Postbox +import TelegramCore +import AsyncDisplayKit +import ContextUI +import UndoUI + +extension PeerInfoScreenNode { + func openUsernameContextMenu(node: ASDisplayNode, gesture: ContextGesture?) { + guard let sourceNode = node as? ContextExtractedContentContainingNode else { + return + } + guard let peer = self.data?.peer else { + return + } + guard let username = peer.addressName else { + return + } + + let copyAction = { [weak self] in + guard let self else { + return + } + UIPasteboard.general.string = "@\(username)" + + self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .copy(text: self.presentationData.strings.Conversation_UsernameCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) + } + + var items: [ContextMenuItem] = [] + + if self.isMyProfile { + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_UsernameActionEdit, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in + c?.dismiss { + guard let self else { + return + } + self.openSettings(section: .username) + } + }))) + } + + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.MyProfile_UsernameActionCopy, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { c, _ in + c?.dismiss { + copyAction() + } + }))) + + let actions = ContextController.Items(content: .list(items)) + + let contextController = ContextController(presentationData: self.presentationData, source: .extracted(PeerInfoContextExtractedContentSource(sourceNode: sourceNode)), items: .single(actions), gesture: gesture) + self.controller?.present(contextController, in: .window(.root)) + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenPerformButtonAction.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenPerformButtonAction.swift new file mode 100644 index 00000000..8ac338ed --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenPerformButtonAction.swift @@ -0,0 +1,1214 @@ +import Foundation +import UIKit +import Display +import AccountContext +import SwiftSignalKit +import Postbox +import TelegramCore +import UndoUI +import ContextUI +import TelegramPresentationData +import NotificationPeerExceptionController +import NotificationExceptionsScreen +import ShareController +import TranslateUI + +extension PeerInfoScreenNode { + func performButtonAction(key: PeerInfoHeaderButtonKey, gesture: ContextGesture?) { + guard let controller = self.controller else { + return + } + switch key { + case .message: + if let navigationController = controller.navigationController as? NavigationController, let peer = self.data?.peer { + if let channel = peer as? TelegramChannel, case let .broadcast(info) = channel.info, info.flags.contains(.hasMonoforum), let linkedMonoforumId = channel.linkedMonoforumId { + Task { @MainActor [weak self] in + guard let self else { + return + } + + guard let peer = await self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: linkedMonoforumId)).get() else { + return + } + + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), keepStack: .default)) + } + } else { + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(EnginePeer(peer)), keepStack: self.nearbyPeerDistance != nil ? .always : .default, peerNearbyData: self.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0) }), completion: { [weak self] _ in + if let strongSelf = self, strongSelf.nearbyPeerDistance != nil { + var viewControllers = navigationController.viewControllers + viewControllers = viewControllers.filter { controller in + if controller is PeerInfoScreen { + return false + } + return true + } + navigationController.setViewControllers(viewControllers, animated: false) + } + })) + } + } + case .discussion: + if let cachedData = self.data?.cachedData as? CachedChannelData, case let .known(maybeLinkedDiscussionPeerId) = cachedData.linkedDiscussionPeerId, let linkedDiscussionPeerId = maybeLinkedDiscussionPeerId { + let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: linkedDiscussionPeerId)) + |> deliverOnMainQueue).startStandalone(next: { [weak self] linkedDiscussionPeer in + guard let self, let linkedDiscussionPeer else { + return + } + if let navigationController = controller.navigationController as? NavigationController { + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(linkedDiscussionPeer))) + } + }) + } + case .call: + self.requestCall(isVideo: false) + case .videoCall: + self.requestCall(isVideo: true) + case .voiceChat: + self.requestCall(isVideo: false, gesture: gesture) + case .mute: + var displayCustomNotificationSettings = false + + let chatIsMuted = peerInfoIsChatMuted(peer: self.data?.peer, peerNotificationSettings: self.data?.peerNotificationSettings, threadNotificationSettings: self.data?.threadNotificationSettings, globalNotificationSettings: self.data?.globalNotificationSettings) + if chatIsMuted { + } else { + displayCustomNotificationSettings = true + } + if self.data?.threadData == nil, let channel = self.data?.peer as? TelegramChannel, channel.isForumOrMonoForum { + displayCustomNotificationSettings = true + } + + let peerId = self.data?.peer?.id ?? self.peerId + + if !displayCustomNotificationSettings { + let _ = self.context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: self.chatLocation.threadId, muteInterval: 0).startStandalone() + + let iconColor: UIColor = .white + self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .universal(animation: "anim_profileunmute", scale: 0.075, colors: [ + "Middle.Group 1.Fill 1": iconColor, + "Top.Group 1.Fill 1": iconColor, + "Bottom.Group 1.Fill 1": iconColor, + "EXAMPLE.Group 1.Fill 1": iconColor, + "Line.Group 1.Stroke 1": iconColor + ], title: nil, text: self.presentationData.strings.PeerInfo_TooltipUnmuted, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) + } else { + self.state = self.state.withHighlightedButton(.mute) + if let (layout, navigationHeight) = self.validLayout { + self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) + } + + var items: [ContextMenuItem] = [] + + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_MuteFor, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Mute2d"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] c, _ in + guard let strongSelf = self else { + return + } + var subItems: [ContextMenuItem] = [] + + subItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Common_Back, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) + }, iconPosition: .left, action: { c, _ in + c?.popItems() + }))) + subItems.append(.separator) + + let presetValues: [Int32] = [ + 1 * 60 * 60, + 8 * 60 * 60, + 1 * 24 * 60 * 60, + 7 * 24 * 60 * 60 + ] + + for value in presetValues { + subItems.append(.action(ContextMenuActionItem(text: muteForIntervalString(strings: strongSelf.presentationData.strings, value: value), icon: { _ in + return nil + }, action: { _, f in + f(.default) + + let _ = strongSelf.context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: strongSelf.chatLocation.threadId, muteInterval: value).startStandalone() + + strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_mute_for", scale: 0.066, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipMutedFor(mutedForTimeIntervalString(strings: strongSelf.presentationData.strings, value: value)).string, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) + }))) + } + + subItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_MuteForCustom, icon: { _ in + return nil + }, action: { _, f in + f(.default) + + self?.openCustomMute() + }))) + + c?.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) + }))) + + items.append(.separator) + + var isSoundEnabled = true + let notificationSettings = self.data?.threadNotificationSettings ?? self.data?.peerNotificationSettings + if let notificationSettings { + switch notificationSettings.messageSound { + case .none: + isSoundEnabled = false + default: + break + } + } + + if !chatIsMuted { + if !isSoundEnabled { + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_EnableSound, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/SoundOn"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.default) + + guard let strongSelf = self else { + return + } + let _ = strongSelf.context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, threadId: strongSelf.chatLocation.threadId, sound: .default).startStandalone() + + strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_sound_on", scale: 0.056, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipSoundEnabled, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) + }))) + } else { + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_DisableSound, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/SoundOff"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.default) + + guard let strongSelf = self else { + return + } + let _ = strongSelf.context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, threadId: strongSelf.chatLocation.threadId, sound: .none).startStandalone() + + strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_sound_off", scale: 0.056, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipSoundDisabled, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) + }))) + } + } + + let context = self.context + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_NotificationsCustomize, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Customize"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + + let _ = (context.engine.data.get( + TelegramEngine.EngineData.Item.NotificationSettings.Global() + ) + |> deliverOnMainQueue).startStandalone(next: { globalSettings in + guard let strongSelf = self, let peer = strongSelf.data?.peer else { + return + } + let threadId = strongSelf.chatLocation.threadId + + let context = strongSelf.context + let updatePeerSound: (PeerId, PeerMessageSound) -> Signal = { peerId, sound in + return context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, threadId: threadId, sound: sound) |> deliverOnMainQueue + } + + let updatePeerNotificationInterval: (PeerId, Int32?) -> Signal = { peerId, muteInterval in + return context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: threadId, muteInterval: muteInterval) |> deliverOnMainQueue + } + + let updatePeerDisplayPreviews: (PeerId, PeerNotificationDisplayPreviews) -> Signal = { + peerId, displayPreviews in + return context.engine.peers.updatePeerDisplayPreviewsSetting(peerId: peerId, threadId: threadId, displayPreviews: displayPreviews) |> deliverOnMainQueue + } + + let updatePeerStoriesMuted: (PeerId, PeerStoryNotificationSettings.Mute) -> Signal = { + peerId, mute in + return context.engine.peers.updatePeerStoriesMutedSetting(peerId: peerId, mute: mute) |> deliverOnMainQueue + } + + let updatePeerStoriesHideSender: (PeerId, PeerStoryNotificationSettings.HideSender) -> Signal = { + peerId, hideSender in + return context.engine.peers.updatePeerStoriesHideSenderSetting(peerId: peerId, hideSender: hideSender) |> deliverOnMainQueue + } + + let updatePeerStorySound: (PeerId, PeerMessageSound) -> Signal = { peerId, sound in + return context.engine.peers.updatePeerStorySoundInteractive(peerId: peerId, sound: sound) |> deliverOnMainQueue + } + + let mode: NotificationExceptionMode + let defaultSound: PeerMessageSound + if let _ = peer as? TelegramUser { + mode = .users([:]) + defaultSound = globalSettings.privateChats.sound._asMessageSound() + } else if let _ = peer as? TelegramSecretChat { + mode = .users([:]) + defaultSound = globalSettings.privateChats.sound._asMessageSound() + } else if let channel = peer as? TelegramChannel { + if case .broadcast = channel.info { + mode = .channels([:]) + defaultSound = globalSettings.channels.sound._asMessageSound() + } else { + mode = .groups([:]) + defaultSound = globalSettings.groupChats.sound._asMessageSound() + } + } else { + mode = .groups([:]) + defaultSound = globalSettings.groupChats.sound._asMessageSound() + } + let _ = mode + + let canRemove = false + + let exceptionController = notificationPeerExceptionController(context: context, updatedPresentationData: strongSelf.controller?.updatedPresentationData, peer: EnginePeer(peer), threadId: threadId, isStories: nil, canRemove: canRemove, defaultSound: defaultSound, defaultStoriesSound: globalSettings.privateChats.storySettings.sound, edit: true, updatePeerSound: { peerId, sound in + let _ = (updatePeerSound(peer.id, sound) + |> deliverOnMainQueue).startStandalone(next: { _ in + }) + }, updatePeerNotificationInterval: { peerId, muteInterval in + let _ = (updatePeerNotificationInterval(peerId, muteInterval) + |> deliverOnMainQueue).startStandalone(next: { _ in + guard let strongSelf = self else { + return + } + if let muteInterval = muteInterval, muteInterval == Int32.max { + let iconColor: UIColor = .white + strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_profilemute", scale: 0.075, colors: [ + "Middle.Group 1.Fill 1": iconColor, + "Top.Group 1.Fill 1": iconColor, + "Bottom.Group 1.Fill 1": iconColor, + "EXAMPLE.Group 1.Fill 1": iconColor, + "Line.Group 1.Stroke 1": iconColor + ], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipMutedForever, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) + } + }) + }, updatePeerDisplayPreviews: { peerId, displayPreviews in + let _ = (updatePeerDisplayPreviews(peerId, displayPreviews) + |> deliverOnMainQueue).startStandalone(next: { _ in + + }) + }, updatePeerStoriesMuted: { peerId, mute in + let _ = (updatePeerStoriesMuted(peerId, mute) + |> deliverOnMainQueue).startStandalone() + }, updatePeerStoriesHideSender: { peerId, hideSender in + let _ = (updatePeerStoriesHideSender(peerId, hideSender) + |> deliverOnMainQueue).startStandalone() + }, updatePeerStorySound: { peerId, sound in + let _ = (updatePeerStorySound(peer.id, sound) + |> deliverOnMainQueue).startStandalone() + }, removePeerFromExceptions: { + }, modifiedPeer: { + }) + exceptionController.navigationPresentation = .modal + controller.push(exceptionController) + }) + }))) + + if chatIsMuted { + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_ButtonUnmute, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Unmute"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.default) + + guard let self else { + return + } + + let _ = self.context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: self.chatLocation.threadId, muteInterval: 0).startStandalone() + + let iconColor: UIColor = .white + self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .universal(animation: "anim_profileunmute", scale: 0.075, colors: [ + "Middle.Group 1.Fill 1": iconColor, + "Top.Group 1.Fill 1": iconColor, + "Bottom.Group 1.Fill 1": iconColor, + "EXAMPLE.Group 1.Fill 1": iconColor, + "Line.Group 1.Stroke 1": iconColor + ], title: nil, text: self.presentationData.strings.PeerInfo_TooltipUnmuted, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) + }))) + } else { + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_MuteForever, textColor: .destructive, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Muted"), color: theme.contextMenu.destructiveColor) + }, action: { [weak self] _, f in + f(.default) + + guard let strongSelf = self else { + return + } + + let _ = strongSelf.context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: strongSelf.chatLocation.threadId, muteInterval: Int32.max).startStandalone() + + let iconColor: UIColor = .white + strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_profilemute", scale: 0.075, colors: [ + "Middle.Group 1.Fill 1": iconColor, + "Top.Group 1.Fill 1": iconColor, + "Bottom.Group 1.Fill 1": iconColor, + "EXAMPLE.Group 1.Fill 1": iconColor, + "Line.Group 1.Stroke 1": iconColor + ], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipMutedForever, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) + }))) + } + + var tip: ContextController.Tip? + tip = nil + if !self.forumTopicNotificationExceptions.isEmpty { + items.append(.separator) + + let text: String = self.presentationData.strings.PeerInfo_TopicNotificationExceptions(Int32(self.forumTopicNotificationExceptions.count)) + + items.append(.action(ContextMenuActionItem( + text: text, + textLayout: .multiline, + textFont: .small, + parseMarkdown: true, + badge: nil, + icon: { _ in + return nil + }, + action: { [weak self] _, f in + guard let self else { + return + } + f(.default) + self.controller?.push(threadNotificationExceptionsScreen(context: self.context, peerId: self.peerId, notificationExceptions: self.forumTopicNotificationExceptions, updated: { [weak self] value in + guard let self else { + return + } + self.forumTopicNotificationExceptions = value + })) + } + ))) + } + + self.view.endEditing(true) + + if let sourceNode = self.headerNode.buttonNodes[.mute]?.referenceNode { + let contextController = ContextController(presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items), tip: tip)), gesture: gesture) + contextController.dismissed = { [weak self] in + if let strongSelf = self { + strongSelf.state = strongSelf.state.withHighlightedButton(nil) + if let (layout, navigationHeight) = strongSelf.validLayout { + strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) + } + } + } + controller.presentInGlobalOverlay(contextController) + } + } + case .more: + guard let data = self.data, let peer = data.peer, let chatPeer = data.chatPeer else { + return + } + let presentationData = self.presentationData + self.state = self.state.withHighlightedButton(.more) + if let (layout, navigationHeight) = self.validLayout { + self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) + } + + var mainItemsImpl: (() -> Signal<[ContextMenuItem], NoError>)? + mainItemsImpl = { [weak self] in + var items: [ContextMenuItem] = [] + guard let strongSelf = self else { + return .single(items) + } + + let allHeaderButtons = Set(peerInfoHeaderButtons(peer: peer, cachedData: data.cachedData, isOpenedFromChat: strongSelf.isOpenedFromChat, isExpanded: false, videoCallsEnabled: strongSelf.videoCallsEnabled, isSecretChat: strongSelf.peerId.namespace == Namespaces.Peer.SecretChat, isContact: strongSelf.data?.isContact ?? false, threadInfo: data.threadData?.info)) + let headerButtons = Set(peerInfoHeaderButtons(peer: peer, cachedData: data.cachedData, isOpenedFromChat: strongSelf.isOpenedFromChat, isExpanded: true, videoCallsEnabled: strongSelf.videoCallsEnabled, isSecretChat: strongSelf.peerId.namespace == Namespaces.Peer.SecretChat, isContact: strongSelf.data?.isContact ?? false, threadInfo: strongSelf.data?.threadData?.info)) + + let filteredButtons = allHeaderButtons.subtracting(headerButtons) + + var currentAutoremoveTimeout: Int32? + if let cachedData = data.cachedData as? CachedUserData { + switch cachedData.autoremoveTimeout { + case let .known(value): + currentAutoremoveTimeout = value?.peerValue + case .unknown: + break + } + } else if let cachedData = data.cachedData as? CachedGroupData { + switch cachedData.autoremoveTimeout { + case let .known(value): + currentAutoremoveTimeout = value?.peerValue + case .unknown: + break + } + } else if let cachedData = data.cachedData as? CachedChannelData { + switch cachedData.autoremoveTimeout { + case let .known(value): + currentAutoremoveTimeout = value?.peerValue + case .unknown: + break + } + } + + var canSetupAutoremoveTimeout = false + + if let secretChat = chatPeer as? TelegramSecretChat { + currentAutoremoveTimeout = secretChat.messageAutoremoveTimeout + canSetupAutoremoveTimeout = false + } else if let group = chatPeer as? TelegramGroup { + if !group.hasBannedPermission(.banChangeInfo) { + canSetupAutoremoveTimeout = true + } + } else if let user = chatPeer as? TelegramUser { + if user.id != strongSelf.context.account.peerId { + canSetupAutoremoveTimeout = true + } + } else if let channel = chatPeer as? TelegramChannel { + if channel.hasPermission(.changeInfo) { + canSetupAutoremoveTimeout = true + } + } + + if filteredButtons.contains(.call) { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.PeerInfo_ButtonCall, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Call"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + self?.requestCall(isVideo: false) + }))) + } + if filteredButtons.contains(.search) { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.ChatSearch_SearchPlaceholder, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Search"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + self?.openChatWithMessageSearch() + }))) + } + + var hasDiscussion = false + if let channel = chatPeer as? TelegramChannel { + switch channel.info { + case let .broadcast(info): + hasDiscussion = info.flags.contains(.hasDiscussionGroup) + case .group: + hasDiscussion = false + } + } + if !headerButtons.contains(.discussion) && hasDiscussion { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.PeerInfo_ViewDiscussion, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MessageBubble"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + self?.performButtonAction(key: .discussion, gesture: nil) + }))) + } + + if let user = peer as? TelegramUser { + if user.botInfo == nil && strongSelf.data?.encryptionKeyFingerprint == nil && !user.isDeleted { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_ChangeWallpaper, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ApplyTheme"), color: theme.contextMenu.primaryColor) + }, action: { _, f in + f(.dismissWithoutContent) + + self?.openChatForThemeChange() + }))) + } + + if let _ = user.botInfo { + if user.addressName != nil { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_ShareBot, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + self?.openShareBot() + }))) + } + + var addedPrivacy = false + var privacyPolicyUrl: String? + if let cachedData = (data.cachedData as? CachedUserData), let botInfo = cachedData.botInfo { + if let url = botInfo.privacyPolicyUrl { + privacyPolicyUrl = url + } else if botInfo.commands.contains(where: { $0.text == "privacy" }) { + + } else { + privacyPolicyUrl = presentationData.strings.WebApp_PrivacyPolicy_URL + } + } + if let privacyPolicyUrl { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_BotPrivacy, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + + guard let self else { + return + } + self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: privacyPolicyUrl, forceExternal: false, presentationData: self.presentationData, navigationController: self.controller?.navigationController as? NavigationController, dismissInput: {}) + }))) + addedPrivacy = true + } + if let cachedData = data.cachedData as? CachedUserData, let botInfo = cachedData.botInfo { + for command in botInfo.commands { + if command.text == "settings" { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_BotSettings, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Bots"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + self?.performBotCommand(command: .settings) + }))) + } else if command.text == "help" { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_BotHelp, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Help"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + self?.performBotCommand(command: .help) + }))) + } else if command.text == "privacy" && !addedPrivacy { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_BotPrivacy, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + self?.performBotCommand(command: .privacy) + }))) + } + } + } + } + + if strongSelf.peerId.namespace == Namespaces.Peer.CloudUser && user.botInfo == nil && !user.flags.contains(.isSupport) { + if let cachedUserData = strongSelf.data?.cachedData as? CachedUserData, let _ = cachedUserData.sendPaidMessageStars { + + } else { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.UserInfo_StartSecretChat, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Lock"), color: theme.contextMenu.primaryColor) + }, action: { _, f in + f(.dismissWithoutContent) + + self?.openStartSecretChat() + }))) + } + } + + if user.botInfo == nil && data.isContact, let peer = strongSelf.data?.peer as? TelegramUser, let phone = peer.phone { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Profile_ShareContactButton, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + + if let strongSelf = self { + let contact = TelegramMediaContact(firstName: peer.firstName ?? "", lastName: peer.lastName ?? "", phoneNumber: phone, peerId: peer.id, vCardData: nil) + let shareController = ShareController(context: strongSelf.context, subject: .media(.standalone(media: contact), nil), updatedPresentationData: strongSelf.controller?.updatedPresentationData) + shareController.completed = { [weak self] peerIds in + if let strongSelf = self { + let _ = (strongSelf.context.engine.data.get( + EngineDataList( + peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init) + ) + ) + |> deliverOnMainQueue).startStandalone(next: { [weak self] peerList in + guard let strongSelf = self else { + return + } + + let peers = peerList.compactMap { $0 } + + let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } + + let text: String + var savedMessages = false + if peerIds.count == 1, let peerId = peerIds.first, peerId == strongSelf.context.account.peerId { + text = presentationData.strings.UserInfo_ContactForwardTooltip_SavedMessages_One + savedMessages = true + } else { + if peers.count == 1, let peer = peers.first { + let peerName = peer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = presentationData.strings.UserInfo_ContactForwardTooltip_Chat_One(peerName).string + } else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last { + let firstPeerName = firstPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + let secondPeerName = secondPeer.id == strongSelf.context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = presentationData.strings.UserInfo_ContactForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string + } else if let peer = peers.first { + let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = presentationData.strings.UserInfo_ContactForwardTooltip_ManyChats_One(peerName, "\(peers.count - 1)").string + } else { + text = "" + } + } + + strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in + if savedMessages, let self, action == .info { + let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) + |> deliverOnMainQueue).start(next: { [weak self] peer in + guard let self, let peer else { + return + } + guard let navigationController = self.controller?.navigationController as? NavigationController else { + return + } + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer), forceOpenChat: true)) + }) + } + return false + }), in: .current) + }) + } + } + strongSelf.controller?.present(shareController, in: .window(.root)) + } + }))) + } + + if strongSelf.peerId.namespace == Namespaces.Peer.CloudUser, !user.isDeleted && user.botInfo == nil && !user.flags.contains(.isSupport) { + if let cachedData = data.cachedData as? CachedUserData, cachedData.disallowedGifts == .All { + } else { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Profile_SendGift, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Gift"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + + if let self { + self.openPremiumGift() + } + }))) + } + } + + if let cachedData = data.cachedData as? CachedUserData, canTranslateChats(context: strongSelf.context), cachedData.flags.contains(.translationHidden) { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ContextMenuTranslate, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Translate"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + + if let strongSelf = self { + let _ = updateChatTranslationStateInteractively(engine: strongSelf.context.engine, peerId: strongSelf.peerId, threadId: nil, { current in + return current?.withIsEnabled(true) + }).startStandalone() + + Queue.mainQueue().after(0.2, { + let _ = (strongSelf.context.engine.messages.togglePeerMessagesTranslationHidden(peerId: strongSelf.peerId, hidden: false) + |> deliverOnMainQueue).startStandalone(completed: { [weak self] in + self?.openChatForTranslation() + }) + }) + } + }))) + } + + let itemsCount = items.count + + if canSetupAutoremoveTimeout { + let strings = strongSelf.presentationData.strings + items.append(.action(ContextMenuActionItem(text: currentAutoremoveTimeout == nil ? strongSelf.presentationData.strings.PeerInfo_EnableAutoDelete : strongSelf.presentationData.strings.PeerInfo_AdjustAutoDelete, icon: { theme in + if let currentAutoremoveTimeout = currentAutoremoveTimeout { + let text = NSAttributedString(string: shortTimeIntervalString(strings: strings, value: currentAutoremoveTimeout), font: Font.regular(14.0), textColor: theme.contextMenu.primaryColor) + let bounds = text.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil) + return generateImage(bounds.size.integralFloor, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + UIGraphicsPushContext(context) + text.draw(in: bounds) + UIGraphicsPopContext() + }) + } else { + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Timer"), color: theme.contextMenu.primaryColor) + } + }, action: { [weak self] c, _ in + var subItems: [ContextMenuItem] = [] + + subItems.append(.action(ContextMenuActionItem(text: strings.Common_Back, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) + }, iconPosition: .left, action: { c, _ in + c?.popItems() + }))) + subItems.append(.separator) + + let presetValues: [Int32] = [ + 1 * 24 * 60 * 60, + 7 * 24 * 60 * 60, + 31 * 24 * 60 * 60 + ] + + for value in presetValues { + subItems.append(.action(ContextMenuActionItem(text: timeIntervalString(strings: strings, value: value), icon: { _ in + return nil + }, action: { _, f in + f(.default) + + self?.setAutoremove(timeInterval: value) + }))) + } + + subItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_AutoDeleteSettingOther, icon: { _ in + return nil + }, action: { _, f in + f(.default) + + self?.openAutoremove(currentValue: currentAutoremoveTimeout) + }))) + + if let _ = currentAutoremoveTimeout { + subItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_AutoDeleteDisable, textColor: .destructive, icon: { _ in + return nil + }, action: { _, f in + f(.default) + + self?.setAutoremove(timeInterval: nil) + }))) + } + + subItems.append(.separator) + + subItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_AutoDeleteInfo + "\n\n" + strongSelf.presentationData.strings.AutoremoveSetup_AdditionalGlobalSettingsInfo, textLayout: .multiline, textFont: .small, parseMarkdown: true, icon: { _ in + return nil + }, textLinkAction: { [weak c] in + c?.dismiss(completion: nil) + + guard let self else { + return + } + self.context.sharedContext.openResolvedUrl(.settings(.autoremoveMessages), context: self.context, urlContext: .generic, navigationController: self.controller?.navigationController as? NavigationController, forceExternal: false, forceUpdate: false, openPeer: { _, _ in }, sendFile: nil, sendSticker: nil, sendEmoji: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { _, _ in }, dismissInput: { [weak self] in + guard let self else { + return + } + self.controller?.view.endEditing(true) + }, contentContext: nil, progress: nil, completion: nil) + }, action: nil as ((ContextControllerProtocol?, @escaping (ContextMenuActionResult) -> Void) -> Void)?))) + + c?.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) + }))) + } + + let clearPeerHistory = ClearPeerHistory(context: strongSelf.context, peer: user, chatPeer: chatPeer, cachedData: strongSelf.data?.cachedData) + if clearPeerHistory.canClearForMyself != nil || clearPeerHistory.canClearForEveryone != nil { + items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_ClearMessages, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ClearMessages"), color: theme.contextMenu.primaryColor) + }, action: { c, _ in + if let c { + self?.openClearHistory(contextController: c, clearPeerHistory: clearPeerHistory, peer: user, chatPeer: user) + } + }))) + } + + if strongSelf.peerId.namespace == Namespaces.Peer.CloudUser && user.botInfo == nil && !user.flags.contains(.isSupport) { + if data.isContact { + if let cachedData = data.cachedData as? CachedUserData, cachedData.isBlocked { + } else { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_BlockUser, textColor: .destructive, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Restrict"), color: theme.contextMenu.destructiveColor) + }, action: { _, f in + f(.dismissWithoutContent) + + self?.updateBlocked(block: true) + }))) + } + } + } else if strongSelf.peerId.namespace == Namespaces.Peer.SecretChat && data.isContact { + if let cachedData = data.cachedData as? CachedUserData, cachedData.isBlocked { + } else { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_BlockUser, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Restrict"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + + self?.updateBlocked(block: true) + }))) + } + } + + let finalItemsCount = items.count + + if finalItemsCount > itemsCount { + items.insert(.separator, at: itemsCount) + } + } else if let channel = peer as? TelegramChannel { + if let cachedData = strongSelf.data?.cachedData as? CachedChannelData { + if case .broadcast = channel.info, cachedData.flags.contains(.starGiftsAvailable) { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Profile_SendGift, badge: nil, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Gift"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + + self?.openPremiumGift() + }))) + } + + let boostTitle: String + switch channel.info { + case .group: + boostTitle = presentationData.strings.PeerInfo_Group_Boost + case .broadcast: + boostTitle = presentationData.strings.PeerInfo_Channel_Boost + } + items.append(.action(ContextMenuActionItem(text: boostTitle, badge: nil, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Boost"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + + self?.openBoost() + }))) + + if channel.hasPermission(.editStories) { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.PeerInfo_Channel_ArchivedStories, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Archive"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + + self?.openStoryArchive() + }))) + } + if cachedData.flags.contains(.canViewStats) { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.ChannelInfo_Stats, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Statistics"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + + self?.openStats(section: .stats) + }))) + } + if cachedData.flags.contains(.translationHidden) { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ContextMenuTranslate, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Translate"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + + if let strongSelf = self { + let _ = updateChatTranslationStateInteractively(engine: strongSelf.context.engine, peerId: strongSelf.peerId, threadId: nil, { current in + return current?.withIsEnabled(true) + }).startStandalone() + + Queue.mainQueue().after(0.2, { + let _ = (strongSelf.context.engine.messages.togglePeerMessagesTranslationHidden(peerId: strongSelf.peerId, hidden: false) + |> deliverOnMainQueue).startStandalone(completed: { [weak self] in + self?.openChatForTranslation() + }) + }) + } + }))) + } + } + + var canReport = true + if channel.adminRights != nil { + canReport = false + } + if channel.flags.contains(.isCreator) { + canReport = false + } + if canReport { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.ReportPeer_Report, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Report"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] c, f in + self?.openReport(type: .default, contextController: c, backAction: { c in + if let mainItemsImpl = mainItemsImpl { + c.setItems(mainItemsImpl() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true) + } + }) + }))) + } + + if canSetupAutoremoveTimeout { + let strings = strongSelf.presentationData.strings + items.append(.action(ContextMenuActionItem(text: currentAutoremoveTimeout == nil ? strongSelf.presentationData.strings.PeerInfo_EnableAutoDelete : strongSelf.presentationData.strings.PeerInfo_AdjustAutoDelete, icon: { theme in + if let currentAutoremoveTimeout = currentAutoremoveTimeout { + let text = NSAttributedString(string: shortTimeIntervalString(strings: strings, value: currentAutoremoveTimeout), font: Font.regular(14.0), textColor: theme.contextMenu.primaryColor) + let bounds = text.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil) + return generateImage(bounds.size.integralFloor, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + UIGraphicsPushContext(context) + text.draw(in: bounds) + UIGraphicsPopContext() + }) + } else { + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Timer"), color: theme.contextMenu.primaryColor) + } + }, action: { [weak self] c, _ in + var subItems: [ContextMenuItem] = [] + + subItems.append(.action(ContextMenuActionItem(text: strings.Common_Back, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) + }, iconPosition: .left, action: { c, _ in + c?.popItems() + }))) + subItems.append(.separator) + + let presetValues: [Int32] = [ + 1 * 24 * 60 * 60, + 7 * 24 * 60 * 60, + 31 * 24 * 60 * 60 + ] + + for value in presetValues { + subItems.append(.action(ContextMenuActionItem(text: timeIntervalString(strings: strings, value: value), icon: { _ in + return nil + }, action: { _, f in + f(.default) + + self?.setAutoremove(timeInterval: value) + }))) + } + + subItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_AutoDeleteSettingOther, icon: { _ in + return nil + }, action: { _, f in + f(.default) + + self?.openAutoremove(currentValue: currentAutoremoveTimeout) + }))) + + if let _ = currentAutoremoveTimeout { + subItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_AutoDeleteDisable, textColor: .destructive, icon: { _ in + return nil + }, action: { _, f in + f(.default) + + self?.setAutoremove(timeInterval: nil) + }))) + } + + subItems.append(.separator) + + let baseText: String + if case .broadcast = channel.info { + baseText = strongSelf.presentationData.strings.PeerInfo_ChannelAutoDeleteInfo + } else { + baseText = strongSelf.presentationData.strings.PeerInfo_AutoDeleteInfo + } + + subItems.append(.action(ContextMenuActionItem(text: baseText + "\n\n" + strongSelf.presentationData.strings.AutoremoveSetup_AdditionalGlobalSettingsInfo, textLayout: .multiline, textFont: .small, parseMarkdown: true, icon: { _ in + return nil + }, textLinkAction: { [weak c] in + c?.dismiss(completion: nil) + + guard let self else { + return + } + self.context.sharedContext.openResolvedUrl(.settings(.autoremoveMessages), context: self.context, urlContext: .generic, navigationController: self.controller?.navigationController as? NavigationController, forceExternal: false, forceUpdate: false, openPeer: { _, _ in }, sendFile: nil, sendSticker: nil, sendEmoji: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { _, _ in }, dismissInput: { [weak self] in + guard let self else { + return + } + self.controller?.view.endEditing(true) + }, contentContext: nil, progress: nil, completion: nil) + }, action: nil as ((ContextControllerProtocol?, @escaping (ContextMenuActionResult) -> Void) -> Void)?))) + + c?.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) + }))) + } + + let clearPeerHistory = ClearPeerHistory(context: strongSelf.context, peer: channel, chatPeer: channel, cachedData: strongSelf.data?.cachedData) + if clearPeerHistory.canClearForMyself != nil || clearPeerHistory.canClearForEveryone != nil { + items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_ClearMessages, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ClearMessages"), color: theme.contextMenu.primaryColor) + }, action: { c, _ in + if let c { + self?.openClearHistory(contextController: c, clearPeerHistory: clearPeerHistory, peer: channel, chatPeer: channel) + } + }))) + } + + switch channel.info { + case .broadcast: + if case .member = channel.participationStatus, !headerButtons.contains(.leave) { + if !items.isEmpty { + items.append(.separator) + } + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Channel_LeaveChannel, textColor: .destructive, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Logout"), color: theme.contextMenu.destructiveColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + + self?.openLeavePeer(delete: false) + }))) + } + case .group: + if case .member = channel.participationStatus, !headerButtons.contains(.leave) { + if !items.isEmpty { + items.append(.separator) + } + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Group_LeaveGroup, textColor: .primary, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Logout"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + + self?.openLeavePeer(delete: false) + }))) + if let cachedData = data.cachedData as? CachedChannelData, cachedData.flags.contains(.canDeleteHistory) { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Group_DeleteGroup, textColor: .destructive, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.contextMenu.destructiveColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + + self?.openLeavePeer(delete: true) + }))) + } + } + } + } else if let group = peer as? TelegramGroup { + if canSetupAutoremoveTimeout { + let strings = strongSelf.presentationData.strings + items.append(.action(ContextMenuActionItem(text: currentAutoremoveTimeout == nil ? strongSelf.presentationData.strings.PeerInfo_EnableAutoDelete : strongSelf.presentationData.strings.PeerInfo_AdjustAutoDelete, icon: { theme in + if let currentAutoremoveTimeout = currentAutoremoveTimeout { + let text = NSAttributedString(string: shortTimeIntervalString(strings: strings, value: currentAutoremoveTimeout), font: Font.regular(14.0), textColor: theme.contextMenu.primaryColor) + let bounds = text.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil) + return generateImage(bounds.size.integralFloor, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + UIGraphicsPushContext(context) + text.draw(in: bounds) + UIGraphicsPopContext() + }) + } else { + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Timer"), color: theme.contextMenu.primaryColor) + } + }, action: { [weak self] c, _ in + var subItems: [ContextMenuItem] = [] + + subItems.append(.action(ContextMenuActionItem(text: strings.Common_Back, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: theme.contextMenu.primaryColor) + }, iconPosition: .left, action: { c, _ in + c?.popItems() + }))) + subItems.append(.separator) + + let presetValues: [Int32] = [ + 1 * 24 * 60 * 60, + 7 * 24 * 60 * 60, + 31 * 24 * 60 * 60 + ] + + for value in presetValues { + subItems.append(.action(ContextMenuActionItem(text: timeIntervalString(strings: strings, value: value), icon: { _ in + return nil + }, action: { _, f in + f(.default) + + self?.setAutoremove(timeInterval: value) + }))) + } + + subItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_AutoDeleteSettingOther, icon: { _ in + return nil + }, action: { _, f in + f(.default) + + self?.openAutoremove(currentValue: currentAutoremoveTimeout) + }))) + + if let _ = currentAutoremoveTimeout { + subItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_AutoDeleteDisable, textColor: .destructive, icon: { _ in + return nil + }, action: { _, f in + f(.default) + + self?.setAutoremove(timeInterval: nil) + }))) + } + + subItems.append(.separator) + + subItems.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_AutoDeleteInfo + "\n\n" + strongSelf.presentationData.strings.AutoremoveSetup_AdditionalGlobalSettingsInfo, textLayout: .multiline, textFont: .small, parseMarkdown: true, icon: { _ in + return nil + }, textLinkAction: { [weak c] in + c?.dismiss(completion: nil) + + guard let self else { + return + } + self.context.sharedContext.openResolvedUrl(.settings(.autoremoveMessages), context: self.context, urlContext: .generic, navigationController: self.controller?.navigationController as? NavigationController, forceExternal: false, forceUpdate: false, openPeer: { _, _ in }, sendFile: nil, sendSticker: nil, sendEmoji: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { _, _ in }, dismissInput: { [weak self] in + guard let self else { + return + } + self.controller?.view.endEditing(true) + }, contentContext: nil, progress: nil, completion: nil) + }, action: nil as ((ContextControllerProtocol?, @escaping (ContextMenuActionResult) -> Void) -> Void)?))) + + c?.pushItems(items: .single(ContextController.Items(content: .list(subItems)))) + }))) + } + + if let cachedData = data.cachedData as? CachedGroupData, cachedData.flags.contains(.translationHidden) { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ContextMenuTranslate, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Translate"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + + if let strongSelf = self { + let _ = updateChatTranslationStateInteractively(engine: strongSelf.context.engine, peerId: strongSelf.peerId, threadId: nil, { current in + return current?.withIsEnabled(true) + }).startStandalone() + + Queue.mainQueue().after(0.2, { + let _ = (strongSelf.context.engine.messages.togglePeerMessagesTranslationHidden(peerId: strongSelf.peerId, hidden: false) + |> deliverOnMainQueue).startStandalone(completed: { [weak self] in + self?.openChatForTranslation() + }) + }) + } + }))) + } + + var canReport = true + if case .creator = group.role { + canReport = false + } + if canReport { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.ReportPeer_Report, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Report"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] c, f in + self?.openReport(type: .default, contextController: c, backAction: { c in + if let mainItemsImpl = mainItemsImpl { + c.setItems(mainItemsImpl() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil, animated: true) + } + }) + }))) + } + + let clearPeerHistory = ClearPeerHistory(context: strongSelf.context, peer: group, chatPeer: group, cachedData: strongSelf.data?.cachedData) + if clearPeerHistory.canClearForMyself != nil || clearPeerHistory.canClearForEveryone != nil { + items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_ClearMessages, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ClearMessages"), color: theme.contextMenu.primaryColor) + }, action: { c, _ in + if let c { + self?.openClearHistory(contextController: c, clearPeerHistory: clearPeerHistory, peer: group, chatPeer: group) + } + }))) + } + + if case .Member = group.membership, !headerButtons.contains(.leave) { + if !items.isEmpty { + items.append(.separator) + } + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Group_LeaveGroup, textColor: .destructive, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Logout"), color: theme.contextMenu.destructiveColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + + self?.openLeavePeer(delete: false) + }))) + + if case .creator = group.role { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Group_DeleteGroup, textColor: .destructive, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.contextMenu.destructiveColor) + }, action: { [weak self] _, f in + f(.dismissWithoutContent) + + self?.openLeavePeer(delete: true) + }))) + } + } + } + + return .single(items) + } + + self.view.endEditing(true) + + if let sourceNode = self.headerNode.buttonNodes[.more]?.referenceNode { + let items = mainItemsImpl?() ?? .single([]) + let contextController = ContextController(presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) + contextController.dismissed = { [weak self] in + if let strongSelf = self { + strongSelf.state = strongSelf.state.withHighlightedButton(nil) + if let (layout, navigationHeight) = strongSelf.validLayout { + strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) + } + } + } + controller.presentInGlobalOverlay(contextController) + } + case .addMember: + self.openAddMember() + case .search: + self.openChatWithMessageSearch() + case .leave: + self.openLeavePeer(delete: false) + case .stop: + self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .universal(animation: "anim_banned", scale: 0.066, colors: [:], title: self.presentationData.strings.PeerInfo_BotBlockedTitle, text: self.presentationData.strings.PeerInfo_BotBlockedText, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) + self.updateBlocked(block: true) + case .addContact: + self.openAddContact() + } + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenSettingsActions.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenSettingsActions.swift new file mode 100644 index 00000000..be447bae --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenSettingsActions.swift @@ -0,0 +1,356 @@ +import Foundation +import UIKit +import Display +import AccountContext +import SwiftSignalKit +import Postbox +import TelegramCore +import SettingsUI +import PeerInfoStoryGridScreen +import CallListUI +import PassportUI +import AccountUtils +import OverlayStatusController +import PremiumUI +import TelegramPresentationData +import PresentationDataUtils +import PasswordSetupUI + +extension PeerInfoScreenNode { + func openSettings(section: PeerInfoSettingsSection) { + let push: (ViewController) -> Void = { [weak self] c in + guard let strongSelf = self, let navigationController = strongSelf.controller?.navigationController as? NavigationController else { + return + } + + if strongSelf.isMyProfile { + navigationController.pushViewController(c) + } else { + var updatedControllers = navigationController.viewControllers + for controller in navigationController.viewControllers.reversed() { + if controller !== strongSelf && !(controller is TabBarController) { + updatedControllers.removeLast() + } else { + break + } + } + updatedControllers.append(c) + + var animated = true + if let validLayout = strongSelf.validLayout?.0, case .regular = validLayout.metrics.widthClass { + animated = false + } + navigationController.setViewControllers(updatedControllers, animated: animated) + } + } + switch section { + case .avatar: + self.controller?.openAvatarForEditing() + case .edit: + self.headerNode.navigationButtonContainer.performAction?(.edit, nil, nil) + case .proxy: + self.controller?.push(proxySettingsController(context: self.context)) + case .profile: + self.controller?.push(PeerInfoScreenImpl( + context: self.context, + updatedPresentationData: self.controller?.updatedPresentationData, + peerId: self.context.account.peerId, + avatarInitiallyExpanded: false, + isOpenedFromChat: false, + nearbyPeerDistance: nil, + reactionSourceMessageId: nil, + callMessages: [], + isMyProfile: true, + profileGiftsContext: self.data?.profileGiftsContext + )) + case .stories: + push(PeerInfoStoryGridScreen(context: self.context, peerId: self.context.account.peerId, scope: .saved)) + case .savedMessages: + let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId)) + |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in + guard let self, let peer = peer else { + return + } + if let controller = self.controller, let navigationController = controller.navigationController as? NavigationController { + self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(peer))) + } + }) + case .recentCalls: + push(CallListController(context: context, mode: .navigation)) + case .devices: + let _ = (self.activeSessionsContextAndCount.get() + |> take(1) + |> deliverOnMainQueue).startStandalone(next: { [weak self] activeSessionsContextAndCount in + if let strongSelf = self, let activeSessionsContextAndCount = activeSessionsContextAndCount { + let (activeSessionsContext, _, webSessionsContext) = activeSessionsContextAndCount + push(recentSessionsController(context: strongSelf.context, activeSessionsContext: activeSessionsContext, webSessionsContext: webSessionsContext, websitesOnly: false)) + } + }) + case .chatFolders: + let controller = self.context.sharedContext.makeFilterSettingsController(context: self.context, modal: false, scrollToTags: false, dismissed: nil) + push(controller) + case .notificationsAndSounds: + if let settings = self.data?.globalSettings { + push(notificationsAndSoundsController(context: self.context, exceptionsList: settings.notificationExceptions)) + } + case .privacyAndSecurity: + if let settings = self.data?.globalSettings { + let _ = (combineLatest(self.blockedPeers.get(), self.hasTwoStepAuth.get()) + |> take(1) + |> deliverOnMainQueue).startStandalone(next: { [weak self] blockedPeersContext, hasTwoStepAuth in + if let strongSelf = self { + let loginEmailPattern = strongSelf.twoStepAuthData.get() |> map { data -> String? in + return data?.loginEmailPattern + } + push(privacyAndSecurityController(context: strongSelf.context, initialSettings: settings.privacySettings, updatedSettings: { [weak self] settings in + self?.privacySettings.set(.single(settings)) + }, updatedBlockedPeers: { [weak self] blockedPeersContext in + self?.blockedPeers.set(.single(blockedPeersContext)) + }, updatedHasTwoStepAuth: { [weak self] hasTwoStepAuthValue in + self?.hasTwoStepAuth.set(.single(hasTwoStepAuthValue)) + }, focusOnItemTag: nil, activeSessionsContext: settings.activeSessionsContext, webSessionsContext: settings.webSessionsContext, blockedPeersContext: blockedPeersContext, hasTwoStepAuth: hasTwoStepAuth, loginEmailPattern: loginEmailPattern, updatedTwoStepAuthData: { [weak self] in + if let strongSelf = self { + strongSelf.twoStepAuthData.set( + strongSelf.context.engine.auth.twoStepAuthData() + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + ) + } + }, requestPublicPhotoSetup: { [weak self] completion in + if let self { + self.controller?.openAvatarForEditing(mode: .fallback, completion: completion) + } + }, requestPublicPhotoRemove: { [weak self] completion in + if let self { + self.controller?.openAvatarRemoval(mode: .fallback, completion: completion) + } + })) + } + }) + } + case .passwordSetup: + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.6, execute: { [weak self] in + guard let self else { + return + } + let _ = self.context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.setupPassword.id).startStandalone() + }) + + let controller = self.context.sharedContext.makeSetupTwoFactorAuthController(context: self.context) + push(controller) + case .dataAndStorage: + push(dataAndStorageController(context: self.context)) + case .appearance: + push(themeSettingsController(context: self.context)) + case .language: + push(LocalizationListController(context: self.context)) + case .premium: + let controller = self.context.sharedContext.makePremiumIntroController(context: self.context, source: .settings, forceDark: false, dismissed: nil) + self.controller?.push(controller) + case .premiumGift: + guard let controller = self.controller, !controller.presentAccountFrozenInfoIfNeeded() else { + return + } + let _ = (self.context.account.stateManager.contactBirthdays + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] birthdays in + guard let self else { + return + } + let giftsController = self.context.sharedContext.makePremiumGiftController(context: self.context, source: .settings(birthdays), completion: nil) + self.controller?.push(giftsController) + }) + case .stickers: + if let settings = self.data?.globalSettings { + push(installedStickerPacksController(context: self.context, mode: .general, archivedPacks: settings.archivedStickerPacks, updatedPacks: { [weak self] packs in + self?.archivedPacks.set(.single(packs)) + })) + } + case .passport: + self.controller?.push(SecureIdAuthController(context: self.context, mode: .list)) + case .watch: + push(watchSettingsController(context: self.context)) + case .support: + let supportPeer = Promise() + supportPeer.set(context.engine.peers.supportPeerId()) + + self.controller?.present(textAlertController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, title: nil, text: self.presentationData.strings.Settings_FAQ_Intro, actions: [ + TextAlertAction(type: .genericAction, title: presentationData.strings.Settings_FAQ_Button, action: { [weak self] in + self?.openFaq() + }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { [weak self] in + guard let self else { + return + } + self.supportPeerDisposable.set((supportPeer.get() |> take(1) |> deliverOnMainQueue).startStrict(next: { [weak self] peerId in + if let strongSelf = self, let peerId = peerId { + push(strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: nil, botStart: nil, mode: .standard(.default), params: nil)) + } + })) + })]), in: .window(.root)) + case .faq: + self.openFaq() + case .tips: + self.openTips() + case .phoneNumber: + guard let controller = self.controller, !controller.presentAccountFrozenInfoIfNeeded() else { + return + } + if let user = self.data?.peer as? TelegramUser, let phoneNumber = user.phone { + let introController = PrivacyIntroController(context: self.context, mode: .changePhoneNumber(phoneNumber), proceedAction: { [weak self] in + if let strongSelf = self, let navigationController = strongSelf.controller?.navigationController as? NavigationController { + navigationController.replaceTopController(ChangePhoneNumberController(context: strongSelf.context), animated: true) + } + }) + push(introController) + } + case .username: + guard let controller = self.controller, !controller.presentAccountFrozenInfoIfNeeded() else { + return + } + push(usernameSetupController(context: self.context)) + case .addAccount: + let _ = (activeAccountsAndPeers(context: context) + |> take(1) + |> deliverOnMainQueue + ).startStandalone(next: { [weak self] accountAndPeer, accountsAndPeers in + guard let strongSelf = self else { + return + } + var maximumAvailableAccounts: Int = 3 + if accountAndPeer?.1.isPremium == true && !strongSelf.context.account.testingEnvironment { + maximumAvailableAccounts = 4 + } + var count: Int = 1 + for (accountContext, peer, _) in accountsAndPeers { + if !accountContext.account.testingEnvironment { + if peer.isPremium { + maximumAvailableAccounts = 4 + } + count += 1 + } + } + + if count >= maximumAvailableAccounts { + var replaceImpl: ((ViewController) -> Void)? + let controller = PremiumLimitScreen(context: strongSelf.context, subject: .accounts, count: Int32(count), action: { + let controller = PremiumIntroScreen(context: strongSelf.context, source: .accounts) + replaceImpl?(controller) + return true + }) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + if let navigationController = strongSelf.context.sharedContext.mainWindow?.viewController as? NavigationController { + navigationController.pushViewController(controller) + } + } else { + strongSelf.context.sharedContext.beginNewAuth(testingEnvironment: strongSelf.context.account.testingEnvironment) + } + }) + case .logout: + if let user = self.data?.peer as? TelegramUser, let phoneNumber = user.phone { + if let controller = self.controller, let navigationController = controller.navigationController as? NavigationController { + self.controller?.push(logoutOptionsController(context: self.context, navigationController: navigationController, canAddAccounts: true, phoneNumber: phoneNumber)) + } + } + case .rememberPassword: + let context = self.context + let controller = TwoFactorDataInputScreen(sharedContext: self.context.sharedContext, engine: .authorized(self.context.engine), mode: .rememberPassword(doneText: self.presentationData.strings.TwoFactorSetup_Done_Action), stateUpdated: { _ in + }, presentation: .modalInLargeLayout) + controller.twoStepAuthSettingsController = { configuration in + return twoStepVerificationUnlockSettingsController(context: context, mode: .access(intro: false, data: .single(TwoStepVerificationUnlockSettingsControllerData.access(configuration: TwoStepVerificationAccessConfiguration(configuration: configuration, password: nil))))) + } + controller.passwordRemembered = { + let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.validatePassword.id).startStandalone() + } + push(controller) + case .emojiStatus: + self.headerNode.invokeDisplayPremiumIntro() + case .profileColor: + self.interaction.editingOpenNameColorSetup() + case .powerSaving: + push(energySavingSettingsScreen(context: self.context)) + case .businessSetup: + guard let controller = self.controller, !controller.presentAccountFrozenInfoIfNeeded() else { + return + } + push(self.context.sharedContext.makeBusinessSetupScreen(context: self.context)) + case .premiumManagement: + guard let controller = self.controller else { + return + } + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 }) + let url = premiumConfiguration.subscriptionManagementUrl + guard !url.isEmpty else { + return + } + self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: url, forceExternal: !url.hasPrefix("tg://") && !url.contains("?start="), presentationData: self.context.sharedContext.currentPresentationData.with({$0}), navigationController: controller.navigationController as? NavigationController, dismissInput: {}) + case .stars: + if let starsContext = self.controller?.starsContext { + push(self.context.sharedContext.makeStarsTransactionsScreen(context: self.context, starsContext: starsContext)) + } + case .ton: + if let tonContext = self.controller?.tonContext { + push(self.context.sharedContext.makeStarsTransactionsScreen(context: self.context, starsContext: tonContext)) + } + case .ghostgram: + push(ghostgramSettingsController(context: self.context)) + } + } + + func openFaq(anchor: String? = nil) { + let presentationData = self.presentationData + let progressSignal = Signal { [weak self] subscriber in + let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil)) + self?.controller?.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() + + let _ = (self.cachedFaq.get() + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] resolvedUrl in + progressDisposable.dispose() + + if let strongSelf = self, let resolvedUrl = resolvedUrl { + var resolvedUrl = resolvedUrl + if case let .instantView(webPage, _) = resolvedUrl, let customAnchor = anchor { + resolvedUrl = .instantView(webPage, customAnchor) + } + strongSelf.context.sharedContext.openResolvedUrl(resolvedUrl, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.controller?.navigationController as? NavigationController, forceExternal: false, forceUpdate: false, openPeer: { peer, navigation in + }, sendFile: nil, sendSticker: nil, sendEmoji: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { [weak self] controller, arguments in + self?.controller?.push(controller) + }, dismissInput: {}, contentContext: nil, progress: nil, completion: nil) + } + }) + } + + private func openTips() { + let controller = OverlayStatusController(theme: self.presentationData.theme, type: .loading(cancelled: nil)) + self.controller?.present(controller, in: .window(.root)) + + let context = self.context + let navigationController = self.controller?.navigationController as? NavigationController + self.tipsPeerDisposable.set((self.context.engine.peers.resolvePeerByName(name: self.presentationData.strings.Settings_TipsUsername, referrer: nil) + |> mapToSignal { result -> Signal in + guard case let .result(result) = result else { + return .complete() + } + return .single(result) + } + |> deliverOnMainQueue).startStrict(next: { [weak controller] peer in + controller?.dismiss() + if let peer = peer, let navigationController = navigationController { + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer))) + } + })) + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSelectionPanelNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSelectionPanelNode.swift new file mode 100644 index 00000000..2e029773 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSelectionPanelNode.swift @@ -0,0 +1,209 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import TelegramCore +import TelegramPresentationData +import ChatMessageSelectionInputPanelNode +import ChatPresentationInterfaceState +import AccountContext + +final class PeerInfoSelectionPanelNode: ASDisplayNode { + private let context: AccountContext + private let peerId: EnginePeer.Id + + private let deleteMessages: () -> Void + private let shareMessages: () -> Void + private let forwardMessages: () -> Void + private let reportMessages: () -> Void + private let displayCopyProtectionTip: (UIView, Bool) -> Void + + let selectionPanel: ChatMessageSelectionInputPanelNode + let separatorNode: ASDisplayNode + let backgroundNode: NavigationBackgroundNode + + var viewForOverlayContent: UIView? { + return self.selectionPanel.viewForOverlayContent + } + + init(context: AccountContext, presentationData: PresentationData, peerId: EnginePeer.Id, deleteMessages: @escaping () -> Void, shareMessages: @escaping () -> Void, forwardMessages: @escaping () -> Void, reportMessages: @escaping () -> Void, displayCopyProtectionTip: @escaping (UIView, Bool) -> Void) { + self.context = context + self.peerId = peerId + self.deleteMessages = deleteMessages + self.shareMessages = shareMessages + self.forwardMessages = forwardMessages + self.reportMessages = reportMessages + self.displayCopyProtectionTip = displayCopyProtectionTip + + let presentationData = presentationData + + self.separatorNode = ASDisplayNode() + self.backgroundNode = NavigationBackgroundNode(color: presentationData.theme.rootController.navigationBar.blurredBackgroundColor) + + self.selectionPanel = ChatMessageSelectionInputPanelNode(theme: presentationData.theme, strings: presentationData.strings, peerMedia: true) + self.selectionPanel.context = context + + let interfaceInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { _, _, _ in + }, setupEditMessage: { _, _ in + }, beginMessageSelection: { _, _ in + }, cancelMessageSelection: { _ in + }, deleteSelectedMessages: { + deleteMessages() + }, reportSelectedMessages: { + reportMessages() + }, reportMessages: { _, _ in + }, blockMessageAuthor: { _, _ in + }, deleteMessages: { _, _, f in + f(.default) + }, forwardSelectedMessages: { + forwardMessages() + }, forwardCurrentForwardMessages: { + }, forwardMessages: { _ in + }, updateForwardOptionsState: { _ in + }, presentForwardOptions: { _ in + }, presentReplyOptions: { _ in + }, presentLinkOptions: { _ in + }, presentSuggestPostOptions: { + }, shareSelectedMessages: { + shareMessages() + }, updateTextInputStateAndMode: { _ in + }, updateInputModeAndDismissedButtonKeyboardMessageId: { _ in + }, openStickers: { + }, editMessage: { + }, beginMessageSearch: { _, _ in + }, dismissMessageSearch: { + }, updateMessageSearch: { _ in + }, openSearchResults: { + }, navigateMessageSearch: { _ in + }, openCalendarSearch: { + }, toggleMembersSearch: { _ in + }, navigateToMessage: { _, _, _, _ in + }, navigateToChat: { _ in + }, navigateToProfile: { _ in + }, openPeerInfo: { + }, togglePeerNotifications: { + }, sendContextResult: { _, _, _, _ in + return false + }, sendBotCommand: { _, _ in + }, sendShortcut: { _ in + }, openEditShortcuts: { + }, sendBotStart: { _ in + }, botSwitchChatWithPayload: { _, _ in + }, beginMediaRecording: { _ in + }, finishMediaRecording: { _ in + }, stopMediaRecording: { + }, lockMediaRecording: { + }, resumeMediaRecording: { + }, deleteRecordedMedia: { + }, sendRecordedMedia: { _, _ in + }, displayRestrictedInfo: { _, _ in + }, displayVideoUnmuteTip: { _ in + }, switchMediaRecordingMode: { + }, setupMessageAutoremoveTimeout: { + }, sendSticker: { _, _, _, _, _, _ in + return false + }, unblockPeer: { + }, pinMessage: { _, _ in + }, unpinMessage: { _, _, _ in + }, unpinAllMessages: { + }, openPinnedList: { _ in + }, shareAccountContact: { + }, reportPeer: { + }, presentPeerContact: { + }, dismissReportPeer: { + }, deleteChat: { + }, beginCall: { _ in + }, toggleMessageStickerStarred: { _ in + }, presentController: { _, _ in + }, presentControllerInCurrent: { _, _ in + }, getNavigationController: { + return nil + }, presentGlobalOverlayController: { _, _ in + }, navigateFeed: { + }, openGrouping: { + }, toggleSilentPost: { + }, requestUnvoteInMessage: { _ in + }, requestStopPollInMessage: { _ in + }, updateInputLanguage: { _ in + }, unarchiveChat: { + }, openLinkEditing: { + }, displaySlowmodeTooltip: { _, _ in + }, displaySendMessageOptions: { _, _ in + }, openScheduledMessages: { + }, openPeersNearby: { + }, displaySearchResultsTooltip: { _, _ in + }, unarchivePeer: { + }, scrollToTop: { + }, viewReplies: { _, _ in + }, activatePinnedListPreview: { _, _ in + }, joinGroupCall: { _ in + }, presentInviteMembers: { + }, presentGigagroupHelp: { + }, openMonoforum: { + }, editMessageMedia: { _, _ in + }, updateShowCommands: { _ in + }, updateShowSendAsPeers: { _ in + }, openInviteRequests: { + }, openSendAsPeer: { _, _ in + }, presentChatRequestAdminInfo: { + }, displayCopyProtectionTip: { view, save in + displayCopyProtectionTip(view, save) + }, openWebView: { _, _, _, _ in + }, updateShowWebView: { _ in + }, insertText: { _ in + }, backwardsDeleteText: { + }, restartTopic: { + }, toggleTranslation: { _ in + }, changeTranslationLanguage: { _ in + }, addDoNotTranslateLanguage: { _ in + }, hideTranslationPanel: { + }, openPremiumGift: { + }, openSuggestPost: { _, _ in + }, openPremiumRequiredForMessaging: { + }, openStarsPurchase: { _ in + }, openMessagePayment: { + }, openBoostToUnrestrict: { + }, updateRecordingTrimRange: { _, _, _, _ in + }, dismissAllTooltips: { + }, editTodoMessage: { _, _, _ in + }, dismissUrlPreview: { + }, dismissForwardMessages: { + }, dismissSuggestPost: { + }, displayUndo: { _ in + }, sendEmoji: { _, _, _ in + }, updateHistoryFilter: { _ in + }, updateChatLocationThread: { _, _ in + }, toggleChatSidebarMode: { + }, updateDisplayHistoryFilterAsList: { _ in + }, requestLayout: { _ in + }, chatController: { + return nil + }, statuses: nil) + + self.selectionPanel.interfaceInteraction = interfaceInteraction + + super.init() + + self.addSubnode(self.backgroundNode) + self.addSubnode(self.separatorNode) + self.addSubnode(self.selectionPanel) + } + + func update(layout: ContainerViewLayout, presentationData: PresentationData, transition: ContainedViewLayoutTransition) -> CGFloat { + self.backgroundNode.updateColor(color: presentationData.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate) + self.separatorNode.backgroundColor = presentationData.theme.rootController.navigationBar.separatorColor + + let interfaceState = ChatPresentationInterfaceState(chatWallpaper: .color(0), theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, limitsConfiguration: .defaultValue, fontSize: .regular, bubbleCorners: PresentationChatBubbleCorners(mainRadius: 16.0, auxiliaryRadius: 8.0, mergeBubbleCorners: true), accountPeerId: self.context.account.peerId, mode: .standard(.default), chatLocation: .peer(id: self.peerId), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil, businessIntro: nil) + let panelHeight = self.selectionPanel.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: layout.intrinsicInsets.bottom, additionalSideInsets: UIEdgeInsets(), maxHeight: layout.size.height, maxOverlayHeight: layout.size.height, isSecondary: false, transition: transition, interfaceState: interfaceState, metrics: layout.metrics, isMediaInputExpanded: false) + + transition.updateFrame(node: self.selectionPanel, frame: CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: panelHeight))) + + let panelHeightWithInset = panelHeight + layout.intrinsicInsets.bottom + + transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: panelHeightWithInset))) + self.backgroundNode.update(size: self.backgroundNode.bounds.size, transition: transition) + transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: layout.size.width, height: UIScreenPixel))) + + return panelHeightWithInset + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSettingsItems.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSettingsItems.swift new file mode 100644 index 00000000..e59306be --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSettingsItems.swift @@ -0,0 +1,509 @@ +import Foundation +import UIKit +import Display +import AccountContext +import TelegramPresentationData +import TelegramCore +import Postbox +import PhoneNumberFormat +import ItemListUI +import SwiftSignalKit +import PhotoResources +import ItemListPeerItem +import DeviceAccess +import TelegramStringFormatting +import PeerNameColorItem + +enum SettingsSection: Int, CaseIterable { + case edit + case phone + case accounts + case myProfile + case proxy + case apps + case shortcuts + case advanced + case payment + case extra + case support +} + +func settingsItems(data: PeerInfoScreenData?, context: AccountContext, presentationData: PresentationData, interaction: PeerInfoInteraction, isExpanded: Bool) -> [(AnyHashable, [PeerInfoScreenItem])] { + guard let data = data else { + return [] + } + + var items: [SettingsSection: [PeerInfoScreenItem]] = [:] + for section in SettingsSection.allCases { + items[section] = [] + } + + let setPhotoTitle: String + if let peer = data.peer, !peer.profileImageRepresentations.isEmpty { + setPhotoTitle = presentationData.strings.Settings_ChangeProfilePhoto + } else { + setPhotoTitle = presentationData.strings.Settings_SetProfilePhotoOrVideo + } + + var setStatusTitle: String = "" + let displaySetStatus: Bool + var hasEmojiStatus = false + if let peer = data.peer as? TelegramUser, peer.isPremium { + if peer.emojiStatus != nil { + hasEmojiStatus = true + setStatusTitle = presentationData.strings.PeerInfo_ChangeEmojiStatus + } else { + setStatusTitle = presentationData.strings.PeerInfo_SetEmojiStatus + } + displaySetStatus = true + } else { + displaySetStatus = false + } + + if displaySetStatus { + items[.edit]!.append(PeerInfoScreenActionItem(id: 0, text: setStatusTitle, icon: UIImage(bundleImageName: hasEmojiStatus ? "Settings/EditEmojiStatus" : "Settings/SetEmojiStatus"), action: { + interaction.openSettings(.emojiStatus) + })) + + items[.edit]!.append(PeerInfoScreenActionItem(id: 1, text: presentationData.strings.PeerInfo_ChangeProfileColor, icon: UIImage(bundleImageName: "Premium/BoostPerk/CoverColor"), action: { + interaction.openSettings(.profileColor) + })) + } + + items[.edit]!.append(PeerInfoScreenActionItem(id: 2, text: setPhotoTitle, icon: UIImage(bundleImageName: "Settings/SetAvatar"), action: { + interaction.openSettings(.avatar) + })) + + if let peer = data.peer, (peer.addressName ?? "").isEmpty { + items[.edit]!.append(PeerInfoScreenActionItem(id: 3, text: presentationData.strings.Settings_SetUsername, icon: UIImage(bundleImageName: "Settings/SetUsername"), action: { + interaction.openSettings(.username) + })) + } + + if let settings = data.globalSettings { + if settings.premiumGracePeriod { + items[.phone]!.append(PeerInfoScreenInfoItem(id: 0, title: "Your access to Telegram Premium will expire soon!", text: .markdown("Unfortunately, your latest payment didn't come through. To keep your access to exclusive features, please renew the subscription."), isWarning: true, linkAction: nil)) + items[.phone]!.append(PeerInfoScreenActionItem(id: 1, text: "Restore Subscription", action: { + interaction.openSettings(.premiumManagement) + })) + } else if settings.suggestPhoneNumberConfirmation, let peer = data.peer as? TelegramUser { + let phoneNumber = formatPhoneNumber(context: context, number: peer.phone ?? "") + items[.phone]!.append(PeerInfoScreenInfoItem(id: 0, title: presentationData.strings.Settings_CheckPhoneNumberTitle(phoneNumber).string, text: .markdown(presentationData.strings.Settings_CheckPhoneNumberText), linkAction: { link in + if case .tap = link { + interaction.openFaq(presentationData.strings.Settings_CheckPhoneNumberFAQAnchor) + } + })) + items[.phone]!.append(PeerInfoScreenActionItem(id: 1, text: presentationData.strings.Settings_KeepPhoneNumber(phoneNumber).string, action: { + let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.validatePhoneNumber.id).startStandalone() + })) + items[.phone]!.append(PeerInfoScreenActionItem(id: 2, text: presentationData.strings.Settings_ChangePhoneNumber, action: { + interaction.openSettings(.phoneNumber) + })) + } else if settings.suggestPasswordConfirmation { + items[.phone]!.append(PeerInfoScreenInfoItem(id: 0, title: presentationData.strings.Settings_CheckPasswordTitle, text: .markdown(presentationData.strings.Settings_CheckPasswordText), linkAction: { _ in + })) + items[.phone]!.append(PeerInfoScreenActionItem(id: 1, text: presentationData.strings.Settings_KeepPassword, action: { + let _ = context.engine.notices.dismissServerProvidedSuggestion(suggestion: ServerProvidedSuggestion.validatePassword.id).startStandalone() + })) + items[.phone]!.append(PeerInfoScreenActionItem(id: 2, text: presentationData.strings.Settings_TryEnterPassword, action: { + interaction.openSettings(.rememberPassword) + })) + } else if settings.suggestPasswordSetup { + items[.phone]!.append(PeerInfoScreenInfoItem(id: 0, title: presentationData.strings.Settings_SuggestSetupPasswordTitle, text: .markdown(presentationData.strings.Settings_SuggestSetupPasswordText), linkAction: { _ in + })) + items[.phone]!.append(PeerInfoScreenActionItem(id: 2, text: presentationData.strings.Settings_SuggestSetupPasswordAction, action: { + interaction.openSettings(.passwordSetup) + })) + } + + if !settings.accountsAndPeers.isEmpty { + for (peerAccountContext, peer, badgeCount) in settings.accountsAndPeers { + let mappedContext = ItemListPeerItem.Context.custom(ItemListPeerItem.Context.Custom( + accountPeerId: peerAccountContext.account.peerId, + postbox: peerAccountContext.account.postbox, + network: peerAccountContext.account.network, + animationCache: context.animationCache, + animationRenderer: context.animationRenderer, + isPremiumDisabled: false, + resolveInlineStickers: { fileIds in + return context.engine.stickers.resolveInlineStickers(fileIds: fileIds) + } + )) + let member: PeerInfoMember = .account(peer: RenderedPeer(peer: peer._asPeer())) + items[.accounts]!.append(PeerInfoScreenMemberItem(id: member.id, context: mappedContext, enclosingPeer: nil, member: member, badge: badgeCount > 0 ? "\(compactNumericCountString(Int(badgeCount), decimalSeparator: presentationData.dateTimeFormat.decimalSeparator))" : nil, isAccount: true, action: { action in + switch action { + case .open: + interaction.switchToAccount(peerAccountContext.account.id) + case .remove: + interaction.logoutAccount(peerAccountContext.account.id) + default: + break + } + }, contextAction: { node, gesture in + interaction.accountContextMenu(peerAccountContext.account.id, node, gesture) + })) + } + + items[.accounts]!.append(PeerInfoScreenActionItem(id: 100, text: presentationData.strings.Settings_AddAccount, icon: PresentationResourcesItemList.plusIconImage(presentationData.theme), action: { + interaction.openSettings(.addAccount) + })) + } + + items[.myProfile]!.append(PeerInfoScreenDisclosureItem(id: 0, text: presentationData.strings.Settings_MyProfile, icon: PresentationResourcesSettings.myProfile, action: { + interaction.openSettings(.profile) + })) + + if !settings.proxySettings.servers.isEmpty { + let proxyType: String + if settings.proxySettings.enabled, let activeServer = settings.proxySettings.activeServer { + switch activeServer.connection { + case .mtp: + proxyType = presentationData.strings.SocksProxySetup_ProxyTelegram + case .socks5: + proxyType = presentationData.strings.SocksProxySetup_ProxySocks5 + } + } else { + proxyType = presentationData.strings.Settings_ProxyDisabled + } + items[.proxy]!.append(PeerInfoScreenDisclosureItem(id: 0, label: .text(proxyType), text: presentationData.strings.Settings_Proxy, icon: PresentationResourcesSettings.proxy, action: { + interaction.openSettings(.proxy) + })) + } + } + + var appIndex = 1000 + if let settings = data.globalSettings { + for bot in settings.bots { + let iconSignal: Signal + if let peer = PeerReference(bot.peer._asPeer()), let icon = bot.icons[.iOSSettingsStatic] { + let fileReference: FileMediaReference = .attachBot(peer: peer, media: icon) + iconSignal = instantPageImageFile(account: context.account, userLocation: .other, fileReference: fileReference, fetched: true) + |> map { generator -> UIImage? in + let size = CGSize(width: 29.0, height: 29.0) + let context = generator(TransformImageArguments(corners: ImageCorners(), imageSize: size, boundingSize: size, intrinsicInsets: .zero)) + return context?.generateImage() + } + let _ = freeMediaFileInteractiveFetched(account: context.account, userLocation: .other, fileReference: fileReference).startStandalone() + } else { + iconSignal = .single(UIImage()) + } + let label: PeerInfoScreenDisclosureItem.Label = bot.flags.contains(.notActivated) || bot.flags.contains(.showInSettingsDisclaimer) ? .titleBadge(presentationData.strings.Settings_New, presentationData.theme.list.itemAccentColor) : .none + items[.apps]!.append(PeerInfoScreenDisclosureItem(id: bot.peer.id.id._internalGetInt64Value(), label: label, text: bot.shortName, icon: nil, iconSignal: iconSignal, action: { + interaction.openBotApp(bot) + })) + appIndex += 1 + } + } + + items[.shortcuts]!.append(PeerInfoScreenDisclosureItem(id: 1, text: presentationData.strings.Settings_SavedMessages, icon: PresentationResourcesSettings.savedMessages, action: { + interaction.openSettings(.savedMessages) + })) + items[.shortcuts]!.append(PeerInfoScreenDisclosureItem(id: 2, text: presentationData.strings.CallSettings_RecentCalls, icon: PresentationResourcesSettings.recentCalls, action: { + interaction.openSettings(.recentCalls) + })) + + let devicesLabel: String + if let settings = data.globalSettings, let otherSessionsCount = settings.otherSessionsCount { + if settings.enableQRLogin { + devicesLabel = otherSessionsCount == 0 ? presentationData.strings.Settings_AddDevice : "\(otherSessionsCount + 1)" + } else { + devicesLabel = otherSessionsCount == 0 ? "" : "\(otherSessionsCount + 1)" + } + } else { + devicesLabel = "" + } + + items[.shortcuts]!.append(PeerInfoScreenDisclosureItem(id: 3, label: .text(devicesLabel), text: presentationData.strings.Settings_Devices, icon: PresentationResourcesSettings.devices, action: { + interaction.openSettings(.devices) + })) + items[.shortcuts]!.append(PeerInfoScreenDisclosureItem(id: 4, text: presentationData.strings.Settings_ChatFolders, icon: PresentationResourcesSettings.chatFolders, action: { + interaction.openSettings(.chatFolders) + })) + + let notificationsWarning: Bool + if let settings = data.globalSettings { + notificationsWarning = shouldDisplayNotificationsPermissionWarning(status: settings.notificationAuthorizationStatus, suppressed: settings.notificationWarningSuppressed) + } else { + notificationsWarning = false + } + items[.advanced]!.append(PeerInfoScreenDisclosureItem(id: 0, label: notificationsWarning ? .badge("!", presentationData.theme.list.itemDestructiveColor) : .none, text: presentationData.strings.Settings_NotificationsAndSounds, icon: PresentationResourcesSettings.notifications, action: { + interaction.openSettings(.notificationsAndSounds) + })) + items[.advanced]!.append(PeerInfoScreenDisclosureItem(id: 1, text: presentationData.strings.Settings_PrivacySettings, icon: PresentationResourcesSettings.security, action: { + interaction.openSettings(.privacyAndSecurity) + })) + items[.advanced]!.append(PeerInfoScreenDisclosureItem(id: 1001, text: "Ghostgram Settings", icon: UIImage(bundleImageName: "Settings/Menu/Appearance"), action: { + interaction.openSettings(.ghostgram) + })) + items[.advanced]!.append(PeerInfoScreenDisclosureItem(id: 2, text: presentationData.strings.Settings_ChatSettings, icon: PresentationResourcesSettings.dataAndStorage, action: { + interaction.openSettings(.dataAndStorage) + })) + items[.advanced]!.append(PeerInfoScreenDisclosureItem(id: 3, text: presentationData.strings.Settings_Appearance, icon: PresentationResourcesSettings.appearance, action: { + interaction.openSettings(.appearance) + })) + + items[.advanced]!.append(PeerInfoScreenDisclosureItem(id: 6, label: .text(data.isPowerSavingEnabled == true ? presentationData.strings.Settings_PowerSavingOn : presentationData.strings.Settings_PowerSavingOff), text: presentationData.strings.Settings_PowerSaving, icon: PresentationResourcesSettings.powerSaving, action: { + interaction.openSettings(.powerSaving) + })) + + let languageName = presentationData.strings.primaryComponent.localizedName + items[.advanced]!.append(PeerInfoScreenDisclosureItem(id: 4, label: .text(languageName.isEmpty ? presentationData.strings.Localization_LanguageName : languageName), text: presentationData.strings.Settings_AppLanguage, icon: PresentationResourcesSettings.language, action: { + interaction.openSettings(.language) + })) + + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) + let isPremiumDisabled = premiumConfiguration.isPremiumDisabled + if !isPremiumDisabled || context.isPremium { + items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 100, label: .text(""), text: presentationData.strings.Settings_Premium, icon: PresentationResourcesSettings.premium, action: { + interaction.openSettings(.premium) + })) + } + if let starsState = data.starsState { + if !isPremiumDisabled || abs(starsState.balance.value) > 0 { + let balanceText: NSAttributedString + if abs(starsState.balance.value) > 0 { + let formattedLabel = formatStarsAmountText(starsState.balance, dateTimeFormat: presentationData.dateTimeFormat) + let smallLabelFont = Font.regular(floor(presentationData.listsFontSize.itemListBaseFontSize / 17.0 * 13.0)) + let labelFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize) + let labelColor = presentationData.theme.list.itemSecondaryTextColor + balanceText = tonAmountAttributedString(formattedLabel, integralFont: labelFont, fractionalFont: smallLabelFont, color: labelColor, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator) + } else { + balanceText = NSAttributedString() + } + items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 102, label: .attributedText(balanceText), text: presentationData.strings.Settings_Stars, icon: PresentationResourcesSettings.stars, action: { + interaction.openSettings(.stars) + })) + } + } + if let tonState = data.tonState { + if abs(tonState.balance.value) > 0 { + let balanceText: NSAttributedString + if abs(tonState.balance.value) > 0 { + let formattedLabel = formatTonAmountText(tonState.balance.value, dateTimeFormat: presentationData.dateTimeFormat) + let smallLabelFont = Font.regular(floor(presentationData.listsFontSize.itemListBaseFontSize / 17.0 * 13.0)) + let labelFont = Font.regular(presentationData.listsFontSize.itemListBaseFontSize) + let labelColor = presentationData.theme.list.itemSecondaryTextColor + balanceText = tonAmountAttributedString(formattedLabel, integralFont: labelFont, fractionalFont: smallLabelFont, color: labelColor, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator) + } else { + balanceText = NSAttributedString() + } + items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 103, label: .attributedText(balanceText), text: presentationData.strings.Settings_MyTon, icon: PresentationResourcesSettings.ton, action: { + interaction.openSettings(.ton) + })) + } + } + if !isPremiumDisabled || context.isPremium { + items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 104, label: .text(""), additionalBadgeLabel: nil, text: presentationData.strings.Settings_Business, icon: PresentationResourcesSettings.business, action: { + interaction.openSettings(.businessSetup) + })) + } + if let starsState = data.starsState { + if !isPremiumDisabled || starsState.balance > StarsAmount.zero { + items[.payment]!.append(PeerInfoScreenDisclosureItem(id: 105, label: .text(""), text: presentationData.strings.Settings_SendGift, icon: PresentationResourcesSettings.premiumGift, action: { + interaction.openSettings(.premiumGift) + })) + } + } + + if let settings = data.globalSettings { + if settings.hasPassport { + items[.extra]!.append(PeerInfoScreenDisclosureItem(id: 0, text: presentationData.strings.Settings_Passport, icon: PresentationResourcesSettings.passport, action: { + interaction.openSettings(.passport) + })) + } + if settings.hasWatchApp { + items[.extra]!.append(PeerInfoScreenDisclosureItem(id: 1, text: presentationData.strings.Settings_AppleWatch, icon: PresentationResourcesSettings.watch, action: { + interaction.openSettings(.watch) + })) + } + } + + items[.support]!.append(PeerInfoScreenDisclosureItem(id: 0, text: presentationData.strings.Settings_Support, icon: PresentationResourcesSettings.support, action: { + interaction.openSettings(.support) + })) + items[.support]!.append(PeerInfoScreenDisclosureItem(id: 1, text: presentationData.strings.Settings_FAQ, icon: PresentationResourcesSettings.faq, action: { + interaction.openSettings(.faq) + })) + items[.support]!.append(PeerInfoScreenDisclosureItem(id: 2, text: presentationData.strings.Settings_Tips, icon: PresentationResourcesSettings.tips, action: { + interaction.openSettings(.tips) + })) + + var result: [(AnyHashable, [PeerInfoScreenItem])] = [] + for section in SettingsSection.allCases { + if let sectionItems = items[section], !sectionItems.isEmpty { + result.append((section, sectionItems)) + } + } + return result +} + +func settingsEditingItems(data: PeerInfoScreenData?, state: PeerInfoState, context: AccountContext, presentationData: PresentationData, interaction: PeerInfoInteraction, isMyProfile: Bool) -> [(AnyHashable, [PeerInfoScreenItem])] { + guard let data = data else { + return [] + } + + enum Section: Int, CaseIterable { + case help + case bio + case birthday + case info + case account + case logout + } + + var items: [Section: [PeerInfoScreenItem]] = [:] + for section in Section.allCases { + items[section] = [] + } + + let ItemNameHelp = 0 + let ItemBio: AnyHashable = AnyHashable("bio_edit") + let ItemBioHelp = 2 + let ItemPhoneNumber = 3 + let ItemUsername = 4 + let ItemAddAccount = 5 + let ItemAddAccountHelp = 6 + let ItemLogout = 7 + let ItemPeerColor = 8 + let ItemBirthday = 9 + let ItemBirthdayPicker = 10 + let ItemBirthdayRemove = 11 + let ItemBirthdayHelp = 12 + let ItemPeerPersonalChannel = 13 + + items[.help]!.append(PeerInfoScreenCommentItem(id: ItemNameHelp, text: presentationData.strings.EditProfile_NameAndPhotoOrVideoHelp)) + + if let cachedData = data.cachedData as? CachedUserData { + items[.bio]!.append(PeerInfoScreenMultilineInputItem(id: ItemBio, text: state.updatingBio ?? (cachedData.about ?? ""), placeholder: presentationData.strings.UserInfo_About_Placeholder, textUpdated: { updatedText in + interaction.updateBio(updatedText) + }, action: { + interaction.dismissInput() + }, maxLength: Int(data.globalSettings?.userLimits.maxAboutLength ?? 70))) + items[.bio]!.append(PeerInfoScreenCommentItem(id: ItemBioHelp, text: presentationData.strings.Settings_About_PrivacyHelp, linkAction: { _ in + interaction.openBioPrivacy() + })) + } + + + var birthday: TelegramBirthday? + if let updatingBirthDate = state.updatingBirthDate { + birthday = updatingBirthDate + } else { + birthday = (data.cachedData as? CachedUserData)?.birthday + } + + var birthDateString: String + if let birthday { + birthDateString = stringForCompactBirthday(birthday, strings: presentationData.strings) + } else { + birthDateString = presentationData.strings.Settings_Birthday_Add + } + + let isEditingBirthDate = state.isEditingBirthDate + items[.birthday]!.append(PeerInfoScreenDisclosureItem(id: ItemBirthday, label: .coloredText(birthDateString, isEditingBirthDate ? .accent : .generic), text: presentationData.strings.Settings_Birthday, icon: nil, hasArrow: false, action: { + interaction.updateIsEditingBirthdate(!isEditingBirthDate) + })) + if isEditingBirthDate, let birthday { + items[.birthday]!.append(PeerInfoScreenBirthdatePickerItem(id: ItemBirthdayPicker, value: birthday, valueUpdated: { value in + interaction.updateBirthdate(value) + })) + items[.birthday]!.append(PeerInfoScreenActionItem(id: ItemBirthdayRemove, text: presentationData.strings.Settings_Birthday_Remove, alignment: .natural, action: { + interaction.updateBirthdate(.some(nil)) + interaction.updateIsEditingBirthdate(false) + })) + } + + + var birthdayIsForContactsOnly = false + if let birthdayPrivacy = data.globalSettings?.privacySettings?.birthday, case .enableContacts = birthdayPrivacy { + birthdayIsForContactsOnly = true + } + items[.birthday]!.append(PeerInfoScreenCommentItem(id: ItemBirthdayHelp, text: birthdayIsForContactsOnly ? presentationData.strings.Settings_Birthday_ContactsHelp : presentationData.strings.Settings_Birthday_Help, linkAction: { _ in + interaction.openBirthdatePrivacy() + })) + + if let user = data.peer as? TelegramUser { + items[.info]!.append(PeerInfoScreenDisclosureItem(id: ItemPhoneNumber, label: .text(user.phone.flatMap({ formatPhoneNumber(context: context, number: $0) }) ?? ""), text: presentationData.strings.Settings_PhoneNumber, action: { + interaction.openSettings(.phoneNumber) + })) + } + var username = "" + if let addressName = data.peer?.addressName, !addressName.isEmpty { + username = "@\(addressName)" + } + items[.info]!.append(PeerInfoScreenDisclosureItem(id: ItemUsername, label: .text(username), text: presentationData.strings.Settings_Username, action: { + interaction.openSettings(.username) + })) + + if let peer = data.peer as? TelegramUser { + var colors: [PeerNameColors.Colors] = [] + if let nameColor = peer.nameColor { + let nameColors: PeerNameColors.Colors + switch nameColor { + case let .preset(nameColor): + nameColors = context.peerNameColors.get(nameColor, dark: presentationData.theme.overallDarkAppearance) + case let .collectible(collectibleColor): + nameColors = collectibleColor.peerNameColors(dark: presentationData.theme.overallDarkAppearance) + } + colors.append(nameColors) + } + if let profileColor = peer.effectiveProfileColor.flatMap({ context.peerNameColors.getProfile($0, dark: presentationData.theme.overallDarkAppearance, subject: .palette) }) { + colors.append(profileColor) + } + let colorImage = generateSettingsMenuPeerColorsLabelIcon(colors: colors) + + items[.info]!.append(PeerInfoScreenDisclosureItem(id: ItemPeerColor, label: .image(colorImage, colorImage.size), text: presentationData.strings.Settings_YourColor, icon: nil, action: { + interaction.editingOpenNameColorSetup() + })) + + var displayPersonalChannel = false + if data.personalChannel != nil { + displayPersonalChannel = true + } else if let personalChannels = state.personalChannels, !personalChannels.isEmpty { + displayPersonalChannel = true + } + if displayPersonalChannel { + var personalChannelTitle: String? + if let personalChannel = data.personalChannel, let peer = personalChannel.peer.chatOrMonoforumMainPeer { + personalChannelTitle = peer.compactDisplayTitle + } + + items[.info]!.append(PeerInfoScreenDisclosureItem(id: ItemPeerPersonalChannel, label: .text(personalChannelTitle ?? presentationData.strings.Settings_PersonalChannelEmptyValue), text: presentationData.strings.Settings_PersonalChannelItem, icon: nil, action: { + interaction.editingOpenPersonalChannel() + })) + } + } + + items[.account]!.append(PeerInfoScreenActionItem(id: ItemAddAccount, text: presentationData.strings.Settings_AddAnotherAccount, alignment: .center, action: { + interaction.openSettings(.addAccount) + })) + + var hasPremiumAccounts = false + if data.peer?.isPremium == true && !context.account.testingEnvironment { + hasPremiumAccounts = true + } + if let settings = data.globalSettings { + for (accountContext, peer, _) in settings.accountsAndPeers { + if !accountContext.account.testingEnvironment { + if peer.isPremium { + hasPremiumAccounts = true + break + } + } + } + } + + items[.account]!.append(PeerInfoScreenCommentItem(id: ItemAddAccountHelp, text: hasPremiumAccounts ? presentationData.strings.Settings_AddAnotherAccount_PremiumHelp : presentationData.strings.Settings_AddAnotherAccount_Help)) + + items[.logout]!.append(PeerInfoScreenActionItem(id: ItemLogout, text: presentationData.strings.Settings_Logout, color: .destructive, alignment: .center, action: { + interaction.openSettings(.logout) + })) + + var result: [(AnyHashable, [PeerInfoScreenItem])] = [] + for section in Section.allCases { + if let sectionItems = items[section], !sectionItems.isEmpty { + result.append((section, sectionItems)) + } + } + return result +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSettingsTabActions.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSettingsTabActions.swift new file mode 100644 index 00000000..5c9bb8c7 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSettingsTabActions.swift @@ -0,0 +1,322 @@ +import Foundation +import UIKit +import Display +import AccountContext +import TelegramPresentationData +import SwiftSignalKit +import ContextUI +import TelegramCore +import AvatarNode +import AsyncDisplayKit +import ComponentFlow +import ComponentDisplayAdapters +import EmojiStatusComponent + +extension PeerInfoScreenNode { + func accountContextMenuItems(context: AccountContext, logout: @escaping () -> Void) -> Signal<[ContextMenuItem], NoError> { + let strings = context.sharedContext.currentPresentationData.with({ $0 }).strings + return context.engine.messages.unreadChatListPeerIds(groupId: .root, filterPredicate: nil) + |> map { unreadChatListPeerIds -> [ContextMenuItem] in + var items: [ContextMenuItem] = [] + + if !unreadChatListPeerIds.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.engine.messages.markAllChatsAsReadInteractively(items: [(groupId: .root, filterPredicate: nil)]) + |> deliverOnMainQueue).startStandalone(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 + } + } + + func accountContextMenu(id: AccountRecordId, node: ASDisplayNode, gesture: ContextGesture?) { + var selectedAccount: Account? + let _ = (self.accountsAndPeers.get() + |> take(1) + |> deliverOnMainQueue).startStandalone(next: { accountsAndPeers in + for (account, _, _) in accountsAndPeers { + if account.account.id == id { + selectedAccount = account.account + break + } + } + }) + if let selectedAccount = selectedAccount { + let accountContext = self.context.sharedContext.makeTempAccountContext(account: selectedAccount) + let chatListController = accountContext.sharedContext.makeChatListController(context: accountContext, location: .chatList(groupId: EngineChatList.Group(.root)), controlsHistoryPreload: false, hideNetworkActivityStatus: true, previewing: true, enableDebugActions: false) + + let contextController = ContextController(presentationData: self.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node)), items: accountContextMenuItems(context: accountContext, logout: { [weak self] in + self?.logoutAccount(id: id) + }) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture) + self.controller?.presentInGlobalOverlay(contextController) + } else { + gesture?.cancel() + } + } + + func switchToAccount(id: AccountRecordId) { + self.accountsAndPeers.set(.never()) + self.context.sharedContext.switchToAccount(id: id, fromSettingsController: nil, withChatListController: nil) + } + + func logoutAccount(id: AccountRecordId) { + let controller = ActionSheetController(presentationData: self.presentationData) + let dismissAction: () -> Void = { [weak controller] in + controller?.dismissAnimated() + } + + var items: [ActionSheetItem] = [] + items.append(ActionSheetTextItem(title: self.presentationData.strings.Settings_LogoutConfirmationText.trimmingCharacters(in: .whitespacesAndNewlines))) + items.append(ActionSheetButtonItem(title: self.presentationData.strings.Settings_Logout, color: .destructive, action: { [weak self] in + dismissAction() + if let strongSelf = self { + let _ = logoutFromAccount(id: id, accountManager: strongSelf.context.sharedContext.accountManager, alreadyLoggedOutRemotely: false).startStandalone() + } + })) + controller.setItemGroups([ + ActionSheetItemGroup(items: items), + ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) + ]) + self.controller?.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + } +} + +final class AccountPeerContextItem: ContextMenuCustomItem { + let context: AccountContext + let account: Account + let peer: EnginePeer + let action: (ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void + + init(context: AccountContext, account: Account, peer: EnginePeer, action: @escaping (ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void) { + self.context = context + self.account = account + self.peer = peer + self.action = action + } + + public func node(presentationData: PresentationData, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void) -> ContextMenuCustomNode { + return AccountPeerContextItemNode(presentationData: presentationData, item: self, getController: getController, actionSelected: actionSelected) + } +} + +private final class AccountPeerContextItemNode: ASDisplayNode, ContextMenuCustomNode { + private let item: AccountPeerContextItem + private let presentationData: PresentationData + private let getController: () -> ContextControllerProtocol? + private let actionSelected: (ContextMenuActionResult) -> Void + + private let highlightedBackgroundNode: ASDisplayNode + private let buttonNode: HighlightTrackingButtonNode + private let textNode: ImmediateTextNode + private let avatarNode: AvatarNode + private let emojiStatusView: ComponentView + + init(presentationData: PresentationData, item: AccountPeerContextItem, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void) { + self.item = item + self.presentationData = presentationData + self.getController = getController + self.actionSelected = actionSelected + + let textFont = Font.regular(presentationData.listsFontSize.baseDisplaySize * 17.0 / 17.0) + + self.highlightedBackgroundNode = ASDisplayNode() + self.highlightedBackgroundNode.isAccessibilityElement = false + self.highlightedBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor + self.highlightedBackgroundNode.alpha = 0.0 + + self.textNode = ImmediateTextNode() + self.textNode.isAccessibilityElement = false + self.textNode.isUserInteractionEnabled = false + self.textNode.displaysAsynchronously = false + let peerTitle = item.peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + self.textNode.attributedText = NSAttributedString(string: peerTitle, font: textFont, textColor: presentationData.theme.contextMenu.primaryColor) + self.textNode.maximumNumberOfLines = 1 + + self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 14.0)) + + self.emojiStatusView = ComponentView() + + self.buttonNode = HighlightTrackingButtonNode() + self.buttonNode.isAccessibilityElement = true + self.buttonNode.accessibilityLabel = peerTitle + + super.init() + + self.addSubnode(self.highlightedBackgroundNode) + self.addSubnode(self.textNode) + self.addSubnode(self.avatarNode) + self.addSubnode(self.buttonNode) + + self.buttonNode.highligthedChanged = { [weak self] highligted in + guard let strongSelf = self else { + return + } + if highligted { + strongSelf.highlightedBackgroundNode.alpha = 1.0 + } else { + strongSelf.highlightedBackgroundNode.alpha = 0.0 + strongSelf.highlightedBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3) + } + } + self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside) + } + + func updateLayout(constrainedWidth: CGFloat, constrainedHeight: CGFloat) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) { + let sideInset: CGFloat = 16.0 + let iconSideInset: CGFloat = 12.0 + let verticalInset: CGFloat = 12.0 + + let iconSize = CGSize(width: 28.0, height: 28.0) + + let standardIconWidth: CGFloat = 32.0 + var rightTextInset: CGFloat = sideInset + if !iconSize.width.isZero { + rightTextInset = max(iconSize.width, standardIconWidth) + iconSideInset + sideInset - 12.0 + } + + self.avatarNode.setPeer(context: self.item.context, account: self.item.account, theme: self.presentationData.theme, peer: self.item.peer) + + if self.item.peer.emojiStatus != nil { + rightTextInset += 32.0 + } + + let textSize = self.textNode.updateLayout(CGSize(width: constrainedWidth - sideInset - rightTextInset, height: .greatestFiniteMagnitude)) + + return (CGSize(width: textSize.width + sideInset + rightTextInset, height: verticalInset * 2.0 + textSize.height), { size, transition in + let verticalOrigin = floor((size.height - textSize.height) / 2.0) + let textFrame = CGRect(origin: CGPoint(x: sideInset, y: verticalOrigin), size: textSize) + transition.updateFrameAdditive(node: self.textNode, frame: textFrame) + + var iconContent: EmojiStatusComponent.Content? + if case let .user(user) = self.item.peer { + if let emojiStatus = user.emojiStatus { + iconContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 28.0, height: 28.0), placeholderColor: self.presentationData.theme.list.mediaPlaceholderColor, themeColor: self.presentationData.theme.list.itemAccentColor, loopMode: .forever) + } else if user.isPremium { + iconContent = .premium(color: self.presentationData.theme.list.itemAccentColor) + } + } else if case let .channel(channel) = self.item.peer { + if let emojiStatus = channel.emojiStatus { + iconContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 28.0, height: 28.0), placeholderColor: self.presentationData.theme.list.mediaPlaceholderColor, themeColor: self.presentationData.theme.list.itemAccentColor, loopMode: .forever) + } + } + if let iconContent { + let emojiStatusSize = self.emojiStatusView.update( + transition: .immediate, + component: AnyComponent(EmojiStatusComponent( + context: self.item.context, + animationCache: self.item.context.animationCache, + animationRenderer: self.item.context.animationRenderer, + content: iconContent, + isVisibleForAnimations: true, + action: nil + )), + environment: {}, + containerSize: CGSize(width: 24.0, height: 24.0) + ) + if let view = self.emojiStatusView.view { + if view.superview == nil { + self.view.addSubview(view) + } + transition.updateFrame(view: view, frame: CGRect(origin: CGPoint(x: textFrame.maxX + 2.0, y: textFrame.minY + floor((textFrame.height - emojiStatusSize.height) / 2.0)), size: emojiStatusSize)) + } + } + + transition.updateFrame(node: self.avatarNode, frame: CGRect(origin: CGPoint(x: size.width - standardIconWidth - iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize)) + + transition.updateFrame(node: self.highlightedBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))) + transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height))) + }) + } + + func updateTheme(presentationData: PresentationData) { + self.highlightedBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor + + if let attributedText = self.textNode.attributedText { + let updatedAttributedText = NSMutableAttributedString(attributedString: attributedText) + updatedAttributedText.addAttribute(.foregroundColor, value: presentationData.theme.contextMenu.primaryColor.cgColor, range: NSRange(location: 0, length: updatedAttributedText.length)) + self.textNode.attributedText = updatedAttributedText + } + } + + @objc private func buttonPressed() { + self.performAction() + } + + func canBeHighlighted() -> Bool { + return true + } + + func setIsHighlighted(_ value: Bool) { + if value { + self.highlightedBackgroundNode.alpha = 1.0 + } else { + self.highlightedBackgroundNode.alpha = 0.0 + } + } + + func updateIsHighlighted(isHighlighted: Bool) { + self.setIsHighlighted(isHighlighted) + } + + func performAction() { + guard let controller = self.getController() else { + return + } + self.item.action(controller, { [weak self] result in + self?.actionSelected(result) + }) + } +} + +final class PeerInfoControllerContextReferenceContentSource: ContextReferenceContentSource { + let controller: ViewController + let sourceView: UIView + let insets: UIEdgeInsets + let contentInsets: UIEdgeInsets + + init(controller: ViewController, sourceView: UIView, insets: UIEdgeInsets, contentInsets: UIEdgeInsets = UIEdgeInsets()) { + self.controller = controller + self.sourceView = sourceView + self.insets = insets + self.contentInsets = contentInsets + } + + func transitionInfo() -> ContextControllerReferenceViewInfo? { + return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: UIScreen.main.bounds.inset(by: self.insets), insets: self.contentInsets) + } +} + +final class HeaderContextReferenceContentSource: ContextReferenceContentSource { + private let controller: ViewController + private let sourceView: UIView + + init(controller: ViewController, sourceView: UIView) { + self.controller = controller + self.sourceView = sourceView + } + + func transitionInfo() -> ContextControllerReferenceViewInfo? { + return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: UIScreen.main.bounds) + } +} + +private func cancelContextGestures(view: UIView) { + if let gestureRecognizers = view.gestureRecognizers { + for gesture in gestureRecognizers { + if let gesture = gesture as? ContextGesture { + gesture.cancel() + } + } + } + for subview in view.subviews { + cancelContextGestures(view: subview) + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PhotoUpdateConfirmationController.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PhotoUpdateConfirmationController.swift index 4e257669..ce30f6f2 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PhotoUpdateConfirmationController.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PhotoUpdateConfirmationController.swift @@ -9,239 +9,110 @@ import TelegramPresentationData import TelegramUIPreferences import AccountContext import AppBundle -import AvatarNode +import ComponentFlow +import AlertComponent +import AlertTransferHeaderComponent +import AvatarComponent -private final class PhotoUpdateConfirmationAlertContentNode: AlertContentNode { - private let strings: PresentationStrings - private let text: String - - private let textNode: ASTextNode - private let avatarNode: AvatarNode - private let arrowNode: ASImageNode - private let iconNode: ASImageNode - - private let actionNodesSeparator: ASDisplayNode - private let actionNodes: [TextAlertContentActionNode] - private let actionVerticalSeparators: [ASDisplayNode] - - private var validLayout: CGSize? - - override var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled - } - - init(context: AccountContext, theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, peer: EnginePeer, image: UIImage, text: String, actions: [TextAlertAction]) { - self.strings = strings - self.text = text - - self.textNode = ASTextNode() - self.textNode.maximumNumberOfLines = 0 - - self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0)) - - self.arrowNode = ASImageNode() - self.arrowNode.displaysAsynchronously = false - self.arrowNode.displayWithoutProcessing = true - - self.iconNode = ASImageNode() - self.iconNode.clipsToBounds = true - self.iconNode.displaysAsynchronously = false - self.iconNode.displayWithoutProcessing = true - self.iconNode.image = image - self.iconNode.cornerRadius = 30.0 - - self.actionNodesSeparator = ASDisplayNode() - self.actionNodesSeparator.isLayerBacked = true - - self.actionNodes = actions.map { action -> TextAlertContentActionNode in - return TextAlertContentActionNode(theme: theme, action: action) - } - - var actionVerticalSeparators: [ASDisplayNode] = [] - if actions.count > 1 { - for _ in 0 ..< actions.count - 1 { - let separatorNode = ASDisplayNode() - separatorNode.isLayerBacked = true - actionVerticalSeparators.append(separatorNode) - } - } - self.actionVerticalSeparators = actionVerticalSeparators - - super.init() - - self.addSubnode(self.textNode) - self.addSubnode(self.avatarNode) - self.addSubnode(self.arrowNode) - self.addSubnode(self.iconNode) - - self.addSubnode(self.actionNodesSeparator) - - for actionNode in self.actionNodes { - self.addSubnode(actionNode) - } - - for separatorNode in self.actionVerticalSeparators { - self.addSubnode(separatorNode) - } - - self.updateTheme(theme) - - self.avatarNode.setPeer(context: context, theme: ptheme, peer: peer) - } - - override func updateTheme(_ theme: AlertControllerTheme) { - self.textNode.attributedText = NSAttributedString(string: self.text, font: Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center) - self.arrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Peer Info/AlertArrow"), color: theme.secondaryColor) - - self.actionNodesSeparator.backgroundColor = theme.separatorColor - for actionNode in self.actionNodes { - actionNode.updateTheme(theme) - } - for separatorNode in self.actionVerticalSeparators { - separatorNode.backgroundColor = theme.separatorColor - } - - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) - } - } - - override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - var size = size - size.width = min(size.width, 270.0) - - self.validLayout = size - - var origin: CGPoint = CGPoint(x: 0.0, y: 20.0) - - let avatarSize = CGSize(width: 60.0, height: 60.0) - self.avatarNode.updateSize(size: avatarSize) - - let avatarFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) - 44.0, y: origin.y), size: avatarSize) - transition.updateFrame(node: self.avatarNode, frame: avatarFrame) - - if let arrowImage = self.arrowNode.image { - let arrowFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - arrowImage.size.width) / 2.0), y: origin.y + floorToScreenPixels((avatarSize.height - arrowImage.size.height) / 2.0)), size: arrowImage.size) - transition.updateFrame(node: self.arrowNode, frame: arrowFrame) - } - - if let _ = self.iconNode.image { - let iconFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) + 44.0, y: origin.y), size: avatarSize) - transition.updateFrame(node: self.iconNode, frame: iconFrame) - origin.y += avatarSize.height + 10.0 - } - - let textSize = self.textNode.measure(CGSize(width: size.width - 32.0, height: size.height)) - transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize)) - - let actionButtonHeight: CGFloat = 44.0 - var minActionsWidth: CGFloat = 0.0 - let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) - let actionTitleInsets: CGFloat = 8.0 - - var effectiveActionLayout = TextAlertContentActionLayout.horizontal - for actionNode in self.actionNodes { - let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) - if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 { - effectiveActionLayout = .vertical - } - switch effectiveActionLayout { - case .horizontal: - minActionsWidth += actionTitleSize.width + actionTitleInsets - case .vertical: - minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) - } - } - - let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0) - - let contentWidth = max(size.width, minActionsWidth) - - var actionsHeight: CGFloat = 0.0 - switch effectiveActionLayout { - case .horizontal: - actionsHeight = actionButtonHeight - case .vertical: - actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) - } - - let resultSize = CGSize(width: contentWidth, height: avatarSize.height + textSize.height + actionsHeight + 16.0 + insets.top + insets.bottom) - - transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - - var actionOffset: CGFloat = 0.0 - let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) - var separatorIndex = -1 - var nodeIndex = 0 - for actionNode in self.actionNodes { - if separatorIndex >= 0 { - let separatorNode = self.actionVerticalSeparators[separatorIndex] - switch effectiveActionLayout { - case .horizontal: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) - case .vertical: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - } - } - separatorIndex += 1 - - let currentActionWidth: CGFloat - switch effectiveActionLayout { - case .horizontal: - if nodeIndex == self.actionNodes.count - 1 { - currentActionWidth = resultSize.width - actionOffset - } else { - currentActionWidth = actionWidth - } - case .vertical: - currentActionWidth = resultSize.width - } - - let actionNodeFrame: CGRect - switch effectiveActionLayout { - case .horizontal: - actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += currentActionWidth - case .vertical: - actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += actionButtonHeight - } - - transition.updateFrame(node: actionNode, frame: actionNodeFrame) - - nodeIndex += 1 - } - - return resultSize - } -} - -func photoUpdateConfirmationController(context: AccountContext, peer: EnginePeer, image: UIImage, text: String, doneTitle: String, isDark: Bool = true, commit: @escaping () -> Void) -> AlertController { - let theme = defaultDarkColorPresentationTheme - var presentationData = context.sharedContext.currentPresentationData.with { $0 } - if isDark { - presentationData = presentationData.withUpdated(theme: theme) - } +func photoUpdateConfirmationController( + context: AccountContext, + peer: EnginePeer, + image: UIImage, + text: String, + doneTitle: String, + isDark: Bool = true, + commit: @escaping () -> Void +) -> ViewController { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } let strings = presentationData.strings + + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "header", + component: AnyComponent( + AlertTransferHeaderComponent( + fromComponent: AnyComponentWithIdentity(id: "user", component: AnyComponent( + AvatarComponent( + context: context, + theme: presentationData.theme, + peer: peer + ) + )), + toComponent: AnyComponentWithIdentity(id: "image", component: AnyComponent( + Image( + image: image, + size: CGSize(width: 60.0, height: 60.0), + cornerRadius: 30.0 + ) + )), + type: .transfer + ) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(text)) + ) + )) - var dismissImpl: ((Bool) -> Void)? - var contentNode: PhotoUpdateConfirmationAlertContentNode? - let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - dismissImpl?(true) - }), TextAlertAction(type: .defaultAction, title: doneTitle, action: { - dismissImpl?(true) - commit() - })] - - contentNode = PhotoUpdateConfirmationAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: theme, strings: strings, peer: peer, image: image, text: text, actions: actions) - - let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode!) - dismissImpl = { [weak controller] animated in - if animated { - controller?.dismissAnimated() - } else { - controller?.dismiss() - } + var updatedPresentationData: (PresentationData, Signal) + if isDark { + updatedPresentationData = (presentationData.withUpdated(theme: defaultDarkColorPresentationTheme), .single(presentationData.withUpdated(theme: defaultDarkColorPresentationTheme))) + } else { + updatedPresentationData = (presentationData, context.sharedContext.presentationData) } - return controller + + let alertController = AlertScreen( + content: content, + actions: [ + .init(title: strings.Common_Cancel), + .init(title: doneTitle, type: .default, action: { + commit() + }) + ], + updatedPresentationData: updatedPresentationData + ) + return alertController } + +//private final class RoundImageComponent: Component { +// let image: UIImage +// +// public init( +// image: UIImage +// ) { +// self.image = image +// } +// +// public static func ==(lhs: RoundImageComponent, rhs: RoundImageComponent) -> Bool { +// if lhs.image !== rhs.image { +// return false +// } +// return true +// } +// +// public final class View: UIImageView { +// private var component: RoundImageComponent? +// private weak var state: EmptyComponentState? +// +// func update(component: RoundImageComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { +// self.component = component +// self.state = state +// +// self.clipsToBounds = true +// self.image = component.image +// self.layer.cornerRadius = 30.0 +// +// return CGSize(width: 60.0, height: 60.0) +// } +// } +// +// public func makeView() -> View { +// return View(frame: CGRect()) +// } +// +// public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { +// return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) +// } +//} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PresentAddMembers.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PresentAddMembers.swift new file mode 100644 index 00000000..af50c523 --- /dev/null +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PresentAddMembers.swift @@ -0,0 +1,220 @@ +import Foundation +import UIKit +import Display +import AccountContext +import TelegramPresentationData +import SwiftSignalKit +import Postbox +import TelegramCore +import InviteLinksUI +import SendInviteLinkScreen +import UndoUI +import PresentationDataUtils + +public func presentAddMembersImpl(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, parentController: ViewController, groupPeer: Peer, selectAddMemberDisposable: MetaDisposable, addMemberDisposable: MetaDisposable) { + let members: Promise<[PeerId]> = Promise() + if groupPeer.id.namespace == Namespaces.Peer.CloudChannel { + /*var membersDisposable: Disposable? + let (disposable, _) = context.peerChannelMemberCategoriesContextsManager.recent(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerView.peerId, updated: { listState in + members.set(.single(listState.list.map {$0.peer.id})) + membersDisposable?.dispose() + }) + membersDisposable = disposable*/ + members.set(.single([])) + } else { + members.set(.single([])) + } + + let _ = (members.get() + |> take(1) + |> deliverOnMainQueue).startStandalone(next: { [weak parentController] recentIds in + var createInviteLinkImpl: (() -> Void)? + var confirmationImpl: ((PeerId) -> Signal)? + let _ = confirmationImpl + var options: [ContactListAdditionalOption] = [] + let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } + + var canCreateInviteLink = false + if let group = groupPeer as? TelegramGroup { + switch group.role { + case .creator: + canCreateInviteLink = true + case let .admin(rights, _): + canCreateInviteLink = rights.rights.contains(.canInviteUsers) + default: + break + } + } else if let channel = groupPeer as? TelegramChannel, (channel.addressName?.isEmpty ?? true) { + if channel.flags.contains(.isCreator) || (channel.adminRights?.rights.contains(.canInviteUsers) == true) { + canCreateInviteLink = true + } + } + + if canCreateInviteLink { + options.append(ContactListAdditionalOption(title: presentationData.strings.GroupInfo_InviteByLink, icon: .generic(UIImage(bundleImageName: "Contact List/LinkActionIcon")!), action: { + createInviteLinkImpl?() + }, clearHighlightAutomatically: true)) + } + + let contactsController = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, updatedPresentationData: updatedPresentationData, mode: .peerSelection(searchChatList: false, searchGroups: false, searchChannels: false), options: .single(options), filters: [.excludeSelf, .disable(recentIds)], onlyWriteable: true, isGroupInvitation: true)) + contactsController.navigationPresentation = .modal + + confirmationImpl = { [weak contactsController] peerId in + return context.account.postbox.loadedPeerWithId(peerId) + |> deliverOnMainQueue + |> mapToSignal { peer in + let result = ValuePromise() + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + if let contactsController = contactsController { + let alertController = textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.GroupInfo_AddParticipantConfirmation(EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string, actions: [ + TextAlertAction(type: .genericAction, title: presentationData.strings.Common_No, action: { + result.set(false) + }), + TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Yes, action: { + result.set(true) + }) + ]) + contactsController.present(alertController, in: .window(.root)) + } + + return result.get() + } + } + + let addMembers: ([ContactListPeerId]) -> Signal<[(PeerId, AddChannelMemberError)], NoError> = { members -> Signal<[(PeerId, AddChannelMemberError)], NoError> in + let memberIds = members.compactMap { contact -> PeerId? in + switch contact { + case let .peer(peerId): + return peerId + default: + return nil + } + } + return context.account.postbox.multiplePeersView(memberIds) + |> take(1) + |> deliverOnMainQueue + |> mapToSignal { view -> Signal<[(PeerId, AddChannelMemberError)], NoError> in + if groupPeer.id.namespace == Namespaces.Peer.CloudChannel { + if memberIds.count == 1 { + return context.peerChannelMemberCategoriesContextsManager.addMember(engine: context.engine, peerId: groupPeer.id, memberId: memberIds[0]) + |> map { _ -> [(PeerId, AddChannelMemberError)] in + } + |> then(Signal<[(PeerId, AddChannelMemberError)], AddChannelMemberError>.single([])) + |> `catch` { error -> Signal<[(PeerId, AddChannelMemberError)], NoError> in + return .single([(memberIds[0], error)]) + } + } else { + return context.peerChannelMemberCategoriesContextsManager.addMembersAllowPartial(engine: context.engine, peerId: groupPeer.id, memberIds: memberIds) + } + } else { + var signals: [Signal<(PeerId, AddChannelMemberError)?, NoError>] = [] + for memberId in memberIds { + let signal: Signal<(PeerId, AddChannelMemberError)?, NoError> = context.engine.peers.addGroupMember(peerId: groupPeer.id, memberId: memberId) + |> mapError { error -> AddChannelMemberError in + switch error { + case .generic: + return .generic + case .groupFull: + return .limitExceeded + case let .privacy(privacy): + return .restricted(privacy?.forbiddenPeers.first) + case .notMutualContact: + return .notMutualContact + case .tooManyChannels: + return .generic + } + } + |> ignoreValues + |> map { _ -> (PeerId, AddChannelMemberError)? in + } + |> then(Signal<(PeerId, AddChannelMemberError)?, AddChannelMemberError>.single(nil)) + |> `catch` { error -> Signal<(PeerId, AddChannelMemberError)?, NoError> in + return .single((memberId, error)) + } + signals.append(signal) + } + return combineLatest(signals) + |> map { values -> [(PeerId, AddChannelMemberError)] in + return values.compactMap { $0 } + } + } + } + } + + createInviteLinkImpl = { [weak contactsController] in + contactsController?.view.window?.endEditing(true) + contactsController?.present(InviteLinkInviteController(context: context, updatedPresentationData: updatedPresentationData, mode: .groupOrChannel(peerId: groupPeer.id), initialInvite: nil, parentNavigationController: contactsController?.navigationController as? NavigationController), in: .window(.root)) + } + + parentController?.push(contactsController) + do { + selectAddMemberDisposable.set(( + combineLatest(queue: .mainQueue(), + context.engine.data.get(TelegramEngine.EngineData.Item.Peer.ExportedInvitation(id: groupPeer.id)), + contactsController.result + ) + |> deliverOnMainQueue).start(next: { [weak contactsController] exportedInvitation, result in + var peers: [ContactListPeerId] = [] + if case let .result(peerIdsValue, _) = result { + peers = peerIdsValue + } + + contactsController?.displayProgress = true + addMemberDisposable.set((addMembers(peers) + |> deliverOnMainQueue).start(next: { failedPeerIds in + if failedPeerIds.isEmpty { + contactsController?.dismiss() + + let mappedPeerIds: [EnginePeer.Id] = peers.compactMap { peer -> EnginePeer.Id? in + switch peer { + case let .peer(id): + return id + default: + return nil + } + } + if !mappedPeerIds.isEmpty { + let _ = (context.engine.data.get(EngineDataMap(mappedPeerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:)))) + |> deliverOnMainQueue).startStandalone(next: { maybePeers in + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let peers = maybePeers.compactMap { $0.value } + + let text: String + if peers.count == 1 { + text = presentationData.strings.PeerInfo_NotificationMemberAdded(peers[0].displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string + } else { + text = presentationData.strings.PeerInfo_NotificationMultipleMembersAdded(Int32(peers.count)) + } + parentController?.present(UndoOverlayController(presentationData: presentationData, content: .peers(context: context, peers: peers, title: nil, text: text, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) + }) + } + } else { + let failedPeers = failedPeerIds.compactMap { _, error -> TelegramForbiddenInvitePeer? in + if case let .restricted(peer) = error { + return peer + } else { + return nil + } + } + + if !failedPeers.isEmpty, let contactsController, let navigationController = contactsController.navigationController as? NavigationController { + var viewControllers = navigationController.viewControllers + if let index = viewControllers.firstIndex(where: { $0 === contactsController }) { + let inviteScreen = SendInviteLinkScreen(context: context, subject: .chat(peer: EnginePeer(groupPeer), link: exportedInvitation?.link), peers: failedPeers) + viewControllers.remove(at: index) + viewControllers.append(inviteScreen) + navigationController.setViewControllers(viewControllers, animated: true) + } + } else { + contactsController?.dismiss() + } + } + })) + })) + contactsController.dismissed = { + selectAddMemberDisposable.set(nil) + addMemberDisposable.set(nil) + } + } + }) +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/PeerInfoStoryGridScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/PeerInfoStoryGridScreen.swift index 22e0ecf4..925c3098 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/PeerInfoStoryGridScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/PeerInfoStoryGridScreen.swift @@ -676,7 +676,7 @@ public class PeerInfoStoryGridScreen: ViewControllerComponentContainer { let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } if self.selectionModeCompletion != nil { - self.titleView?.titleContent = .custom(presentationData.strings.Stories_AddStoriesTitle, nil, false) + self.titleView?.titleContent = .custom(title: [ChatTitleContent.TitleTextItem(id: AnyHashable(0), content: .text(presentationData.strings.Stories_AddStoriesTitle))], subtitle: nil, isEnabled: false) } else { switch self.scope { case .saved: @@ -691,7 +691,7 @@ public class PeerInfoStoryGridScreen: ViewControllerComponentContainer { } else { title = nil } - self.titleView?.titleContent = .custom(presentationData.strings.StoryList_TitleSaved, title, false) + self.titleView?.titleContent = .custom(title: [ChatTitleContent.TitleTextItem(id: AnyHashable(0), content: .text(presentationData.strings.StoryList_TitleSaved))], subtitle: title, isEnabled: false) if paneNode.isSelectionModeActive { self.navigationItem.setRightBarButton(self.doneBarButtonItem, animated: false) @@ -708,7 +708,7 @@ public class PeerInfoStoryGridScreen: ViewControllerComponentContainer { } else { title = presentationData.strings.StoryList_TitleArchive } - self.titleView?.titleContent = .custom(title, nil, false) + self.titleView?.titleContent = .custom(title: [ChatTitleContent.TitleTextItem(id: AnyHashable(0), content: .text(title))], subtitle: nil, isEnabled: false) var hasMenu = false if componentView.selectedCount != 0 { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/StorySearchGridScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/StorySearchGridScreen.swift index 025a6703..3a15d112 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/StorySearchGridScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoStoryGridScreen/Sources/StorySearchGridScreen.swift @@ -277,12 +277,12 @@ public final class StorySearchGridScreen: ViewControllerComponentContainer { switch self.scope { case let .query(peer, query): if let peer, let addressName = peer.addressName { - self.titleView?.titleContent = .custom("\(query)@\(addressName)", title, false) + self.titleView?.titleContent = .custom(title: [ChatTitleContent.TitleTextItem(id: AnyHashable(0), content: .text("\(query)@\(addressName)"))], subtitle: title, isEnabled: false) } else { - self.titleView?.titleContent = .custom("\(query)", title, false) + self.titleView?.titleContent = .custom(title: [ChatTitleContent.TitleTextItem(id: AnyHashable(0), content: .text("\(query)"))], subtitle: title, isEnabled: false) } case .location: - self.titleView?.titleContent = .custom(presentationData.strings.StoryGridScreen_TitleLocationSearch, nil, false) + self.titleView?.titleContent = .custom(title: [ChatTitleContent.TitleTextItem(id: AnyHashable(0), content: .text(presentationData.strings.StoryGridScreen_TitleLocationSearch))], subtitle: nil, isEnabled: false) } } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/BUILD b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/BUILD index 16458e00..3719417c 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/BUILD +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/BUILD @@ -58,7 +58,8 @@ swift_library( "//submodules/TelegramUI/Components/BottomButtonPanelComponent", "//submodules/PromptUI", "//submodules/TelegramUI/Components/EmojiTextAttachmentView", - "//submodules/TelegramUI/Components/PeerInfo/CollectionTabItemComponent" + "//submodules/TelegramUI/Components/PeerInfo/CollectionTabItemComponent", + "//submodules/TelegramUI/Components/EdgeEffect", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift index 26e09bea..60884480 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/AddGiftsScreen.swift @@ -484,18 +484,18 @@ private final class FilterHeaderButton: HighlightableButtonNode { component: AnyComponent( BundleIconComponent( name: "Peer Info/SortIcon", - tintColor: theme.rootController.navigationBar.accentTextColor + tintColor: theme.chat.inputPanel.panelControlColor ) ), environment: {}, - containerSize: CGSize(width: 30.0, height: 30.0) + containerSize: CGSize(width: 44.0, height: 44.0) ) if let view = self.icon.view { if view.superview == nil { view.isUserInteractionEnabled = false self.referenceNode.view.addSubview(view) } - view.frame = CGRect(origin: CGPoint(x: 14.0, y: 7.0), size: iconSize) + view.frame = CGRect(origin: CGPoint(x: 7.0, y: 7.0), size: iconSize) } self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 44.0, height: 44.0)) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift index c1409f7b..d9a85eb9 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoGiftsPaneNode.swift @@ -35,6 +35,7 @@ import EmojiTextAttachmentView import TextFormat import PromptUI import CollectionTabItemComponent +import EdgeEffect public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScrollViewDelegate { public enum GiftCollection: Equatable { @@ -91,8 +92,8 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr private let tabSelector = ComponentView() public private(set) var currentCollection: GiftCollection = .all - private var panelBackground: NavigationBackgroundNode? - private var panelSeparator: ASDisplayNode? + private var panelEdgeEffectView: EdgeEffectView? + private var panelContentContainer: UIView? private var panelButton: ComponentView? private var panelCheck: ComponentView? @@ -233,7 +234,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr return } - let promptController = promptController(sharedContext: self.context.sharedContext, updatedPresentationData: nil, text: params.presentationData.strings.PeerInfo_Gifts_CreateCollection_Title, titleFont: .bold, subtitle: params.presentationData.strings.PeerInfo_Gifts_CreateCollection_Text, value: "", placeholder: params.presentationData.strings.PeerInfo_Gifts_CreateCollection_Placeholder, characterLimit: 12, displayCharacterLimit: true, apply: { [weak self] value in + let promptController = promptController(context: self.context, updatedPresentationData: nil, text: params.presentationData.strings.PeerInfo_Gifts_CreateCollection_Title, titleFont: .bold, subtitle: params.presentationData.strings.PeerInfo_Gifts_CreateCollection_Text, value: "", placeholder: params.presentationData.strings.PeerInfo_Gifts_CreateCollection_Placeholder, characterLimit: 12, displayCharacterLimit: true, apply: { [weak self] value in guard let self, let value else { return } @@ -304,7 +305,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr return } - let promptController = promptController(sharedContext: self.context.sharedContext, updatedPresentationData: nil, text: params.presentationData.strings.PeerInfo_Gifts_RenameCollection_Title, titleFont: .bold, value: collection.title, placeholder: params.presentationData.strings.PeerInfo_Gifts_CreateCollection_Placeholder, characterLimit: 12, displayCharacterLimit: true, apply: { [weak self] value in + let promptController = promptController(context: self.context, updatedPresentationData: nil, text: params.presentationData.strings.PeerInfo_Gifts_RenameCollection_Title, titleFont: .bold, value: collection.title, placeholder: params.presentationData.strings.PeerInfo_Gifts_CreateCollection_Placeholder, characterLimit: 12, displayCharacterLimit: true, apply: { [weak self] value in guard let self, let value else { return } @@ -579,7 +580,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr if let params = self.currentParams { let visibleBounds = self.scrollNode.bounds.insetBy(dx: 0.0, dy: -10.0) - var topInset: CGFloat = 60.0 + var topInset: CGFloat = params.topInset var canEditCollections = false if self.peerId == self.context.account.peerId || self.canManage { @@ -657,14 +658,15 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr component: AnyComponent(TabSelectorComponent( context: self.context, colors: TabSelectorComponent.Colors( - foreground: params.presentationData.theme.list.itemSecondaryTextColor, + foreground: params.presentationData.theme.list.itemPrimaryTextColor, selection: params.presentationData.theme.list.itemSecondaryTextColor.withMultipliedAlpha(0.15), simple: true ), theme: params.presentationData.theme, customLayout: TabSelectorComponent.CustomLayout( - font: Font.medium(14.0), - spacing: 2.0 + font: Font.medium(15.0), + spacing: 2.0, + height: 44.0 - 5.0 * 2.0 ), items: tabSelectorItems, selectedId: AnyHashable(self.currentCollection.rawValue), @@ -701,7 +703,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr } )), environment: {}, - containerSize: CGSize(width: params.size.width - 10.0 * 2.0, height: 50.0) + containerSize: CGSize(width: params.size.width - 14.0 * 2.0, height: 44.0) ) if let tabSelectorView = self.tabSelector.view { if tabSelectorView.superview == nil { @@ -712,9 +714,9 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr tabSelectorView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) } } - transition.setFrame(view: tabSelectorView, frame: CGRect(origin: CGPoint(x: floor((params.size.width - tabSelectorSize.width) / 2.0), y: 60.0), size: tabSelectorSize)) + transition.setFrame(view: tabSelectorView, frame: CGRect(origin: CGPoint(x: floor((params.size.width - tabSelectorSize.width) / 2.0), y: topInset), size: tabSelectorSize)) - topInset += tabSelectorSize.height + 14.0 + topInset += tabSelectorSize.height + 15.0 } } else if let tabSelectorView = self.tabSelector.view { tabSelectorView.alpha = 0.0 @@ -731,32 +733,31 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr let bottomInset = params.bottomInset let presentationData = params.presentationData - let themeUpdated = self.theme !== presentationData.theme self.theme = presentationData.theme - let panelBackground: NavigationBackgroundNode - let panelSeparator: ASDisplayNode + let panelEdgeEffectView: EdgeEffectView + let panelContentContainer: UIView var panelVisibility = params.expandProgress < 1.0 ? 0.0 : 1.0 if !self.canGift || self.resultsAreEmpty { panelVisibility = 0.0 } - let panelTransition: ComponentTransition = .immediate - if let current = self.panelBackground { - panelBackground = current + if let current = self.panelContentContainer { + panelContentContainer = current } else { - panelBackground = NavigationBackgroundNode(color: presentationData.theme.rootController.tabBar.backgroundColor) - self.addSubnode(panelBackground) - self.panelBackground = panelBackground + panelContentContainer = UIView() + self.view.addSubview(panelContentContainer) + self.panelContentContainer = panelContentContainer } - if let current = self.panelSeparator { - panelSeparator = current + let panelTransition: ComponentTransition = .immediate + if let current = self.panelEdgeEffectView { + panelEdgeEffectView = current } else { - panelSeparator = ASDisplayNode() - panelBackground.addSubnode(panelSeparator) - self.panelSeparator = panelSeparator + panelEdgeEffectView = EdgeEffectView() + panelContentContainer.addSubview(panelEdgeEffectView) + self.panelEdgeEffectView = panelEdgeEffectView } let panelButton: ComponentView @@ -767,7 +768,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr self.panelButton = panelButton } - let buttonSideInset = sideInset + 16.0 + let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: params.bottomInset, innerDiameter: 52.0 * 0.5, sideInset: sideInset + 30.0) let buttonTitle: String var buttonIconName: String? @@ -800,6 +801,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr component: AnyComponent( ButtonComponent( background: ButtonComponent.Background( + style: .glass, color: presentationData.theme.list.itemCheckColors.fillColor, foreground: presentationData.theme.list.itemCheckColors.foregroundColor, pressedColor: presentationData.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.8) @@ -815,32 +817,27 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr ) ), environment: {}, - containerSize: CGSize(width: size.width - buttonSideInset * 2.0, height: 50.0) + containerSize: CGSize(width: size.width - buttonInsets.left * 2.0, height: 52.0) ) var scrollOffset: CGFloat = max(0.0, size.height - params.visibleHeight) - let effectiveBottomInset = max(8.0, bottomInset) - var bottomPanelHeight = effectiveBottomInset + panelButtonSize.height + 8.0 + let effectiveBottomInset = max(buttonInsets.bottom, bottomInset) + let bottomPanelHeight = effectiveBottomInset + panelButtonSize.height + 8.0 if params.visibleHeight < 110.0 { scrollOffset -= bottomPanelHeight } if let panelButtonView = panelButton.view { if panelButtonView.superview == nil { - panelBackground.view.addSubview(panelButtonView) + panelContentContainer.addSubview(panelButtonView) } - panelButtonView.frame = CGRect(origin: CGPoint(x: buttonSideInset, y: 8.0), size: panelButtonSize) + panelButtonView.frame = CGRect(origin: CGPoint(x: buttonInsets.left, y: 8.0), size: panelButtonSize) } - if themeUpdated { - panelBackground.updateColor(color: presentationData.theme.rootController.tabBar.backgroundColor, transition: .immediate) - panelSeparator.backgroundColor = presentationData.theme.rootController.tabBar.separatorColor - } + panelTransition.setFrame(view: panelContentContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: size.height - bottomPanelHeight), size: CGSize(width: size.width, height: bottomPanelHeight))) if self.canManage { - bottomPanelHeight -= 9.0 - let panelCheck: ComponentView if let current = self.panelCheck { panelCheck = current @@ -903,20 +900,20 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr ) if let panelCheckView = panelCheck.view { if panelCheckView.superview == nil { - panelBackground.view.addSubview(panelCheckView) + panelContentContainer.addSubview(panelCheckView) } - panelCheckView.frame = CGRect(origin: CGPoint(x: floor((size.width - panelCheckSize.width) / 2.0), y: 16.0), size: panelCheckSize) + panelCheckView.frame = CGRect(origin: CGPoint(x: floor((size.width - panelCheckSize.width) / 2.0), y: 16.0 + 16.0), size: panelCheckSize) } if let panelButtonView = panelButton.view { panelButtonView.isHidden = true } } - panelTransition.setFrame(view: panelBackground.view, frame: CGRect(x: 0.0, y: size.height - bottomPanelHeight - scrollOffset, width: size.width, height: bottomPanelHeight)) - ComponentTransition.spring(duration: 0.4).setSublayerTransform(view: panelBackground.view, transform: CATransform3DMakeTranslation(0.0, bottomPanelHeight * (1.0 - panelVisibility), 0.0)) + let edgeEffectFrame = CGRect(x: 0.0, y: 0.0, width: size.width, height: bottomPanelHeight) + panelTransition.setFrame(view: panelEdgeEffectView, frame: edgeEffectFrame) + panelEdgeEffectView.update(content: presentationData.theme.list.blocksBackgroundColor, blur: false, rect: edgeEffectFrame, edge: .bottom, edgeSize: 40.0, transition: panelTransition) - panelBackground.update(size: CGSize(width: size.width, height: bottomPanelHeight), transition: transition.containedViewLayoutTransition) - panelTransition.setFrame(view: panelSeparator.view, frame: CGRect(x: 0.0, y: 0.0, width: size.width, height: UIScreenPixel)) + ComponentTransition.spring(duration: 0.4).setSublayerTransform(view: panelContentContainer, transform: CATransform3DMakeTranslation(0.0, bottomPanelHeight * (1.0 - panelVisibility), 0.0)) contentHeight += bottomPanelHeight bottomScrollInset = bottomPanelHeight - 40.0 @@ -932,7 +929,9 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr let bottomContentOffset = max(0.0, self.scrollNode.view.contentSize.height - self.scrollNode.view.contentOffset.y - self.scrollNode.view.frame.height) if bottomContentOffset < 200.0 { - self.giftsListView.loadMore() + Queue.mainQueue().justDispatch { + self.giftsListView.loadMore() + } } } @@ -1438,7 +1437,7 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr self.presentationDataPromise.set(.single(presentationData)) self.backgroundNode.backgroundColor = presentationData.theme.list.blocksBackgroundColor - transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 48.0), size: size)) + transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset), size: CGSize(width: size.width, height: size.height - topInset))) transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: size)) let visibleBounds = self.scrollNode.bounds.insetBy(dx: 0.0, dy: -10.0) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift index d6810166..4df8073a 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoStoryPaneNode.swift @@ -6,6 +6,7 @@ import TelegramCore import SwiftSignalKit import Postbox import TelegramPresentationData +import PresentationDataUtils import AccountContext import ContextUI import PhotoResources @@ -1824,6 +1825,8 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr self.maxStoriesPerFolder = maxStoriesPerFolder super.init() + + self.clipsToBounds = true if case .peer = self.scope { let _ = (ApplicationSpecificNotice.getSharedMediaScrollingTooltip(accountManager: context.sharedContext.accountManager) @@ -2112,7 +2115,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr } if case .botPreview = scope { - let backgroundColor = presentationData.theme.list.plainBackgroundColor + let backgroundColor = presentationData.theme.list.blocksBackgroundColor let foregroundColor = presentationData.theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.6) return SparseItemGrid.ShimmerColors(background: backgroundColor.argb, foreground: foregroundColor.argb) @@ -4024,14 +4027,17 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr transition: folderTabTransition, component: AnyComponent(TabSelectorComponent( colors: TabSelectorComponent.Colors( - foreground: self.presentationData.theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.8), - selection: self.presentationData.theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.05) + foreground: self.presentationData.theme.list.itemPrimaryTextColor, + selection: self.presentationData.theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.05), + normal: self.presentationData.theme.list.itemPrimaryTextColor, + simple: true ), theme: self.presentationData.theme, customLayout: TabSelectorComponent.CustomLayout( - font: Font.medium(14.0), + font: Font.medium(15.0), spacing: 9.0, - verticalInset: 11.0 + verticalInset: 11.0, + height: 44.0 - 5.0 * 2.0 ), items: folderItems, selectedId: selectedId, @@ -4090,9 +4096,9 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr } )), environment: {}, - containerSize: CGSize(width: size.width, height: 44.0) + containerSize: CGSize(width: size.width - 6.0 * 2.0, height: 44.0) ) - var folderTabFrame = CGRect(origin: CGPoint(x: floor((size.width - folderTabSize.width) * 0.5), y: topInset - 11.0), size: folderTabSize) + var folderTabFrame = CGRect(origin: CGPoint(x: floor((size.width - folderTabSize.width) * 0.5), y: topInset - 21.0), size: folderTabSize) let effectiveScrollingOffset: CGFloat effectiveScrollingOffset = self.itemGrid.scrollingOffset @@ -4236,7 +4242,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr } var hasBarBackground = false - if self.isProfileEmbedded { + if self.isProfileEmbedded && !"".isEmpty { if case .botPreview = self.scope { hasBarBackground = true } else if case let .peer(_, _, isArchived) = self.scope, ((self.canManageStories && !isArchived) || !self.currentStoryFolders.isEmpty) { @@ -4278,10 +4284,10 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr if case .botPreview = self.scope { updateBotPreviewFooter(size: size, bottomInset: 0.0, transition: transition) if let botPreviewFooterView = self.botPreviewFooter?.view { - listBottomInset += 18.0 + botPreviewFooterView.bounds.height + listBottomInset += 18.0 + botPreviewFooterView.bounds.height + } } } - } if self.isProfileEmbedded, let selectedIds = self.itemInteraction.selectedIds, self.canManageStories, case let .peer(peerId, _, isArchived) = self.scope { let selectionPanel: ComponentView @@ -4589,12 +4595,13 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr } else { backgroundColor = presentationData.theme.list.blocksBackgroundColor } + let _ = backgroundColor - if self.didUpdateItemsOnce { + /*if self.didUpdateItemsOnce { ComponentTransition(animation: .curve(duration: 0.2, curve: .easeInOut)).setBackgroundColor(view: self.view, color: backgroundColor) } else { self.view.backgroundColor = backgroundColor - } + }*/ } else { let emptyStateView: ComponentView var emptyStateTransition = ComponentTransition(transition) @@ -4656,12 +4663,13 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr } else { backgroundColor = presentationData.theme.list.blocksBackgroundColor } + let _ = backgroundColor - if self.didUpdateItemsOnce { + /*if self.didUpdateItemsOnce { ComponentTransition(animation: .curve(duration: 0.2, curve: .easeInOut)).setBackgroundColor(view: self.view, color: backgroundColor) } else { self.view.backgroundColor = backgroundColor - } + }*/ } } else if case .botPreview = self.scope, let items = self.items, items.items.isEmpty, items.count == 0 { let emptyStateView: ComponentView @@ -4741,12 +4749,13 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr } else { backgroundColor = presentationData.theme.list.blocksBackgroundColor } + let _ = backgroundColor - if self.didUpdateItemsOnce { + /*if self.didUpdateItemsOnce { ComponentTransition(animation: .curve(duration: 0.2, curve: .easeInOut)).setBackgroundColor(view: self.view, color: backgroundColor) } else { self.view.backgroundColor = backgroundColor - } + }*/ } else if case let .peer(_, _, isArchived) = self.scope, self.canManageStories, !isArchived, self.isProfileEmbedded, let items = self.items, items.items.isEmpty, items.count == 0 { let emptyStateView: ComponentView var emptyStateTransition = ComponentTransition(transition) @@ -4822,12 +4831,13 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr } else { backgroundColor = presentationData.theme.list.blocksBackgroundColor } + let _ = backgroundColor - if self.didUpdateItemsOnce { + /*if self.didUpdateItemsOnce { ComponentTransition(animation: .curve(duration: 0.2, curve: .easeInOut)).setBackgroundColor(view: self.view, color: backgroundColor) } else { self.view.backgroundColor = backgroundColor - } + }*/ } else { if let emptyStateView = self.emptyStateView { let subTransition = ComponentTransition(animation: .curve(duration: 0.2, curve: .easeInOut)) @@ -4843,17 +4853,17 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr } } - if self.isProfileEmbedded, case .botPreview = self.scope { + /*if self.isProfileEmbedded, case .botPreview = self.scope { subTransition.setBackgroundColor(view: self.view, color: presentationData.theme.list.blocksBackgroundColor) } else if self.isProfileEmbedded, case let .peer(_, _, isArchived) = self.scope, ((self.canManageStories && !isArchived) || !self.currentStoryFolders.isEmpty), self.isProfileEmbedded { subTransition.setBackgroundColor(view: self.view, color: presentationData.theme.list.blocksBackgroundColor) } else if self.isProfileEmbedded { - subTransition.setBackgroundColor(view: self.view, color: presentationData.theme.list.plainBackgroundColor) + subTransition.setBackgroundColor(view: self.view, color: presentationData.theme.list.blocksBackgroundColor) } else { subTransition.setBackgroundColor(view: self.view, color: presentationData.theme.list.blocksBackgroundColor) - } + }*/ } else { - if self.isProfileEmbedded, case .botPreview = self.scope { + /*if self.isProfileEmbedded, case .botPreview = self.scope { self.view.backgroundColor = presentationData.theme.list.blocksBackgroundColor } else if self.isProfileEmbedded, case let .peer(_, _, isArchived) = self.scope, self.canManageStories, self.isProfileEmbedded, !isArchived { self.view.backgroundColor = presentationData.theme.list.blocksBackgroundColor @@ -4863,7 +4873,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr } else { self.view.backgroundColor = .clear } - } + }*/ } } @@ -5013,7 +5023,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr private func presentAddStoryFolder(addItems: [EngineStoryItem] = []) { let promptController = promptController( - sharedContext: self.context.sharedContext, + context: self.context, updatedPresentationData: nil, text: self.presentationData.strings.Stories_CreateAlbum_Title, titleFont: .bold, @@ -5046,7 +5056,7 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr private func presentRenameStoryFolder(id: Int64, title: String) { let promptController = promptController( - sharedContext: self.context.sharedContext, + context: self.context, updatedPresentationData: nil, text: self.presentationData.strings.Stories_EditAlbum_Title, titleFont: .bold, @@ -5173,14 +5183,14 @@ public final class PeerInfoStoryPaneNode: ASDisplayNode, PeerInfoPaneNode, ASScr } public func presentUnableToAddMorePreviewsAlert() { - self.parentController?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.BotPreviews_AlertTooManyPreviews(Int32(self.maxBotPreviewCount)), actions: [ + self.parentController?.present(textAlertController(context: self.context, title: nil, text: self.presentationData.strings.BotPreviews_AlertTooManyPreviews(Int32(self.maxBotPreviewCount)), actions: [ TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: { }) ], parseMarkdown: true), in: .window(.root)) } public func presentDeleteBotPreviewLanguage() { - self.parentController?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: self.presentationData.strings.BotPreviews_DeleteTranslationAlert_Title, text: self.presentationData.strings.BotPreviews_DeleteTranslationAlert_Text, actions: [ + self.parentController?.present(textAlertController(context: self.context, title: self.presentationData.strings.BotPreviews_DeleteTranslationAlert_Title, text: self.presentationData.strings.BotPreviews_DeleteTranslationAlert_Text, actions: [ TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_Cancel, action: { }), TextAlertAction(type: .destructiveAction, title: self.presentationData.strings.Common_OK, action: { [weak self] in diff --git a/submodules/TelegramUI/Components/PeerInfo/PostSuggestionsSettingsScreen/BUILD b/submodules/TelegramUI/Components/PeerInfo/PostSuggestionsSettingsScreen/BUILD index 4166ff84..d0bc74ab 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PostSuggestionsSettingsScreen/BUILD +++ b/submodules/TelegramUI/Components/PeerInfo/PostSuggestionsSettingsScreen/BUILD @@ -24,6 +24,7 @@ swift_library( "//submodules/Components/MultilineTextComponent", "//submodules/Markdown", "//submodules/TelegramUI/Components/ButtonComponent", + "//submodules/TelegramUI/Components/PlainButtonComponent", "//submodules/Components/BundleIconComponent", "//submodules/TextFormat", "//submodules/TelegramUI/Components/ListSectionComponent", @@ -32,6 +33,8 @@ swift_library( "//submodules/TelegramStringFormatting", "//submodules/TelegramUI/Components/ListItemComponentAdaptor", "//submodules/TelegramUI/Components/PeerInfo/MessagePriceItem", + "//submodules/UndoUI", + "//submodules/ShareController", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/PeerInfo/PostSuggestionsSettingsScreen/Sources/PostSuggestionsSettingsScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PostSuggestionsSettingsScreen/Sources/PostSuggestionsSettingsScreen.swift index be77701a..4917bbf0 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PostSuggestionsSettingsScreen/Sources/PostSuggestionsSettingsScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PostSuggestionsSettingsScreen/Sources/PostSuggestionsSettingsScreen.swift @@ -22,6 +22,10 @@ import Markdown import TelegramStringFormatting import MessagePriceItem import ListItemComponentAdaptor +import ButtonComponent +import PlainButtonComponent +import UndoUI +import ShareController final class PostSuggestionsSettingsScreenComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment @@ -68,6 +72,7 @@ final class PostSuggestionsSettingsScreenComponent: Component { private let subtitle = ComponentView() private let switchSection = ComponentView() private let contentSection = ComponentView() + private let linkSection = ComponentView() private var isUpdating: Bool = false @@ -166,6 +171,91 @@ final class PostSuggestionsSettingsScreenComponent: Component { } } + func dismissAllTooltips() { + guard let environment = self.environment, let controller = environment.controller() else { + return + } + controller.window?.forEachController({ controller in + if let controller = controller as? UndoOverlayController { + controller.dismissWithCommitAction() + } + }) + } + + func copyLink(_ link: String) { + guard let component = self.component, let environment = self.environment, let controller = environment.controller() else { + return + } + UIPasteboard.general.string = link + + self.dismissAllTooltips() + + let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } + controller.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root)) + } + + func shareLink(_ link: String) { + guard let component = self.component, let environment = self.environment, let controller = environment.controller() else { + return + } + + let context = component.context + let shareController = ShareController(context: context, subject: .url(link), updatedPresentationData: nil) + shareController.completed = { [weak controller] peerIds in + let _ = (context.engine.data.get( + EngineDataList( + peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init) + ) + ) + |> deliverOnMainQueue).start(next: { [weak controller] peerList in + let peers = peerList.compactMap { $0 } + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + + let text: String + var savedMessages = false + if peerIds.count == 1, let peerId = peerIds.first, peerId == context.account.peerId { + text = presentationData.strings.InviteLink_InviteLinkForwardTooltip_SavedMessages_One + savedMessages = true + } else { + if peers.count == 1, let peer = peers.first { + let peerName = peer.id == context.account.peerId ? presentationData.strings.DialogList_SavedMessages : peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = presentationData.strings.UserInfo_LinkForwardTooltip_Chat_One(peerName).string + } else if peers.count == 2, let firstPeer = peers.first, let secondPeer = peers.last { + let firstPeerName = firstPeer.id == context.account.peerId ? presentationData.strings.DialogList_SavedMessages : firstPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + let secondPeerName = secondPeer.id == context.account.peerId ? presentationData.strings.DialogList_SavedMessages : secondPeer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = presentationData.strings.UserInfo_LinkForwardTooltip_TwoChats_One(firstPeerName, secondPeerName).string + } else if let peer = peers.first { + let peerName = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + text = presentationData.strings.UserInfo_LinkForwardTooltip_ManyChats_One(peerName, "\(peers.count - 1)").string + } else { + text = "" + } + } + + controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in + if savedMessages, action == .info { + let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)) + |> deliverOnMainQueue).start(next: { [weak controller] peer in + guard let peer else { + return + } + guard let navigationController = controller?.navigationController as? NavigationController else { + return + } + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer), forceOpenChat: true)) + }) + } + return false + }), in: .window(.root)) + }) + } + shareController.actionCompleted = { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + controller.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root)) + } + controller.present(shareController, in: .window(.root)) + } + func update(component: PostSuggestionsSettingsScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { self.isUpdating = true defer { @@ -429,6 +519,50 @@ final class PostSuggestionsSettingsScreenComponent: Component { if self.areSuggestionsEnabled { contentHeight += contentSectionSize.height + contentHeight += sectionSpacing + } + + let address = component.peer?.addressName ?? "" + let link = "t.me/\(address)?direct" + let fullLink = "https://\(link)" + var linkSectionItems: [AnyComponentWithIdentity] = [] + linkSectionItems.append(AnyComponentWithIdentity(id: 0, component: AnyComponent( + LinkComponent( + theme: environment.theme, + strings: environment.strings, + link: link, + copyAction: { [weak self] in + self?.copyLink(fullLink) + }, + shareAction: { [weak self] in + self?.shareLink(fullLink) + } + ) + ))) + let linkSectionSize = self.linkSection.update( + transition: transition, + component: AnyComponent(ListSectionComponent( + theme: environment.theme, + style: .glass, + header: nil, + footer: nil, + items: linkSectionItems + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 10000.0) + ) + let linkSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: linkSectionSize) + if let linkSectionView = self.linkSection.view { + if linkSectionView.superview == nil { + self.scrollView.addSubview(linkSectionView) + self.linkSection.parentState = state + } + transition.setFrame(view: linkSectionView, frame: linkSectionFrame) + alphaTransition.setAlpha(view: linkSectionView, alpha: self.areSuggestionsEnabled && !address.isEmpty ? 1.0 : 0.0) + } + if self.areSuggestionsEnabled && !address.isEmpty { + contentHeight += switchSectionSize.height + contentHeight += sectionSpacing } contentHeight += bottomContentInset @@ -546,3 +680,323 @@ public final class PostSuggestionsSettingsScreen: ViewControllerComponentContain super.containerLayoutUpdated(layout, transition: transition) } } + + +private final class LinkContentComponent: Component { + let theme: PresentationTheme + let link: String + + init( + theme: PresentationTheme, + link: String + ) { + self.theme = theme + self.link = link + } + + static func ==(lhs: LinkContentComponent, rhs: LinkContentComponent) -> Bool { + if lhs.theme !== rhs.theme { + return false + } + if lhs.link != rhs.link { + return false + } + return true + } + + final class View: UIView { + private var component: LinkContentComponent? + + private let background = ComponentView() + private let link = ComponentView() + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(component: LinkContentComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + + let padding: CGFloat = 10.0 + + let backgroundSize = self.background.update( + transition: transition, + component: AnyComponent( + FilledRoundedRectangleComponent( + color: component.theme.list.itemInputField.backgroundColor, + cornerRadius: .minEdge, + smoothCorners: false + ) + ), + environment: {}, + containerSize: availableSize + ) + let backgroundFrame = CGRect(origin: .zero, size: backgroundSize) + if let backgroundView = self.background.view { + if backgroundView.superview == nil { + self.addSubview(backgroundView) + } + transition.setFrame(view: backgroundView, frame: backgroundFrame) + } + + let linkFont = Font.regular(17.0) + let linkSize = self.link.update( + transition: transition, + component: AnyComponent( + MultilineTextComponent( + text: .plain(NSAttributedString(string: component.link, font: linkFont, textColor: component.theme.list.itemPrimaryTextColor)), + horizontalAlignment: .center, + maximumNumberOfLines: 2 + ) + ), + environment: {}, + containerSize: CGSize(width: availableSize.width - padding * 4.0, height: availableSize.height) + ) + let linkFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - linkSize.width) / 2.0), y: floorToScreenPixels((availableSize.height - linkSize.height) / 2.0) - UIScreenPixel), size: linkSize) + if let linkView = self.link.view { + if linkView.superview == nil { + self.addSubview(linkView) + } + transition.setFrame(view: linkView, frame: linkFrame) + } + + return availableSize + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + +private final class LinkComponent: Component { + let theme: PresentationTheme + let strings: PresentationStrings + let link: String + let copyAction: () -> Void + let shareAction: () -> Void + + init( + theme: PresentationTheme, + strings: PresentationStrings, + link: String, + copyAction: @escaping () -> Void, + shareAction: @escaping () -> Void + ) { + self.theme = theme + self.strings = strings + self.link = link + self.copyAction = copyAction + self.shareAction = shareAction + } + + static func ==(lhs: LinkComponent, rhs: LinkComponent) -> Bool { + if lhs.theme !== rhs.theme { + return false + } + if lhs.strings !== rhs.strings { + return false + } + if lhs.link != rhs.link { + return false + } + return true + } + + final class View: UIView { + private let linkButton = ComponentView() + private let moreButton = ComponentView() + private var copyButton = ComponentView() + private var shareButton = ComponentView() + + private var component: LinkComponent? + private weak var state: EmptyComponentState? + + private var cachedMoreImage: (UIImage, PresentationTheme)? + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(component: LinkComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + self.state = state + + let sideInset: CGFloat = 16.0 + var contentHeight: CGFloat = sideInset + + let linkButtonSize = self.linkButton.update( + transition: transition, + component: AnyComponent( + PlainButtonComponent( + content: AnyComponent(LinkContentComponent(theme: component.theme, link: component.link)), + action: { [weak self] in + guard let self, let component = self.component else { + return + } + component.copyAction() + }, + animateScale: false + ) + ), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 52.0) + ) + let linkButtonFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: linkButtonSize) + if let linkButtonView = self.linkButton.view { + if linkButtonView.superview == nil { + self.addSubview(linkButtonView) + } + linkButtonView.frame = linkButtonFrame + } + + let moreButtonImage: UIImage + if let (image, theme) = self.cachedMoreImage, theme === component.theme { + moreButtonImage = image + } else { + moreButtonImage = actionButtonImage(color: component.theme.list.itemInputField.controlColor)! + self.cachedMoreImage = (moreButtonImage, component.theme) + } + + let moreButtonSize = self.moreButton.update( + transition: transition, + component: AnyComponent( + PlainButtonComponent( + content: AnyComponent(Image(image: moreButtonImage, contentMode: .center)), + minSize: CGSize(width: 52.0, height: 52.0), + action: { [weak self] in + guard let self, let component = self.component else { + return + } + component.copyAction() + }, + animateScale: false + ) + ), + environment: {}, + containerSize: CGSize(width: 52.0, height: 52.0) + ) + let moreButtonFrame = CGRect(origin: CGPoint(x: availableSize.width - sideInset - moreButtonSize.width, y: contentHeight), size: moreButtonSize) + if let moreButtonView = self.moreButton.view { + if moreButtonView.superview == nil { + self.addSubview(moreButtonView) + } + moreButtonView.frame = moreButtonFrame + } + + contentHeight += linkButtonSize.height + contentHeight += 10.0 + + var buttonWidth = availableSize.width - sideInset * 2.0 + buttonWidth = (buttonWidth - 10.0) / 2.0 + + let copyButtonSize = self.copyButton.update( + transition: transition, + component: AnyComponent(ButtonComponent( + background: ButtonComponent.Background( + style: .glass, + color: component.theme.list.itemCheckColors.fillColor, + foreground: component.theme.list.itemCheckColors.foregroundColor, + pressedColor: component.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.8) + ), + content: AnyComponentWithIdentity(id: "label", component: AnyComponent(Text(text: component.strings.FolderLinkScreen_LinkActionCopy, font: Font.semibold(17.0), color: component.theme.list.itemCheckColors.foregroundColor))), + action: { [weak self] in + guard let self, let component = self.component else { + return + } + component.copyAction() + } + )), + environment: {}, + containerSize: CGSize(width: buttonWidth, height: 52.0) + ) + let copyButtonFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: copyButtonSize) + if let copyButtonView = self.copyButton.view { + if copyButtonView.superview == nil { + self.addSubview(copyButtonView) + } + copyButtonView.frame = copyButtonFrame + } + + let shareButtonSize = self.shareButton.update( + transition: transition, + component: AnyComponent(ButtonComponent( + background: ButtonComponent.Background( + style: .glass, + color: component.theme.list.itemCheckColors.fillColor, + foreground: component.theme.list.itemCheckColors.foregroundColor, + pressedColor: component.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.8) + ), + content: AnyComponentWithIdentity(id: "label", component: AnyComponent(Text(text: component.strings.FolderLinkScreen_LinkActionShare, font: Font.semibold(17.0), color: component.theme.list.itemCheckColors.foregroundColor))), + action: { [weak self] in + guard let self, let component = self.component else { + return + } + component.shareAction() + } + )), + environment: {}, + containerSize: CGSize(width: buttonWidth, height: 52.0) + ) + let shareButtonFrame = CGRect(origin: CGPoint(x: availableSize.width - sideInset - shareButtonSize.width, y: contentHeight), size: shareButtonSize) + if let shareButtonView = self.shareButton.view { + if shareButtonView.superview == nil { + self.addSubview(shareButtonView) + } + shareButtonView.frame = shareButtonFrame + } + + contentHeight += copyButtonSize.height + contentHeight += sideInset + + return CGSize(width: availableSize.width, height: contentHeight) + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + +func stringForRemainingTime(_ duration: Int32) -> String { + let hours = duration / 3600 + let minutes = duration / 60 % 60 + let seconds = duration % 60 + let durationString: String + if hours > 0 { + durationString = String(format: "%d:%02d", hours, minutes) + } else { + durationString = String(format: "%02d:%02d", minutes, seconds) + } + return durationString +} + +private func actionButtonImage(color: UIColor) -> UIImage? { + return generateImage(CGSize(width: 24.0, height: 24.0), contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + context.setFillColor(color.cgColor) + context.fillEllipse(in: CGRect(origin: CGPoint(), size: size)) + + context.setBlendMode(.clear) + context.fillEllipse(in: CGRect(origin: CGPoint(x: 4.0, y: 10.0), size: CGSize(width: 4.0, height: 4.0))) + context.fillEllipse(in: CGRect(origin: CGPoint(x: 10.0, y: 10.0), size: CGSize(width: 4.0, height: 4.0))) + context.fillEllipse(in: CGRect(origin: CGPoint(x: 16.0, y: 10.0), size: CGSize(width: 4.0, height: 4.0))) + }) +} diff --git a/submodules/TelegramUI/Components/PeerInfo/ProfileLevelRatingBarComponent/Sources/ProfileLevelRatingBarComponent.swift b/submodules/TelegramUI/Components/PeerInfo/ProfileLevelRatingBarComponent/Sources/ProfileLevelRatingBarComponent.swift index 8ba5e861..102613ff 100644 --- a/submodules/TelegramUI/Components/PeerInfo/ProfileLevelRatingBarComponent/Sources/ProfileLevelRatingBarComponent.swift +++ b/submodules/TelegramUI/Components/PeerInfo/ProfileLevelRatingBarComponent/Sources/ProfileLevelRatingBarComponent.swift @@ -483,7 +483,7 @@ public final class ProfileLevelRatingBarComponent: Component { self.state = state if self.barBackground.image == nil { - self.barBackground.image = generateStretchableFilledCircleImage(diameter: 12.0, color: .white)?.withRenderingMode(.alwaysTemplate) + self.barBackground.image = generateStretchableFilledCircleImage(diameter: 30.0, color: .white)?.withRenderingMode(.alwaysTemplate) self.barForeground.image = self.barBackground.image } diff --git a/submodules/TelegramUI/Components/PeerManagement/OldChannelsController/Sources/OldChannelsSearch.swift b/submodules/TelegramUI/Components/PeerManagement/OldChannelsController/Sources/OldChannelsSearch.swift index 5541bace..9f563290 100644 --- a/submodules/TelegramUI/Components/PeerManagement/OldChannelsController/Sources/OldChannelsSearch.swift +++ b/submodules/TelegramUI/Components/PeerManagement/OldChannelsController/Sources/OldChannelsSearch.swift @@ -391,7 +391,7 @@ private final class OldChannelsSearchItemNode: ItemListControllerSearchNode { self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: OldChannelsSearchContainerNode(context: self.context, peers: self.peers, selectedPeerIds: self.selectedPeerIds, togglePeer: self.togglePeer), cancel: { [weak self] in self?.cancel() - }) + }, fieldStyle: placeholderNode.fieldStyle) self.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: navigationBarHeight, transition: .immediate) self.searchDisplayController?.activate(insertSubnode: { [weak self, weak placeholderNode] subnode, isSearchBar in diff --git a/submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController/BUILD b/submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController/BUILD index 86c46876..0afb5003 100644 --- a/submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController/BUILD +++ b/submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController/BUILD @@ -18,11 +18,12 @@ swift_library( "//submodules/PresentationDataUtils", "//submodules/AccountContext", "//submodules/TextFormat", - "//submodules/AlertUI", "//submodules/PasswordSetupUI", "//submodules/Markdown", "//submodules/ActivityIndicator", "//submodules/TelegramUI/Components/PeerManagement/OldChannelsController", + "//submodules/TelegramUI/Components/AlertComponent", + "//submodules/TelegramUI/Components/AlertComponent/AlertInputFieldComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController/Sources/ChannelOwnershipTransferController.swift b/submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController/Sources/ChannelOwnershipTransferController.swift index e68b26f8..4ed82f94 100644 --- a/submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController/Sources/ChannelOwnershipTransferController.swift +++ b/submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController/Sources/ChannelOwnershipTransferController.swift @@ -5,460 +5,92 @@ import Display import SwiftSignalKit import TelegramCore import TelegramPresentationData -import ActivityIndicator import TextFormat import AccountContext -import AlertUI import PresentationDataUtils import PasswordSetupUI -import Markdown import OldChannelsController +import ComponentFlow +import AlertComponent +import AlertInputFieldComponent -private final class ChannelOwnershipTransferPasswordFieldNode: ASDisplayNode, UITextFieldDelegate { - private var theme: PresentationTheme - private let backgroundNode: ASImageNode - private let textInputNode: TextFieldNode - private let placeholderNode: ASTextNode - private var clearOnce: Bool = false - private let inputActivityNode: ActivityIndicator - - private var isChecking = false - - var complete: (() -> Void)? - var textChanged: ((String) -> Void)? - - private let backgroundInsets = UIEdgeInsets(top: 8.0, left: 22.0, bottom: 15.0, right: 22.0) - private let inputInsets = UIEdgeInsets(top: 5.0, left: 11.0, bottom: 5.0, right: 11.0) - - var password: String { - get { - return self.textInputNode.textField.text ?? "" - } - set { - self.textInputNode.textField.text = newValue - self.placeholderNode.isHidden = !newValue.isEmpty - } - } - - var placeholder: String = "" { - didSet { - self.placeholderNode.attributedText = NSAttributedString(string: self.placeholder, font: Font.regular(17.0), textColor: self.theme.actionSheet.inputPlaceholderColor) - } - } - - init(theme: PresentationTheme, placeholder: String) { - self.theme = theme - - self.backgroundNode = ASImageNode() - self.backgroundNode.isLayerBacked = true - self.backgroundNode.displaysAsynchronously = false - self.backgroundNode.displayWithoutProcessing = true - self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 16.0, color: theme.actionSheet.inputHollowBackgroundColor, strokeColor: theme.actionSheet.inputBorderColor, strokeWidth: UIScreenPixel) - - self.textInputNode = TextFieldNode() +private func commitChannelOwnershipTransferController( + context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, + peer: EnginePeer, + member: TelegramUser, + present: @escaping (ViewController, Any?) -> Void, + push: @escaping (ViewController) -> Void, + completion: @escaping (EnginePeer.Id?) -> Void +) -> ViewController { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let strings = presentationData.strings - self.placeholderNode = ASTextNode() - self.placeholderNode.isUserInteractionEnabled = false - self.placeholderNode.displaysAsynchronously = false - self.placeholderNode.attributedText = NSAttributedString(string: placeholder, font: Font.regular(14.0), textColor: self.theme.actionSheet.inputPlaceholderColor) - - self.inputActivityNode = ActivityIndicator(type: .custom(theme.list.itemAccentColor, 18.0, 1.5, false)) - - super.init() - - self.addSubnode(self.backgroundNode) - self.addSubnode(self.textInputNode) - self.addSubnode(self.placeholderNode) - self.addSubnode(self.inputActivityNode) - - self.inputActivityNode.isHidden = true - } - - override func didLoad() { - super.didLoad() - - self.textInputNode.textField.typingAttributes = [NSAttributedString.Key.font: Font.regular(14.0), NSAttributedString.Key.foregroundColor: self.theme.actionSheet.inputTextColor] - self.textInputNode.textField.font = Font.regular(14.0) - self.textInputNode.textField.textColor = self.theme.list.itemPrimaryTextColor - self.textInputNode.textField.isSecureTextEntry = true - self.textInputNode.textField.returnKeyType = .done - self.textInputNode.textField.keyboardAppearance = self.theme.rootController.keyboardColor.keyboardAppearance - self.textInputNode.clipsToBounds = true - self.textInputNode.textField.delegate = self - self.textInputNode.textField.addTarget(self, action: #selector(self.textFieldTextChanged(_:)), for: .editingChanged) - self.textInputNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0) - self.textInputNode.textField.tintColor = self.theme.list.itemAccentColor - } - - func updateTheme(_ theme: PresentationTheme) { - self.theme = theme - - self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 16.0, color: theme.actionSheet.inputHollowBackgroundColor, strokeColor: theme.actionSheet.inputBorderColor, strokeWidth: UIScreenPixel) - self.textInputNode.textField.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance - self.textInputNode.textField.textColor = theme.list.itemPrimaryTextColor - self.textInputNode.textField.typingAttributes = [NSAttributedString.Key.font: Font.regular(14.0), NSAttributedString.Key.foregroundColor: theme.actionSheet.inputTextColor] - self.textInputNode.textField.tintColor = theme.list.itemAccentColor - self.placeholderNode.attributedText = NSAttributedString(string: self.placeholderNode.attributedText?.string ?? "", font: Font.regular(14.0), textColor: theme.actionSheet.inputPlaceholderColor) - } - - func updateIsChecking(_ isChecking: Bool) { - self.isChecking = isChecking - self.inputActivityNode.isHidden = !isChecking - } - - func updateIsInvalid() { - self.clearOnce = true - } - - func updateLayout(width: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat { - let backgroundInsets = self.backgroundInsets - let inputInsets = self.inputInsets - - let textFieldHeight: CGFloat = 30.0 - let panelHeight = textFieldHeight + backgroundInsets.top + backgroundInsets.bottom - - let backgroundFrame = CGRect(origin: CGPoint(x: backgroundInsets.left, y: backgroundInsets.top), size: CGSize(width: width - backgroundInsets.left - backgroundInsets.right, height: panelHeight - backgroundInsets.top - backgroundInsets.bottom)) - transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame) - - let placeholderSize = self.placeholderNode.measure(backgroundFrame.size) - transition.updateFrame(node: self.placeholderNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + inputInsets.left, y: backgroundFrame.minY + floor((backgroundFrame.size.height - placeholderSize.height) / 2.0)), size: placeholderSize)) - - transition.updateFrame(node: self.textInputNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + inputInsets.left, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.size.width - inputInsets.left - inputInsets.right, height: backgroundFrame.size.height))) - - let activitySize = CGSize(width: 18.0, height: 18.0) - transition.updateFrame(node: self.inputActivityNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX - activitySize.width - 6.0, y: backgroundFrame.minY + floor((backgroundFrame.height - activitySize.height) / 2.0)), size: activitySize)) - - return panelHeight - } - - func activateInput() { - self.textInputNode.becomeFirstResponder() - } - - func deactivateInput() { - self.textInputNode.resignFirstResponder() - } - - @objc func editableTextNodeDidUpdateText(_ editableTextNode: ASEditableTextNode) { - self.textChanged?(editableTextNode.textView.text) - self.placeholderNode.isHidden = !(editableTextNode.textView.text ?? "").isEmpty - } - - @objc func textFieldTextChanged(_ textField: UITextField) { - let text = textField.text ?? "" - self.textChanged?(text) - self.placeholderNode.isHidden = !text.isEmpty - } - - func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { - if self.isChecking { - return false - } - - if string == "\n" { - self.complete?() - return false - } - - if self.clearOnce { - self.clearOnce = false - if range.length > string.count { - textField.text = "" - return false - } - } - - return true - } -} + let inputState = AlertInputFieldComponent.ExternalState() -public final class ChannelOwnershipTransferAlertContentNode: AlertContentNode { - private let strings: PresentationStrings - private let title: String - private let text: String - - private let titleNode: ASTextNode - private let textNode: ASTextNode - fileprivate let inputFieldNode: ChannelOwnershipTransferPasswordFieldNode - - private let actionNodesSeparator: ASDisplayNode - private let actionNodes: [TextAlertContentActionNode] - private let actionVerticalSeparators: [ASDisplayNode] - - private let disposable = MetaDisposable() - - private var validLayout: CGSize? - - private let hapticFeedback = HapticFeedback() - - public var complete: (() -> Void)? { - didSet { - self.inputFieldNode.complete = self.complete - } + let doneIsEnabled: Signal = inputState.valueSignal + |> map { value in + return !value.isEmpty } - public var theme: PresentationTheme { - didSet { - self.inputFieldNode.updateTheme(self.theme) - } - } + let doneInProgressPromise = ValuePromise(false) - public override var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled - } - - public init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, title: String, text: String, actions: [TextAlertAction]) { - self.strings = strings - self.theme = ptheme - self.title = title - self.text = text - - self.titleNode = ASTextNode() - self.titleNode.maximumNumberOfLines = 2 - self.textNode = ASTextNode() - self.textNode.maximumNumberOfLines = 4 - - self.inputFieldNode = ChannelOwnershipTransferPasswordFieldNode(theme: ptheme, placeholder: strings.Channel_OwnershipTransfer_PasswordPlaceholder) - - self.actionNodesSeparator = ASDisplayNode() - self.actionNodesSeparator.isLayerBacked = true - - self.actionNodes = actions.map { action -> TextAlertContentActionNode in - return TextAlertContentActionNode(theme: theme, action: action) - } - - var actionVerticalSeparators: [ASDisplayNode] = [] - if actions.count > 1 { - for _ in 0 ..< actions.count - 1 { - let separatorNode = ASDisplayNode() - separatorNode.isLayerBacked = true - actionVerticalSeparators.append(separatorNode) - } - } - self.actionVerticalSeparators = actionVerticalSeparators - - super.init() - - self.addSubnode(self.titleNode) - self.addSubnode(self.textNode) - - self.addSubnode(self.inputFieldNode) - - self.addSubnode(self.actionNodesSeparator) - - for actionNode in self.actionNodes { - self.addSubnode(actionNode) - } - self.actionNodes.last?.actionEnabled = false - - for separatorNode in self.actionVerticalSeparators { - self.addSubnode(separatorNode) - } - - self.inputFieldNode.textChanged = { [weak self] text in - if let strongSelf = self, let lastNode = strongSelf.actionNodes.last { - lastNode.actionEnabled = !text.isEmpty - } - } - - self.updateTheme(theme) - } - - deinit { - self.disposable.dispose() - } - - public func dismissInput() { - self.inputFieldNode.deactivateInput() - } - - public var password: String { - return self.inputFieldNode.password - } - - public func updateIsChecking(_ checking: Bool) { - self.inputFieldNode.updateIsChecking(checking) - } - - public override func updateTheme(_ theme: AlertControllerTheme) { - self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.bold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center) - self.textNode.attributedText = NSAttributedString(string: self.text, font: Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center) - - self.actionNodesSeparator.backgroundColor = theme.separatorColor - for actionNode in self.actionNodes { - actionNode.updateTheme(theme) - } - for separatorNode in self.actionVerticalSeparators { - separatorNode.backgroundColor = theme.separatorColor - } - - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) - } - } - - public override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - var size = size - size.width = min(size.width, 270.0) - let measureSize = CGSize(width: size.width - 16.0 * 2.0, height: CGFloat.greatestFiniteMagnitude) - - let hadValidLayout = self.validLayout != nil - - self.validLayout = size - - var origin: CGPoint = CGPoint(x: 0.0, y: 20.0) - - let titleSize = self.titleNode.measure(measureSize) - transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize)) - origin.y += titleSize.height + 4.0 - - let textSize = self.textNode.measure(measureSize) - transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize)) - origin.y += textSize.height + 6.0 - - let actionButtonHeight: CGFloat = 44.0 - var minActionsWidth: CGFloat = 0.0 - let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) - let actionTitleInsets: CGFloat = 8.0 - - var effectiveActionLayout = TextAlertContentActionLayout.horizontal - for actionNode in self.actionNodes { - let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) - if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 { - effectiveActionLayout = .vertical - } - switch effectiveActionLayout { - case .horizontal: - minActionsWidth += actionTitleSize.width + actionTitleInsets - case .vertical: - minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) - } - } - - let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0) - - var contentWidth = max(titleSize.width, minActionsWidth) - contentWidth = max(contentWidth, 234.0) - - var actionsHeight: CGFloat = 0.0 - switch effectiveActionLayout { - case .horizontal: - actionsHeight = actionButtonHeight - case .vertical: - actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) - } - - let resultWidth = contentWidth + insets.left + insets.right - - let inputFieldWidth = resultWidth - let inputFieldHeight = self.inputFieldNode.updateLayout(width: inputFieldWidth, transition: transition) - let inputHeight = inputFieldHeight - transition.updateFrame(node: self.inputFieldNode, frame: CGRect(x: 0.0, y: origin.y, width: resultWidth, height: inputFieldHeight)) - transition.updateAlpha(node: self.inputFieldNode, alpha: inputHeight > 0.0 ? 1.0 : 0.0) - - let resultSize = CGSize(width: resultWidth, height: titleSize.height + textSize.height + actionsHeight + inputHeight + insets.top + insets.bottom) - - transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - - var actionOffset: CGFloat = 0.0 - let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) - var separatorIndex = -1 - var nodeIndex = 0 - for actionNode in self.actionNodes { - if separatorIndex >= 0 { - let separatorNode = self.actionVerticalSeparators[separatorIndex] - switch effectiveActionLayout { - case .horizontal: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) - case .vertical: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: strings.Channel_OwnershipTransfer_EnterPassword) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(strings.Channel_OwnershipTransfer_EnterPasswordText)) + ) + )) + + var applyImpl: (() -> Void)? + content.append(AnyComponentWithIdentity( + id: "input", + component: AnyComponent( + AlertInputFieldComponent( + context: context, + placeholder: strings.Channel_OwnershipTransfer_PasswordPlaceholder, + isSecureTextEntry: true, + isInitiallyFocused: true, + externalState: inputState, + returnKeyAction: { + applyImpl?() } - } - separatorIndex += 1 - - let currentActionWidth: CGFloat - switch effectiveActionLayout { - case .horizontal: - if nodeIndex == self.actionNodes.count - 1 { - currentActionWidth = resultSize.width - actionOffset - } else { - currentActionWidth = actionWidth - } - case .vertical: - currentActionWidth = resultSize.width - } - - let actionNodeFrame: CGRect - switch effectiveActionLayout { - case .horizontal: - actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += currentActionWidth - case .vertical: - actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += actionButtonHeight - } - - transition.updateFrame(node: actionNode, frame: actionNodeFrame) - - nodeIndex += 1 - } - - if !hadValidLayout { - self.inputFieldNode.activateInput() - } - - return resultSize - } + ) + ) + )) - public func animateError() { - self.inputFieldNode.updateIsInvalid() - self.inputFieldNode.layer.addShakeAnimation() - self.hapticFeedback.error() + var effectiveUpdatedPresentationData: (PresentationData, Signal) + if let updatedPresentationData { + effectiveUpdatedPresentationData = updatedPresentationData + } else { + effectiveUpdatedPresentationData = (presentationData, context.sharedContext.presentationData) } -} - -private func commitChannelOwnershipTransferController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peer: EnginePeer, member: TelegramUser, present: @escaping (ViewController, Any?) -> Void, completion: @escaping (EnginePeer.Id?) -> Void) -> ViewController { - let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } var dismissImpl: (() -> Void)? - var proceedImpl: (() -> Void)? - - var pushControllerImpl: ((ViewController) -> Void)? - - let disposable = MetaDisposable() - - let contentNode = ChannelOwnershipTransferAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, title: presentationData.strings.Channel_OwnershipTransfer_EnterPassword, text: presentationData.strings.Channel_OwnershipTransfer_EnterPasswordText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - dismissImpl?() - }), TextAlertAction(type: .defaultAction, title: presentationData.strings.OwnershipTransfer_Transfer, action: { - proceedImpl?() - })]) - - contentNode.complete = { - proceedImpl?() - } - - let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode) - let presentationDataDisposable = (updatedPresentationData?.signal ?? context.sharedContext.presentationData).start(next: { [weak controller, weak contentNode] presentationData in - controller?.theme = AlertControllerTheme(presentationData: presentationData) - contentNode?.inputFieldNode.updateTheme(presentationData.theme) - }) - controller.dismissed = { _ in - presentationDataDisposable.dispose() - disposable.dispose() - } - dismissImpl = { [weak controller, weak contentNode] in - contentNode?.dismissInput() - controller?.dismissAnimated() - } - proceedImpl = { [weak contentNode] in - guard let contentNode = contentNode else { - return - } - contentNode.updateIsChecking(true) + let alertController = AlertScreen( + configuration: AlertScreen.Configuration(allowInputInset: true), + content: content, + actions: [ + .init(title: strings.Common_Cancel), + .init(title: strings.OwnershipTransfer_Transfer, type: .default, action: { + applyImpl?() + }, autoDismiss: false, isEnabled: doneIsEnabled, progress: doneInProgressPromise.get()) + ], + updatedPresentationData: effectiveUpdatedPresentationData + ) + applyImpl = { + doneInProgressPromise.set(true) let signal: Signal if case let .channel(peer) = peer { - signal = context.peerChannelMemberCategoriesContextsManager.transferOwnership(engine: context.engine, peerId: peer.id, memberId: member.id, password: contentNode.password) |> mapToSignal { _ in + signal = context.peerChannelMemberCategoriesContextsManager.transferOwnership(engine: context.engine, peerId: peer.id, memberId: member.id, password: inputState.value) |> mapToSignal { _ in return .complete() } |> then(.single(nil)) @@ -478,7 +110,7 @@ private func commitChannelOwnershipTransferController(context: AccountContext, u guard let upgradedPeerId = upgradedPeerId else { return .fail(.generic) } - return context.peerChannelMemberCategoriesContextsManager.transferOwnership(engine: context.engine, peerId: upgradedPeerId, memberId: member.id, password: contentNode.password) |> mapToSignal { _ in + return context.peerChannelMemberCategoriesContextsManager.transferOwnership(engine: context.engine, peerId: upgradedPeerId, memberId: member.id, password: inputState.value) |> mapToSignal { _ in return .complete() } |> then(.single(upgradedPeerId)) @@ -487,54 +119,61 @@ private func commitChannelOwnershipTransferController(context: AccountContext, u signal = .never() } - disposable.set((signal |> deliverOnMainQueue).start(next: { upgradedPeerId in + let _ = (signal + |> deliverOnMainQueue).start(next: { upgradedPeerId in dismissImpl?() completion(upgradedPeerId) - }, error: { [weak contentNode] error in + }, error: { error in var isGroup = true if case let .channel(channel) = peer, case .broadcast = channel.info { isGroup = false } + doneInProgressPromise.set(false) + var errorTextAndActions: (String, [TextAlertAction])? switch error { - case .tooMuchJoined: - pushControllerImpl?(oldChannelsController(context: context, intent: .upgrade)) - return - case .invalidPassword: - contentNode?.animateError() - case .limitExceeded: - errorTextAndActions = (presentationData.strings.TwoStepAuth_FloodError, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) - case .adminsTooMuch: - errorTextAndActions = (isGroup ? presentationData.strings.Group_OwnershipTransfer_ErrorAdminsTooMuch : presentationData.strings.Channel_OwnershipTransfer_ErrorAdminsTooMuch, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) - case .userPublicChannelsTooMuch: - errorTextAndActions = (presentationData.strings.Channel_OwnershipTransfer_ErrorPublicChannelsTooMuch, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) - case .userLocatedGroupsTooMuch: - errorTextAndActions = (presentationData.strings.Group_OwnershipTransfer_ErrorLocatedGroupsTooMuch, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) - case .userBlocked, .restricted: - errorTextAndActions = (isGroup ? presentationData.strings.Group_OwnershipTransfer_ErrorPrivacyRestricted : presentationData.strings.Channel_OwnershipTransfer_ErrorPrivacyRestricted, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) - default: - errorTextAndActions = (presentationData.strings.Login_UnknownError, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) + case .tooMuchJoined: + push(oldChannelsController(context: context, intent: .upgrade)) + return + case .invalidPassword: + inputState.animateError() + case .limitExceeded: + errorTextAndActions = (strings.TwoStepAuth_FloodError, [TextAlertAction(type: .defaultAction, title: strings.Common_OK, action: {})]) + case .adminsTooMuch: + errorTextAndActions = (isGroup ? strings.Group_OwnershipTransfer_ErrorAdminsTooMuch : strings.Channel_OwnershipTransfer_ErrorAdminsTooMuch, [TextAlertAction(type: .defaultAction, title: strings.Common_OK, action: {})]) + case .userPublicChannelsTooMuch: + errorTextAndActions = (strings.Channel_OwnershipTransfer_ErrorPublicChannelsTooMuch, [TextAlertAction(type: .defaultAction, title: strings.Common_OK, action: {})]) + case .userLocatedGroupsTooMuch: + errorTextAndActions = (strings.Group_OwnershipTransfer_ErrorLocatedGroupsTooMuch, [TextAlertAction(type: .defaultAction, title: strings.Common_OK, action: {})]) + case .userBlocked, .restricted: + errorTextAndActions = (isGroup ? strings.Group_OwnershipTransfer_ErrorPrivacyRestricted : strings.Channel_OwnershipTransfer_ErrorPrivacyRestricted, [TextAlertAction(type: .defaultAction, title: strings.Common_OK, action: {})]) + default: + errorTextAndActions = (strings.Login_UnknownError, [TextAlertAction(type: .defaultAction, title: strings.Common_OK, action: {})]) } - contentNode?.updateIsChecking(false) - + if let (text, actions) = errorTextAndActions { dismissImpl?() present(textAlertController(context: context, title: nil, text: text, actions: actions), nil) } - })) + }) } - - pushControllerImpl = { [weak controller] c in - controller?.push(c) + dismissImpl = { [weak alertController] in + alertController?.dismiss(completion: nil) } - - return controller + return alertController } -private func confirmChannelOwnershipTransferController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peer: EnginePeer, member: TelegramUser, present: @escaping (ViewController, Any?) -> Void, completion: @escaping (EnginePeer.Id?) -> Void) -> ViewController { +private func confirmChannelOwnershipTransferController( + context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, + peer: EnginePeer, + member: TelegramUser, + present: @escaping (ViewController, Any?) -> Void, + push: @escaping (ViewController) -> Void, + completion: @escaping (EnginePeer.Id?) -> Void +) -> ViewController { let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } - let theme = AlertControllerTheme(presentationData: presentationData) var isGroup = true if case let .channel(channel) = peer, case .broadcast = channel.info { @@ -551,69 +190,82 @@ private func confirmChannelOwnershipTransferController(context: AccountContext, text = presentationData.strings.Channel_OwnershipTransfer_DescriptionInfo(peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), EnginePeer.user(member).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).string } - let attributedTitle = NSAttributedString(string: title, font: Font.semibold(presentationData.listsFontSize.baseDisplaySize), textColor: theme.primaryColor, paragraphAlignment: .center) - let body = MarkdownAttributeSet(font: Font.regular(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: theme.primaryColor) - let bold = MarkdownAttributeSet(font: Font.semibold(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: theme.primaryColor) - let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .center) - - let controller = richTextAlertController(context: context, title: attributedTitle, text: attributedText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Channel_OwnershipTransfer_ChangeOwner, action: { - present(commitChannelOwnershipTransferController(context: context, peer: peer, member: member, present: present, completion: completion), nil) - }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: { - })], actionLayout: .vertical) + let controller = textAlertController( + context: context, + updatedPresentationData: updatedPresentationData, + title: title, + text: text, + actions: [ + TextAlertAction(type: .genericAction, title: presentationData.strings.Channel_OwnershipTransfer_ChangeOwner, action: { + present(commitChannelOwnershipTransferController(context: context, peer: peer, member: member, present: present, push: push, completion: completion), nil) + }), + TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {}) + ], + actionLayout: .vertical + ) return controller } -public func channelOwnershipTransferController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peer: EnginePeer, member: TelegramUser, initialError: ChannelOwnershipTransferError, present: @escaping (ViewController, Any?) -> Void, completion: @escaping (EnginePeer.Id?) -> Void) -> ViewController { +public func channelOwnershipTransferController( + context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, + peer: EnginePeer, + member: TelegramUser, + initialError: ChannelOwnershipTransferError, + present: @escaping (ViewController, Any?) -> Void, + push: @escaping (ViewController) -> Void, + completion: @escaping (EnginePeer.Id?) -> Void +) -> ViewController { let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } - let theme = AlertControllerTheme(presentationData: presentationData) + let strings = presentationData.strings - var title: NSAttributedString? = NSAttributedString(string: presentationData.strings.OwnershipTransfer_SecurityCheck, font: Font.semibold(presentationData.listsFontSize.itemListBaseFontSize), textColor: theme.primaryColor, paragraphAlignment: .center) - - var text = presentationData.strings.OwnershipTransfer_SecurityRequirements - let textFontSize = presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0 + var title: String? = strings.OwnershipTransfer_SecurityCheck + var text = strings.OwnershipTransfer_SecurityRequirements var isGroup = true if case let .channel(channel) = peer, case .broadcast = channel.info { isGroup = false } - var actions: [TextAlertAction] = [] + var actions: [AlertScreen.Action] = [ + .init(title: strings.Common_OK, type: .default) + ] switch initialError { - case .requestPassword: - return confirmChannelOwnershipTransferController(context: context, updatedPresentationData: updatedPresentationData, peer: peer, member: member, present: present, completion: completion) - case .twoStepAuthTooFresh, .authSessionTooFresh: - text = text + presentationData.strings.OwnershipTransfer_ComeBackLater - actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})] - case .twoStepAuthMissing: - actions = [TextAlertAction(type: .genericAction, title: presentationData.strings.OwnershipTransfer_SetupTwoStepAuth, action: { + case .requestPassword: + return confirmChannelOwnershipTransferController(context: context, updatedPresentationData: updatedPresentationData, peer: peer, member: member, present: present, push: push, completion: completion) + case .twoStepAuthTooFresh, .authSessionTooFresh: + text = text + strings.OwnershipTransfer_ComeBackLater + case .twoStepAuthMissing: + actions = [ + .init(title: strings.OwnershipTransfer_SetupTwoStepAuth, type: .default, action: { let controller = SetupTwoStepVerificationController(context: context, initialState: .automatic, stateUpdated: { update, shouldDismiss, controller in if shouldDismiss { controller.dismiss() } }) present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) - }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {})] - case .adminsTooMuch: - title = nil - text = isGroup ? presentationData.strings.Group_OwnershipTransfer_ErrorAdminsTooMuch : presentationData.strings.Channel_OwnershipTransfer_ErrorAdminsTooMuch - actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})] - case .userPublicChannelsTooMuch: - title = nil - text = presentationData.strings.Channel_OwnershipTransfer_ErrorPublicChannelsTooMuch - actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})] - case .userBlocked, .restricted: - title = nil - text = isGroup ? presentationData.strings.Group_OwnershipTransfer_ErrorPrivacyRestricted : presentationData.strings.Channel_OwnershipTransfer_ErrorPrivacyRestricted - actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})] - default: - title = nil - text = presentationData.strings.Login_UnknownError - actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})] + }), + .init(title: strings.Common_Cancel) + ] + case .adminsTooMuch: + title = nil + text = isGroup ? strings.Group_OwnershipTransfer_ErrorAdminsTooMuch : strings.Channel_OwnershipTransfer_ErrorAdminsTooMuch + case .userPublicChannelsTooMuch: + title = nil + text = strings.Channel_OwnershipTransfer_ErrorPublicChannelsTooMuch + case .userBlocked, .restricted: + title = nil + text = isGroup ? strings.Group_OwnershipTransfer_ErrorPrivacyRestricted : strings.Channel_OwnershipTransfer_ErrorPrivacyRestricted + default: + title = nil + text = strings.Login_UnknownError } - let body = MarkdownAttributeSet(font: Font.regular(textFontSize), textColor: theme.primaryColor) - let bold = MarkdownAttributeSet(font: Font.semibold(textFontSize), textColor: theme.primaryColor) - let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .center) - - return richTextAlertController(context: context, title: title, text: attributedText, actions: actions) + return AlertScreen( + context: context, + configuration: AlertScreen.Configuration(actionAlignment: .vertical), + title: title, + text: text, + actions: actions + ) } diff --git a/submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController/Sources/OwnershipTransferController.swift b/submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController/Sources/OwnershipTransferController.swift index 4d5f6e9e..baa29fbf 100644 --- a/submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController/Sources/OwnershipTransferController.swift +++ b/submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController/Sources/OwnershipTransferController.swift @@ -8,113 +8,162 @@ import TelegramPresentationData import ActivityIndicator import TextFormat import AccountContext -import AlertUI import PresentationDataUtils import PasswordSetupUI -import Markdown +import ComponentFlow +import AlertComponent +import AlertInputFieldComponent -private func commitOwnershipTransferController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, present: @escaping (ViewController, Any?) -> Void, commit: @escaping (String) -> Signal, completion: @escaping (MessageActionCallbackResult) -> Void) -> ViewController { - let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } +private func commitOwnershipTransferController( + context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, + present: @escaping (ViewController, Any?) -> Void, + commit: @escaping (String) -> Signal, + completion: @escaping (MessageActionCallbackResult) -> Void +) -> ViewController { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let strings = presentationData.strings + + let inputState = AlertInputFieldComponent.ExternalState() + + let doneIsEnabled: Signal = inputState.valueSignal + |> map { value in + return !value.isEmpty + } + + let doneInProgressPromise = ValuePromise(false) + + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: strings.OwnershipTransfer_EnterPassword) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(strings.OwnershipTransfer_EnterPasswordText)) + ) + )) + + var applyImpl: (() -> Void)? + content.append(AnyComponentWithIdentity( + id: "input", + component: AnyComponent( + AlertInputFieldComponent( + context: context, + placeholder: strings.Channel_OwnershipTransfer_PasswordPlaceholder, + isSecureTextEntry: true, + isInitiallyFocused: true, + externalState: inputState, + returnKeyAction: { + applyImpl?() + } + ) + ) + )) + + var effectiveUpdatedPresentationData: (PresentationData, Signal) + if let updatedPresentationData { + effectiveUpdatedPresentationData = updatedPresentationData + } else { + effectiveUpdatedPresentationData = (presentationData, context.sharedContext.presentationData) + } var dismissImpl: (() -> Void)? - var proceedImpl: (() -> Void)? - - let disposable = MetaDisposable() - - let contentNode = ChannelOwnershipTransferAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, title: presentationData.strings.OwnershipTransfer_EnterPassword, text: presentationData.strings.OwnershipTransfer_EnterPasswordText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - dismissImpl?() - }), TextAlertAction(type: .defaultAction, title: presentationData.strings.OwnershipTransfer_Transfer, action: { - proceedImpl?() - })]) - - contentNode.complete = { - proceedImpl?() - } - - let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode) - let presentationDataDisposable = (updatedPresentationData?.signal ?? context.sharedContext.presentationData).start(next: { [weak controller, weak contentNode] presentationData in - controller?.theme = AlertControllerTheme(presentationData: presentationData) - contentNode?.theme = presentationData.theme - }) - controller.dismissed = { _ in - presentationDataDisposable.dispose() - disposable.dispose() - } - dismissImpl = { [weak controller, weak contentNode] in - contentNode?.dismissInput() - controller?.dismissAnimated() - } - proceedImpl = { [weak contentNode] in - guard let contentNode = contentNode else { - return - } - contentNode.updateIsChecking(true) - - disposable.set((commit(contentNode.password) |> deliverOnMainQueue).start(next: { result in - completion(result) + let alertController = AlertScreen( + configuration: AlertScreen.Configuration(allowInputInset: true), + content: content, + actions: [ + .init(title: strings.Common_Cancel), + .init(title: strings.OwnershipTransfer_Transfer, type: .default, action: { + applyImpl?() + }, autoDismiss: false, isEnabled: doneIsEnabled, progress: doneInProgressPromise.get()) + ], + updatedPresentationData: effectiveUpdatedPresentationData + ) + applyImpl = { + doneInProgressPromise.set(true) + + let _ = (commit(inputState.value) + |> deliverOnMainQueue).start(next: { result in dismissImpl?() - }, error: { [weak contentNode] error in + completion(result) + }, error: { error in var errorTextAndActions: (String, [TextAlertAction])? switch error { - case .invalidPassword: - contentNode?.animateError() - case .limitExceeded: - errorTextAndActions = (presentationData.strings.TwoStepAuth_FloodError, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) - case .userBlocked, .restricted: - errorTextAndActions = (presentationData.strings.Group_OwnershipTransfer_ErrorPrivacyRestricted, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) - default: - errorTextAndActions = (presentationData.strings.Login_UnknownError, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) + case .invalidPassword: + inputState.animateError() + case .limitExceeded: + errorTextAndActions = (strings.TwoStepAuth_FloodError, [TextAlertAction(type: .defaultAction, title: strings.Common_OK, action: {})]) + case .userBlocked, .restricted: + errorTextAndActions = (presentationData.strings.Group_OwnershipTransfer_ErrorPrivacyRestricted, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) + default: + errorTextAndActions = (strings.Login_UnknownError, [TextAlertAction(type: .defaultAction, title: strings.Common_OK, action: {})]) } - contentNode?.updateIsChecking(false) - + doneInProgressPromise.set(false) + if let (text, actions) = errorTextAndActions { dismissImpl?() present(textAlertController(context: context, title: nil, text: text, actions: actions), nil) } - })) + }) } - - return controller + dismissImpl = { [weak alertController] in + alertController?.dismiss(completion: nil) + } + return alertController } -public func ownershipTransferController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, initialError: MessageActionCallbackError, present: @escaping (ViewController, Any?) -> Void, commit: @escaping (String) -> Signal, completion: @escaping (MessageActionCallbackResult) -> Void) -> ViewController { +public func ownershipTransferController( + context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, + initialError: MessageActionCallbackError, + present: @escaping (ViewController, Any?) -> Void, + commit: @escaping (String) -> Signal, + completion: @escaping (MessageActionCallbackResult) -> Void +) -> ViewController { let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } - let theme = AlertControllerTheme(presentationData: presentationData) + let strings = presentationData.strings - var title: NSAttributedString? = NSAttributedString(string: presentationData.strings.OwnershipTransfer_SecurityCheck, font: Font.semibold(presentationData.listsFontSize.itemListBaseFontSize), textColor: theme.primaryColor, paragraphAlignment: .center) + var title: String? = strings.OwnershipTransfer_SecurityCheck + var text = strings.OwnershipTransfer_SecurityRequirements - var text = presentationData.strings.OwnershipTransfer_SecurityRequirements - var actions: [TextAlertAction] = [] - let textFontSize = presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0 + var actions: [AlertScreen.Action] = [ + .init(title: strings.Common_OK, type: .default) + ] switch initialError { case .requestPassword: return commitOwnershipTransferController(context: context, updatedPresentationData: updatedPresentationData, present: present, commit: commit, completion: completion) case .twoStepAuthTooFresh, .authSessionTooFresh: text = text + presentationData.strings.OwnershipTransfer_ComeBackLater - actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})] case .twoStepAuthMissing: - actions = [TextAlertAction(type: .genericAction, title: presentationData.strings.OwnershipTransfer_SetupTwoStepAuth, action: { - let controller = SetupTwoStepVerificationController(context: context, initialState: .automatic, stateUpdated: { update, shouldDismiss, controller in - if shouldDismiss { - controller.dismiss() - } - }) - present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) - }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {})] + actions = [ + .init(title: strings.OwnershipTransfer_SetupTwoStepAuth, type: .default, action: { + let controller = SetupTwoStepVerificationController(context: context, initialState: .automatic, stateUpdated: { update, shouldDismiss, controller in + if shouldDismiss { + controller.dismiss() + } + }) + present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + }), + .init(title: strings.Common_Cancel) + ] case .userBlocked, .restricted: title = nil text = presentationData.strings.Group_OwnershipTransfer_ErrorPrivacyRestricted - actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})] default: title = nil text = presentationData.strings.Login_UnknownError - actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})] } - let body = MarkdownAttributeSet(font: Font.regular(textFontSize), textColor: theme.primaryColor) - let bold = MarkdownAttributeSet(font: Font.semibold(textFontSize), textColor: theme.primaryColor) - let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .center) - - return richTextAlertController(context: context, title: title, text: attributedText, actions: actions) + return AlertScreen( + context: context, + configuration: AlertScreen.Configuration(actionAlignment: .vertical), + title: title, + text: text, + actions: actions + ) } diff --git a/submodules/TelegramUI/Components/PeerSelectionController/BUILD b/submodules/TelegramUI/Components/PeerSelectionController/BUILD index 230b9636..f3d3fd0e 100644 --- a/submodules/TelegramUI/Components/PeerSelectionController/BUILD +++ b/submodules/TelegramUI/Components/PeerSelectionController/BUILD @@ -21,7 +21,7 @@ swift_library( "//submodules/ChatListUI", "//submodules/SearchBarNode", "//submodules/ContactListUI", - "//submodules/SegmentedControlNode", + "//submodules/TelegramUI/Components/SegmentControlComponent", "//submodules/AttachmentTextInputPanelNode", "//submodules/ChatPresentationInterfaceState", "//submodules/ChatSendMessageActionUI", @@ -35,6 +35,10 @@ swift_library( "//submodules/TextFormat", "//submodules/TelegramUI/Components/Chat/ForwardAccessoryPanelNode", "//submodules/CounterControllerTitleView", + "//submodules/TelegramUI/Components/ChatList/ChatListFilterTabContainerNode", + "//submodules/ComponentFlow", + "//submodules/Components/ComponentDisplayAdapters", + "//submodules/TelegramUI/Components/EdgeEffect", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift index db095daa..d5361bc5 100644 --- a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift +++ b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionController.swift @@ -10,6 +10,7 @@ import AccountContext import SearchUI import ChatListUI import CounterControllerTitleView +import ChatListFilterTabContainerNode public final class PeerSelectionControllerImpl: ViewController, PeerSelectionController { private let context: AccountContext @@ -112,7 +113,9 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon self.immediatelyActivateMultipleSelection = params.immediatelyActivateMultipleSelection self.multipleSelectionLimit = params.multipleSelectionLimit - super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData)) + super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData, style: .glass)) + + self._hasGlassStyle = true self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style @@ -147,7 +150,6 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon if params.forumPeerId == nil { self.navigationPresentation = .modal - self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed)) } self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) @@ -206,7 +208,9 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon if force { strongSelf.tabContainerNode?.cancelAnimations() } - strongSelf.tabContainerNode?.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: tabContainerData.0, selectedFilter: filter, isReordering: false, isEditing: false, canReorderAllChats: false, filtersLimit: tabContainerData.2, transitionFraction: fraction, presentationData: strongSelf.presentationData, transition: transition) + if let tabContainerNode = strongSelf.tabContainerNode { + tabContainerNode.update(size: CGSize(width: layout.size.width, height: 44.0), sideInset: layout.safeInsets.left, filters: tabContainerData.0, selectedFilter: filter, isReordering: false, isEditing: false, canReorderAllChats: false, filtersLimit: tabContainerData.2, transitionFraction: fraction, presentationData: strongSelf.presentationData, transition: transition) + } } self.tabContainerNode?.tabSelected = { [weak self] id, isDisabled in @@ -244,7 +248,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon private func updateThemeAndStrings() { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData, style: .glass), transition: .immediate) self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search) self.title = self.customTitle ?? self.presentationData.strings.Conversation_ForwardTitle self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) @@ -252,6 +256,8 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon } override public func loadDisplayNode() { + self.navigationBar?.secondaryContentHeight = 44.0 + 10.0 + self.displayNode = PeerSelectionControllerNode(context: self.context, controller: self, presentationData: self.presentationData, filter: self.filter, forumPeerId: self.forumPeerId, hasFilters: self.hasFilters, hasChatListSelector: self.hasChatListSelector, hasContactSelector: self.hasContactSelector, hasGlobalSearch: self.hasGlobalSearch, forwardedMessageIds: self.forwardedMessageIds, hasTypeHeaders: self.hasTypeHeaders, requestPeerType: self.requestPeerType, hasCreation: self.hasCreation, createNewGroup: self.createNewGroup, present: { [weak self] c, a in self?.present(c, in: .window(.root), with: a) }, presentInGlobalOverlay: { [weak self] c, a in @@ -419,8 +425,8 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon if let tabContainerNode = self.tabContainerNode, let mainContainerNode = self.peerSelectionNode.mainContainerNode { let tabContainerOffset: CGFloat = 0.0 let navigationBarHeight = self.navigationBar?.frame.maxY ?? 0.0 - transition.updateFrame(node: tabContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight - self.additionalNavigationBarHeight - 46.0 + tabContainerOffset), size: CGSize(width: layout.size.width, height: 46.0))) - tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: mainContainerNode.currentItemFilter, isReordering: false, isEditing: false, canReorderAllChats: false, filtersLimit: self.tabContainerData?.2, transitionFraction: mainContainerNode.transitionFraction, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring)) + transition.updateFrame(node: tabContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight - self.additionalNavigationBarHeight - 44.0 - 8.0 + tabContainerOffset), size: CGSize(width: layout.size.width, height: 44.0))) + tabContainerNode.update(size: CGSize(width: layout.size.width, height: 44.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: mainContainerNode.currentItemFilter, isReordering: false, isEditing: false, canReorderAllChats: false, filtersLimit: self.tabContainerData?.2, transitionFraction: mainContainerNode.transitionFraction, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring)) } } @@ -458,6 +464,8 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon } private var initializedFilters = false + private(set) var chatListFiltersNonEmpty: Bool = false + private func reloadFilters(firstUpdate: (() -> Void)? = nil) { let filterItems = chatListFilterItems(context: self.context) var notifiedFirstUpdate = false @@ -561,6 +569,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon let isEmpty = resolvedItems.count <= 1 + strongSelf.chatListFiltersNonEmpty = !isEmpty if wasEmpty != isEmpty, strongSelf.displayNavigationBar { strongSelf.navigationBar?.setSecondaryContentNode(isEmpty ? nil : strongSelf.tabContainerNode, animated: false) } @@ -568,8 +577,8 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon if let layout = strongSelf.validLayout { if wasEmpty != isEmpty { strongSelf.containerLayoutUpdated(layout, transition: .immediate) - } else { - strongSelf.tabContainerNode?.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: resolvedItems, selectedFilter: selectedEntryId, isReordering: false, isEditing: false, canReorderAllChats: false, filtersLimit: filtersLimit, transitionFraction: 0.0, presentationData: strongSelf.presentationData, transition: .animated(duration: 0.4, curve: .spring)) + } else if let tabContainerNode = strongSelf.tabContainerNode { + tabContainerNode.update(size: CGSize(width: layout.size.width, height: 44.0), sideInset: layout.safeInsets.left, filters: resolvedItems, selectedFilter: selectedEntryId, isReordering: false, isEditing: false, canReorderAllChats: false, filtersLimit: filtersLimit, transitionFraction: 0.0, presentationData: strongSelf.presentationData, transition: .animated(duration: 0.4, curve: .spring)) } } diff --git a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift index f34f345f..dbf4b785 100644 --- a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift @@ -11,7 +11,6 @@ import SearchBarNode import SearchUI import ContactListUI import ChatListUI -import SegmentedControlNode import AttachmentTextInputPanelNode import ChatPresentationInterfaceState import ChatSendMessageActionUI @@ -25,6 +24,10 @@ import ContextUI import TextFormat import ForwardAccessoryPanelNode import CounterControllerTitleView +import SegmentControlComponent +import ComponentFlow +import ComponentDisplayAdapters +import EdgeEffect final class PeerSelectionControllerNode: ASDisplayNode { private let context: AccountContext @@ -58,9 +61,10 @@ final class PeerSelectionControllerNode: ASDisplayNode { private let emptyTextNode: ImmediateTextNode private let emptyButtonNode: SolidRoundedButtonNode - private let toolbarBackgroundNode: NavigationBackgroundNode? - private let toolbarSeparatorNode: ASDisplayNode? - private let segmentedControlNode: SegmentedControlNode? + private let bottomEdgeEffectView: EdgeEffectView? + private let segmentedControl: ComponentView? + private var segmentedControlItems: [String]? + private var segmentedControlSelectedIndex: Int = 0 private var textInputPanelNode: AttachmentTextInputPanelNode? private var forwardAccessoryPanelNode: ForwardAccessoryPanelNode? @@ -127,7 +131,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { self.animationCache = context.animationCache self.animationRenderer = context.animationRenderer - self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: .builtin(WallpaperSettings()), theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.context.account.peerId, mode: .standard(.default), chatLocation: .peer(id: PeerId(0)), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil, businessIntro: nil) + self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: .builtin(WallpaperSettings()), theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.context.account.peerId, mode: .standard(.default), chatLocation: .peer(id: PeerId(0)), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil, businessIntro: nil) self.presentationInterfaceState = self.presentationInterfaceState.updatedInterfaceState { $0.withUpdatedForwardMessageIds(forwardedMessageIds) } self.presentationInterfaceStatePromise.set(self.presentationInterfaceState) @@ -170,20 +174,16 @@ final class PeerSelectionControllerNode: ASDisplayNode { } if hasChatListSelector && hasContactSelector { - self.toolbarBackgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.rootController.navigationBar.blurredBackgroundColor) + self.bottomEdgeEffectView = EdgeEffectView() - self.toolbarSeparatorNode = ASDisplayNode() - self.toolbarSeparatorNode?.backgroundColor = self.presentationData.theme.rootController.navigationBar.separatorColor - - let items = [ + self.segmentedControl = ComponentView() + self.segmentedControlItems = [ self.presentationData.strings.DialogList_TabTitle, self.presentationData.strings.Contacts_TabTitle ] - self.segmentedControlNode = SegmentedControlNode(theme: SegmentedControlTheme(theme: self.presentationData.theme), items: items.map { SegmentedControlItem(title: $0) }, selectedIndex: 0) } else { - self.toolbarBackgroundNode = nil - self.toolbarSeparatorNode = nil - self.segmentedControlNode = nil + self.bottomEdgeEffectView = nil + self.segmentedControl = nil } var chatListCategories: [ChatListNodeAdditionalCategory] = [] @@ -357,13 +357,9 @@ final class PeerSelectionControllerNode: ASDisplayNode { } if hasChatListSelector && hasContactSelector { - self.segmentedControlNode!.selectedIndexChanged = { [weak self] index in - self?.indexChanged(index) + if let bottomEdgeEffectView = self.bottomEdgeEffectView { + self.view.addSubview(bottomEdgeEffectView) } - - self.addSubnode(self.toolbarBackgroundNode!) - self.addSubnode(self.toolbarSeparatorNode!) - self.addSubnode(self.segmentedControlNode!) } if let requirementsBackgroundNode = self.requirementsBackgroundNode, let requirementsSeparatorNode = self.requirementsSeparatorNode, let requirementsTextNode = self.requirementsTextNode { @@ -700,7 +696,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { } } - let controller = chatTextLinkEditController(sharedContext: strongSelf.context.sharedContext, updatedPresentationData: (presentationData, .never()), account: strongSelf.context.account, text: text?.string ?? "", link: link, apply: { [weak self] link in + let controller = chatTextLinkEditController(context: context, updatedPresentationData: (presentationData, .never()), text: text?.string ?? "", link: link, apply: { [weak self] link in if let strongSelf = self, let inputMode = inputMode, let selectionRange = selectionRange { if let link = link { strongSelf.updateChatPresentationInterfaceState(animated: true, { state in @@ -1016,9 +1012,6 @@ final class PeerSelectionControllerNode: ASDisplayNode { self.updateChatPresentationInterfaceState({ $0.updatedTheme(self.presentationData.theme) }) self.requirementsBackgroundNode?.updateColor(color: self.presentationData.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate) - self.toolbarBackgroundNode?.updateColor(color: self.presentationData.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate) - self.toolbarSeparatorNode?.backgroundColor = self.presentationData.theme.rootController.navigationBar.separatorColor - self.segmentedControlNode?.updateTheme(SegmentedControlTheme(theme: self.presentationData.theme)) if let (layout, navigationBarHeight, actualNavigationBarHeight) = self.containerLayout { self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, actualNavigationBarHeight: actualNavigationBarHeight, transition: .immediate) @@ -1085,19 +1078,51 @@ final class PeerSelectionControllerNode: ASDisplayNode { toolbarHeight = countPanelHeight transition.updateFrame(node: countPanelNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - countPanelHeight), size: CGSize(width: layout.size.width, height: countPanelHeight))) } - } else if let segmentedControlNode = self.segmentedControlNode, let toolbarBackgroundNode = self.toolbarBackgroundNode, let toolbarSeparatorNode = self.toolbarSeparatorNode { + } else if let segmentedControl = self.segmentedControl, let segmentedControlItems = self.segmentedControlItems, let bottomEdgeEffectView = self.bottomEdgeEffectView { if let textPanelHeight = textPanelHeight { toolbarHeight = textPanelHeight + accessoryHeight } else { toolbarHeight += 44.0 } - transition.updateFrame(node: toolbarBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - toolbarHeight), size: CGSize(width: layout.size.width, height: toolbarHeight))) - toolbarBackgroundNode.update(size: toolbarBackgroundNode.bounds.size, transition: transition) - transition.updateFrame(node: toolbarSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - toolbarHeight), size: CGSize(width: layout.size.width, height: UIScreenPixel))) - let controlSize = segmentedControlNode.updateLayout(.sizeToFit(maximumWidth: layout.size.width, minimumWidth: 200.0, height: 32.0), transition: transition) + let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - toolbarHeight - 10.0), size: CGSize(width: layout.size.width, height: toolbarHeight + 10.0)) + transition.updateFrame(view: bottomEdgeEffectView, frame: edgeEffectFrame) + bottomEdgeEffectView.update(content: self.presentationData.theme.list.plainBackgroundColor, blur: true, alpha: 0.3, rect: edgeEffectFrame, edge: .bottom, edgeSize: min(30.0, edgeEffectFrame.height), transition: ComponentTransition(transition)) + + let controlSize = segmentedControl.update( + transition: ComponentTransition(transition), + component: AnyComponent(SegmentControlComponent( + theme: self.presentationData.theme, + items: (0 ..< segmentedControlItems.count).map { index in + return SegmentControlComponent.Item( + id: AnyHashable(index), + title: segmentedControlItems[index] + ) + }, + selectedId: AnyHashable(self.segmentedControlSelectedIndex), + action: { [weak self] id in + guard let self, let index = id.base as? Int else { + return + } + self.segmentedControlSelectedIndex = max(0, min(segmentedControlItems.count - 1, index)) + if let (layout, navigationBarHeight, actualNavigationBarHeight) = self.containerLayout { + self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, actualNavigationBarHeight: actualNavigationBarHeight, transition: .animated(duration: 0.4, curve: .spring)) + } + + self.indexChanged(index) + } + )), + environment: {}, + containerSize: CGSize(width: 200.0, height: 32.0) + ) let controlOrigin = layout.size.height - (textPanelHeight == nil ? toolbarHeight : 0.0) + floor((44.0 - controlSize.height) / 2.0) - transition.updateFrame(node: segmentedControlNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - controlSize.width) / 2.0), y: controlOrigin), size: controlSize)) + let segmentedControlFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - controlSize.width) / 2.0), y: controlOrigin), size: controlSize) + if let segmentedControlView = segmentedControl.view { + if segmentedControlView.superview == nil { + self.view.addSubview(segmentedControlView) + } + transition.updateFrame(view: segmentedControlView, frame: segmentedControlFrame) + } } insets.top += navigationBarHeight @@ -1363,7 +1388,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { if let requestDeactivateSearch = self?.requestDeactivateSearch { requestDeactivateSearch() } - } + }, fieldStyle: placeholderNode.fieldStyle ) self.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: navigationBarHeight, transition: .immediate) @@ -1438,7 +1463,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { if let requestDeactivateSearch = self?.requestDeactivateSearch { requestDeactivateSearch() } - }) + }, fieldStyle: placeholderNode.fieldStyle) self.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: navigationBarHeight, transition: .immediate) self.searchDisplayController?.activate(insertSubnode: { [weak self, weak placeholderNode] subnode, isSearchBar in @@ -1459,7 +1484,9 @@ final class PeerSelectionControllerNode: ASDisplayNode { self.chatListNode?.accessibilityElementsHidden = false self.mainContainerNode?.accessibilityElementsHidden = false - self.navigationBar?.setSecondaryContentNode(self.controller?.tabContainerNode, animated: true) + if let controller = self.controller, controller.chatListFiltersNonEmpty { + self.navigationBar?.setSecondaryContentNode(self.controller?.tabContainerNode, animated: true) + } self.controller?.setDisplayNavigationBar(true, transition: .animated(duration: 0.5, curve: .spring)) searchDisplayController.deactivate(placeholder: placeholderNode) @@ -1587,7 +1614,9 @@ final class PeerSelectionControllerNode: ASDisplayNode { } } } else if let contactListNode = self.contactListNode { - self.navigationBar?.setSecondaryContentNode(self.controller?.tabContainerNode, animated: false) + if let controller = self.controller, controller.chatListFiltersNonEmpty { + self.navigationBar?.setSecondaryContentNode(self.controller?.tabContainerNode, animated: false) + } contactListNode.enableUpdates = false if let mainContainerNode = self.mainContainerNode { diff --git a/submodules/TelegramUI/Components/Premium/PremiumDiamondComponent/Sources/PremiumDiamondComponent.swift b/submodules/TelegramUI/Components/Premium/PremiumDiamondComponent/Sources/PremiumDiamondComponent.swift index ffa39db9..06ac685c 100644 --- a/submodules/TelegramUI/Components/Premium/PremiumDiamondComponent/Sources/PremiumDiamondComponent.swift +++ b/submodules/TelegramUI/Components/Premium/PremiumDiamondComponent/Sources/PremiumDiamondComponent.swift @@ -251,7 +251,7 @@ public final class PremiumDiamondComponent: Component { func update(component: PremiumDiamondComponent, availableSize: CGSize, transition: ComponentTransition) -> CGSize { self.component = component - self.sceneView.backgroundColor = component.theme.list.blocksBackgroundColor + //self.sceneView.backgroundColor = component.theme.list.blocksBackgroundColor self.sceneView.bounds = CGRect(origin: .zero, size: CGSize(width: availableSize.width * 2.0, height: availableSize.height * 2.0)) self.sceneView.center = CGPoint(x: availableSize.width / 2.0, y: availableSize.height / 2.0) diff --git a/submodules/TelegramUI/Components/PremiumAlertController/BUILD b/submodules/TelegramUI/Components/PremiumAlertController/BUILD new file mode 100644 index 00000000..25d0e52f --- /dev/null +++ b/submodules/TelegramUI/Components/PremiumAlertController/BUILD @@ -0,0 +1,27 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "PremiumAlertController", + module_name = "PremiumAlertController", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", + "//submodules/AsyncDisplayKit:AsyncDisplayKit", + "//submodules/Display:Display", + "//submodules/Postbox:Postbox", + "//submodules/TelegramCore:TelegramCore", + "//submodules/AccountContext:AccountContext", + "//submodules/TelegramPresentationData:TelegramPresentationData", + "//submodules/ComponentFlow", + "//submodules/TelegramUI/Components/AlertComponent", + "//submodules/TelegramUI/Components/Premium/PremiumStarComponent", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/PremiumAlertController/Sources/PremiumAlertController.swift b/submodules/TelegramUI/Components/PremiumAlertController/Sources/PremiumAlertController.swift new file mode 100644 index 00000000..001ca763 --- /dev/null +++ b/submodules/TelegramUI/Components/PremiumAlertController/Sources/PremiumAlertController.swift @@ -0,0 +1,124 @@ +import Foundation +import UIKit +import SwiftSignalKit +import AsyncDisplayKit +import Display +import Postbox +import TelegramCore +import TelegramPresentationData +import AccountContext +import ComponentFlow +import AlertComponent +import PremiumStarComponent + +public func premiumAlertController( + context: AccountContext, + parentController: ViewController, + title: String? = nil, + text: String +) -> ViewController { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let strings = presentationData.strings + + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "header", + component: AnyComponent( + AlertPremiumStarComponent() + ) + )) + + let title = strings.PremiumNeeded_Title + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: title) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(text)) + ) + )) + + let alertController = AlertScreen( + context: context, + content: content, + actions: [ + .init(title: strings.Common_Cancel), + .init(title: strings.PremiumNeeded_Subscribe, type: .default, action: { [weak parentController] in + let controller = context.sharedContext.makePremiumIntroController(context: context, source: .nameColor, forceDark: false, dismissed: nil) + parentController?.push(controller) + }) + ] + ) + return alertController +} + +private final class AlertPremiumStarComponent: Component { + public typealias EnvironmentType = AlertComponentEnvironment + + public init() { + } + + public static func ==(lhs: AlertPremiumStarComponent, rhs: AlertPremiumStarComponent) -> Bool { + return true + } + + public final class View: UIView { + private let clippingView = UIView() + private let icon = ComponentView() + + private var component: AlertPremiumStarComponent? + private weak var state: EmptyComponentState? + + func update(component: AlertPremiumStarComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + self.state = state + + let environment = environment[AlertComponentEnvironment.self] + + let starHeight: CGFloat = 105.0 + let starSize = self.icon.update( + transition: .immediate, + component: AnyComponent( + PremiumStarComponent( + theme: environment.theme, + isIntro: false, + isVisible: true, + hasIdleAnimations: true, + colors: [ + UIColor(rgb: 0x6a94ff), + UIColor(rgb: 0x9472fd), + UIColor(rgb: 0xe26bd3) + ] + ) + ), + environment: {}, + containerSize: CGSize(width: availableSize.width + 60.0, height: 200.0) + ) + if let view = self.icon.view { + if view.superview == nil { + self.addSubview(self.clippingView) + self.clippingView.addSubview(view) + } + view.frame = CGRect(origin: CGPoint(x: 0.0, y: -24.0), size: starSize) + } + + self.clippingView.clipsToBounds = true + self.clippingView.layer.cornerRadius = 35.0 + self.clippingView.frame = CGRect(origin: CGPoint(x: -30.0, y: -22.0), size: CGSize(width: starSize.width, height: starSize.height)) + + return CGSize(width: availableSize.width, height: starHeight + 10.0) + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/SearchInputPanelComponent/Sources/SearchInputPanelComponent.swift b/submodules/TelegramUI/Components/SearchInputPanelComponent/Sources/SearchInputPanelComponent.swift index 66566d8e..2ff9c802 100644 --- a/submodules/TelegramUI/Components/SearchInputPanelComponent/Sources/SearchInputPanelComponent.swift +++ b/submodules/TelegramUI/Components/SearchInputPanelComponent/Sources/SearchInputPanelComponent.swift @@ -32,6 +32,7 @@ public final class SearchInputPanelComponent: Component { public let safeInsets: UIEdgeInsets public let placeholder: String? public let resetText: ResetText? + public let hasEdgeEffect: Bool public let updated: ((String) -> Void) public let cancel: () -> Void @@ -42,6 +43,7 @@ public final class SearchInputPanelComponent: Component { safeInsets: UIEdgeInsets, placeholder: String? = nil, resetText: ResetText? = nil, + hasEdgeEffect: Bool = true, updated: @escaping ((String) -> Void), cancel: @escaping () -> Void ) { @@ -51,6 +53,7 @@ public final class SearchInputPanelComponent: Component { self.safeInsets = safeInsets self.placeholder = placeholder self.resetText = resetText + self.hasEdgeEffect = hasEdgeEffect self.updated = updated self.cancel = cancel } @@ -292,7 +295,7 @@ public final class SearchInputPanelComponent: Component { component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: component.theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: component.theme.chat.inputPanel.panelControlColor ) )), action: { [weak self] _ in @@ -321,6 +324,7 @@ public final class SearchInputPanelComponent: Component { let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - edgeEffectHeight + 30.0), size: CGSize(width: size.width, height: edgeEffectHeight)) transition.setFrame(view: self.edgeEffectView, frame: edgeEffectFrame) self.edgeEffectView.update(content: edgeColor, blur: true, rect: edgeEffectFrame, edge: .bottom, edgeSize: edgeEffectFrame.height, transition: transition) + self.edgeEffectView.isHidden = !component.hasEdgeEffect transition.setFrame(view: self.containerView, frame: CGRect(origin: .zero, size: size)) self.containerView.update(size: size, isDark: component.theme.overallDarkAppearance, transition: transition) diff --git a/submodules/TelegramUI/Components/Settings/AccountFreezeInfoScreen/Sources/AccountFreezeInfoScreen.swift b/submodules/TelegramUI/Components/Settings/AccountFreezeInfoScreen/Sources/AccountFreezeInfoScreen.swift index 480ec2ea..8c974a43 100644 --- a/submodules/TelegramUI/Components/Settings/AccountFreezeInfoScreen/Sources/AccountFreezeInfoScreen.swift +++ b/submodules/TelegramUI/Components/Settings/AccountFreezeInfoScreen/Sources/AccountFreezeInfoScreen.swift @@ -49,7 +49,6 @@ private final class SheetContent: CombinedComponent { final class State: ComponentState { var cachedChevronImage: (UIImage, PresentationTheme)? - var cachedCloseImage: (UIImage, PresentationTheme)? let playOnce = ActionSlot() private var didPlayAnimation = false @@ -189,14 +188,15 @@ private final class SheetContent: CombinedComponent { contentSize.height += list.size.height contentSize.height += spacing + 2.0 + let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: environment.safeInsets.bottom, innerDiameter: 52.0, sideInset: 30.0) let buttonAttributedString = NSMutableAttributedString(string: strings.FrozenAccount_SubmitAppeal, font: Font.semibold(17.0), textColor: environment.theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center) let actionButton = actionButton.update( component: ButtonComponent( background: ButtonComponent.Background( + style: .glass, color: environment.theme.list.itemCheckColors.fillColor, foreground: environment.theme.list.itemCheckColors.foregroundColor, pressedColor: environment.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9), - cornerRadius: 10.0 ), content: AnyComponentWithIdentity( id: AnyHashable(0), @@ -209,7 +209,7 @@ private final class SheetContent: CombinedComponent { component.dismiss() } ), - availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0), + availableSize: CGSize(width: context.availableSize.width - buttonInsets.left - buttonInsets.right, height: 52.0), transition: context.transition ) context.add(actionButton @@ -223,10 +223,10 @@ private final class SheetContent: CombinedComponent { let closeButton = closeButton.update( component: ButtonComponent( background: ButtonComponent.Background( - color: .clear, - foreground: .clear, - pressedColor: .clear, - cornerRadius: 10.0 + style: .glass, + color: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.1), + foreground: theme.list.itemCheckColors.fillColor, + pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.8) ), content: AnyComponentWithIdentity( id: AnyHashable(1), @@ -238,19 +238,14 @@ private final class SheetContent: CombinedComponent { component.dismiss() } ), - availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0), + availableSize: CGSize(width: context.availableSize.width - buttonInsets.left - buttonInsets.right, height: 52.0), transition: context.transition ) context.add(closeButton .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + actionButton.size.height / 2.0)) ) contentSize.height += closeButton.size.height - - if environment.safeInsets.bottom > 0 { - contentSize.height += environment.safeInsets.bottom + 5.0 - } else { - contentSize.height += 12.0 - } + contentSize.height += buttonInsets.bottom state.playAnimationIfNeeded() @@ -312,6 +307,7 @@ private final class SheetContainerComponent: CombinedComponent { }) } )), + style: .glass, backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor), followContentSizeChanges: true, externalState: sheetExternalState, @@ -507,8 +503,8 @@ private final class ParagraphComponent: CombinedComponent { textColor: component.titleColor, paragraphAlignment: .natural )), - horizontalAlignment: .center, - maximumNumberOfLines: 1 + horizontalAlignment: .natural, + maximumNumberOfLines: 2 ), availableSize: CGSize(width: context.availableSize.width - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude), transition: .immediate @@ -576,24 +572,3 @@ private final class ParagraphComponent: CombinedComponent { } } } - -private func generateCloseButtonImage(backgroundColor: UIColor, foregroundColor: UIColor) -> UIImage? { - return generateImage(CGSize(width: 30.0, height: 30.0), contextGenerator: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - - context.setFillColor(backgroundColor.cgColor) - context.fillEllipse(in: CGRect(origin: CGPoint(), size: size)) - - context.setLineWidth(2.0) - context.setLineCap(.round) - context.setStrokeColor(foregroundColor.cgColor) - - context.move(to: CGPoint(x: 10.0, y: 10.0)) - context.addLine(to: CGPoint(x: 20.0, y: 20.0)) - context.strokePath() - - context.move(to: CGPoint(x: 20.0, y: 10.0)) - context.addLine(to: CGPoint(x: 10.0, y: 20.0)) - context.strokePath() - }) -} diff --git a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageListItemComponent.swift b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageListItemComponent.swift index 74bcf76e..6e604801 100644 --- a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageListItemComponent.swift +++ b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageListItemComponent.swift @@ -205,8 +205,6 @@ final class GreetingMessageListItemComponent: Component { }, openStarsTopup: { _ in }, - dismissNotice: { _ in - }, editPeer: { _ in }, openWebApp: { _ in diff --git a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageSetupScreen.swift b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageSetupScreen.swift index 9a4d2879..5a30131d 100644 --- a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageSetupScreen.swift +++ b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/AutomaticBusinessMessageSetupScreen.swift @@ -195,7 +195,7 @@ final class AutomaticBusinessMessageSetupScreenComponent: Component { if self.isOn { if !self.hasAccessToAllChatsByDefault && self.additionalPeerList.categories.isEmpty && self.additionalPeerList.peers.isEmpty { - self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.BusinessMessageSetup_ErrorNoRecipients_Text, actions: [ + self.environment?.controller()?.present(textAlertController(context: component.context, title: nil, text: presentationData.strings.BusinessMessageSetup_ErrorNoRecipients_Text, actions: [ TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { }), TextAlertAction(type: .defaultAction, title: presentationData.strings.BusinessMessageSetup_ErrorNoRecipients_ResetAction, action: { @@ -223,7 +223,7 @@ final class AutomaticBusinessMessageSetupScreenComponent: Component { } if let errorText { - self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: errorText, actions: [ + self.environment?.controller()?.present(textAlertController(context: component.context, title: nil, text: errorText, actions: [ TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { }), TextAlertAction(type: .defaultAction, title: presentationData.strings.BusinessMessageSetup_ErrorScheduleTime_ResetAction, action: { diff --git a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/BusinessLinksSetupScreen.swift b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/BusinessLinksSetupScreen.swift index 6eef6691..d7d1e466 100644 --- a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/BusinessLinksSetupScreen.swift +++ b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/BusinessLinksSetupScreen.swift @@ -4,6 +4,7 @@ import Display import ComponentFlow import ListSectionComponent import TelegramPresentationData +import PresentationDataUtils import AppBundle import AccountContext import ViewControllerComponent @@ -185,7 +186,7 @@ final class BusinessLinksSetupScreenComponent: Component { errorText = presentationData.strings.Business_Links_ErrorTooManyLinks } - environment.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: errorText, actions: [ + environment.controller()?.present(textAlertController(context: component.context, title: nil, text: errorText, actions: [ TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { }) ]), in: .window(.root)) diff --git a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift index 67c06654..6e85dd1e 100644 --- a/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift +++ b/submodules/TelegramUI/Components/Settings/AutomaticBusinessMessageSetupScreen/Sources/QuickReplySetupScreen.swift @@ -219,8 +219,6 @@ final class QuickReplySetupScreenComponent: Component { }, openStarsTopup: { _ in }, - dismissNotice: { _ in - }, editPeer: { [weak listNode] _ in guard let listNode, let parentView = listNode.parentView else { return @@ -798,8 +796,7 @@ final class QuickReplySetupScreenComponent: Component { } ))) : nil, rightButtons: rightButtons, - backTitle: isModal ? nil : strings.Common_Back, - backPressed: { [weak self] in + backPressed: isModal ? nil :{ [weak self] in guard let self else { return } @@ -818,14 +815,15 @@ final class QuickReplySetupScreenComponent: Component { strings: strings, statusBarHeight: statusBarHeight, sideInset: insets.left, - isSearchActive: self.isSearchDisplayControllerActive, - isSearchEnabled: !self.isEditing, + search: ChatListNavigationBar.Search(isEnabled: !self.isEditing), + activeSearch: self.isSearchDisplayControllerActive ? ChatListNavigationBar.ActiveSearch(isExternal: false) : nil, primaryContent: headerContent, secondaryContent: nil, secondaryTransition: 0.0, storySubscriptions: nil, storiesIncludeHidden: false, uploadProgress: [:], + headerPanels: nil, tabsNode: nil, tabsNodeIsSearch: false, accessoryPanelContainer: nil, @@ -1022,8 +1020,9 @@ final class QuickReplySetupScreenComponent: Component { let searchBarTheme = SearchBarNodeTheme(theme: environment.theme, hasSeparator: false) searchBarNode = SearchBarNode( theme: searchBarTheme, + presentationTheme: environment.theme, strings: environment.strings, - fieldStyle: .modern, + fieldStyle: .glass, displayBackground: false ) searchBarNode.placeholderString = NSAttributedString(string: environment.strings.Common_Search, font: Font.regular(17.0), textColor: searchBarTheme.placeholder) diff --git a/submodules/TelegramUI/Components/Settings/BirthdayPickerScreen/Sources/BirthdayPickerScreen.swift b/submodules/TelegramUI/Components/Settings/BirthdayPickerScreen/Sources/BirthdayPickerScreen.swift index a1972adc..7c92a69b 100644 --- a/submodules/TelegramUI/Components/Settings/BirthdayPickerScreen/Sources/BirthdayPickerScreen.swift +++ b/submodules/TelegramUI/Components/Settings/BirthdayPickerScreen/Sources/BirthdayPickerScreen.swift @@ -127,7 +127,7 @@ private final class BirthdayPickerSheetContentComponent: Component { component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: environment.theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: environment.theme.chat.inputPanel.panelControlColor ) )), action: { [weak self] _ in diff --git a/submodules/TelegramUI/Components/Settings/BusinessHoursSetupScreen/Sources/BusinessDaySetupScreen.swift b/submodules/TelegramUI/Components/Settings/BusinessHoursSetupScreen/Sources/BusinessDaySetupScreen.swift index a5dee4fe..6e90d95f 100644 --- a/submodules/TelegramUI/Components/Settings/BusinessHoursSetupScreen/Sources/BusinessDaySetupScreen.swift +++ b/submodules/TelegramUI/Components/Settings/BusinessHoursSetupScreen/Sources/BusinessDaySetupScreen.swift @@ -127,8 +127,7 @@ final class BusinessDaySetupScreenComponent: Component { } } - let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } - self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: enviroment.strings.BusinessHoursSetup_ErrorIntersectingHours_Text, actions: [ + self.environment?.controller()?.present(textAlertController(context: component.context, title: nil, text: enviroment.strings.BusinessHoursSetup_ErrorIntersectingHours_Text, actions: [ TextAlertAction(type: .genericAction, title: enviroment.strings.Common_Cancel, action: { }), TextAlertAction(type: .defaultAction, title: enviroment.strings.BusinessHoursSetup_ErrorIntersectingHours_ResetAction, action: { diff --git a/submodules/TelegramUI/Components/Settings/BusinessHoursSetupScreen/Sources/BusinessHoursSetupScreen.swift b/submodules/TelegramUI/Components/Settings/BusinessHoursSetupScreen/Sources/BusinessHoursSetupScreen.swift index 5b453f54..71e325ae 100644 --- a/submodules/TelegramUI/Components/Settings/BusinessHoursSetupScreen/Sources/BusinessHoursSetupScreen.swift +++ b/submodules/TelegramUI/Components/Settings/BusinessHoursSetupScreen/Sources/BusinessHoursSetupScreen.swift @@ -307,8 +307,7 @@ final class BusinessHoursSetupScreenComponent: Component { let _ = component.context.engine.accountData.updateAccountBusinessHours(businessHours: businessHours).startStandalone() return true } catch _ { - let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } - self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: environment.strings.BusinessHoursSetup_ErrorIntersectingDays_Text, actions: [ + self.environment?.controller()?.present(textAlertController(context: component.context, title: nil, text: environment.strings.BusinessHoursSetup_ErrorIntersectingDays_Text, actions: [ TextAlertAction(type: .genericAction, title: environment.strings.Common_Cancel, action: { }), TextAlertAction(type: .defaultAction, title: environment.strings.BusinessHoursSetup_ErrorIntersectingDays_ResetAction, action: { [weak self] in diff --git a/submodules/TelegramUI/Components/Settings/BusinessIntroSetupScreen/Sources/ChatIntroItemComponent.swift b/submodules/TelegramUI/Components/Settings/BusinessIntroSetupScreen/Sources/ChatIntroItemComponent.swift index 1bd282be..996ed9f9 100644 --- a/submodules/TelegramUI/Components/Settings/BusinessIntroSetupScreen/Sources/ChatIntroItemComponent.swift +++ b/submodules/TelegramUI/Components/Settings/BusinessIntroSetupScreen/Sources/ChatIntroItemComponent.swift @@ -126,7 +126,6 @@ final class ChatIntroItemComponent: Component { pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, - importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, diff --git a/submodules/TelegramUI/Components/Settings/BusinessLinkNameAlertController/BUILD b/submodules/TelegramUI/Components/Settings/BusinessLinkNameAlertController/BUILD index 174ee14b..dd67a3b4 100644 --- a/submodules/TelegramUI/Components/Settings/BusinessLinkNameAlertController/BUILD +++ b/submodules/TelegramUI/Components/Settings/BusinessLinkNameAlertController/BUILD @@ -20,6 +20,8 @@ swift_library( "//submodules/ComponentFlow", "//submodules/Components/MultilineTextComponent", "//submodules/Components/BalancedTextComponent", + "//submodules/TelegramUI/Components/AlertComponent", + "//submodules/TelegramUI/Components/AlertComponent/AlertInputFieldComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Settings/BusinessLinkNameAlertController/Sources/BusinessLinkNameAlertController.swift b/submodules/TelegramUI/Components/Settings/BusinessLinkNameAlertController/Sources/BusinessLinkNameAlertController.swift index b5bd40fb..8a531ac3 100644 --- a/submodules/TelegramUI/Components/Settings/BusinessLinkNameAlertController/Sources/BusinessLinkNameAlertController.swift +++ b/submodules/TelegramUI/Components/Settings/BusinessLinkNameAlertController/Sources/BusinessLinkNameAlertController.swift @@ -10,533 +10,73 @@ import AccountContext import ComponentFlow import MultilineTextComponent import BalancedTextComponent +import AlertComponent +import AlertInputFieldComponent -private final class PromptInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegate { - private var theme: PresentationTheme - private let backgroundNode: ASImageNode - private let textInputNode: EditableTextNode - private let placeholderNode: ASTextNode - private let characterLimitView = ComponentView() - - private let characterLimit: Int - - var updateHeight: (() -> Void)? - var complete: (() -> Void)? - var textChanged: ((String) -> Void)? - - private let backgroundInsets = UIEdgeInsets(top: 8.0, left: 16.0, bottom: 15.0, right: 16.0) - private let inputInsets: UIEdgeInsets - - private let validCharacterSets: [CharacterSet] - - var text: String { - get { - return self.textInputNode.attributedText?.string ?? "" - } - set { - self.textInputNode.attributedText = NSAttributedString(string: newValue, font: Font.regular(13.0), textColor: self.theme.actionSheet.inputTextColor) - self.placeholderNode.isHidden = !newValue.isEmpty - } - } - - var placeholder: String = "" { - didSet { - self.placeholderNode.attributedText = NSAttributedString(string: self.placeholder, font: Font.regular(13.0), textColor: self.theme.actionSheet.inputPlaceholderColor) - } - } - - init(theme: PresentationTheme, placeholder: String, characterLimit: Int) { - self.theme = theme - self.characterLimit = characterLimit - - self.inputInsets = UIEdgeInsets(top: 9.0, left: 6.0, bottom: 9.0, right: 16.0) - - self.backgroundNode = ASImageNode() - self.backgroundNode.isLayerBacked = true - self.backgroundNode.displaysAsynchronously = false - self.backgroundNode.displayWithoutProcessing = true - self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 16.0, color: theme.actionSheet.inputHollowBackgroundColor, strokeColor: theme.actionSheet.inputBorderColor, strokeWidth: 1.0) - - self.textInputNode = EditableTextNode() - self.textInputNode.typingAttributes = [NSAttributedString.Key.font.rawValue: Font.regular(13.0), NSAttributedString.Key.foregroundColor.rawValue: theme.actionSheet.inputTextColor] - self.textInputNode.clipsToBounds = true - self.textInputNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0) - self.textInputNode.textContainerInset = UIEdgeInsets(top: self.inputInsets.top, left: self.inputInsets.left, bottom: self.inputInsets.bottom, right: self.inputInsets.right) - self.textInputNode.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance - self.textInputNode.keyboardType = .default - self.textInputNode.autocapitalizationType = .none - self.textInputNode.returnKeyType = .done - self.textInputNode.autocorrectionType = .no - self.textInputNode.tintColor = theme.actionSheet.controlAccentColor - - self.placeholderNode = ASTextNode() - self.placeholderNode.isUserInteractionEnabled = false - self.placeholderNode.displaysAsynchronously = false - self.placeholderNode.attributedText = NSAttributedString(string: placeholder, font: Font.regular(13.0), textColor: self.theme.actionSheet.inputPlaceholderColor) - - self.validCharacterSets = [ - CharacterSet.alphanumerics, - CharacterSet(charactersIn: "0123456789_ "), - ] - - super.init() - - self.textInputNode.delegate = self - - self.addSubnode(self.backgroundNode) - self.addSubnode(self.textInputNode) - self.addSubnode(self.placeholderNode) - } - - func updateTheme(_ theme: PresentationTheme) { - self.theme = theme - - self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 16.0, color: self.theme.actionSheet.inputHollowBackgroundColor, strokeColor: self.theme.actionSheet.inputBorderColor, strokeWidth: 1.0) - self.textInputNode.keyboardAppearance = self.theme.rootController.keyboardColor.keyboardAppearance - self.placeholderNode.attributedText = NSAttributedString(string: self.placeholderNode.attributedText?.string ?? "", font: Font.regular(13.0), textColor: self.theme.actionSheet.inputPlaceholderColor) - self.textInputNode.tintColor = self.theme.actionSheet.controlAccentColor - } - - func updateLayout(width: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat { - let backgroundInsets = self.backgroundInsets - let inputInsets = self.inputInsets - - let textFieldHeight = self.calculateTextFieldMetrics(width: width) - let panelHeight = textFieldHeight + backgroundInsets.top + backgroundInsets.bottom - - let backgroundFrame = CGRect(origin: CGPoint(x: backgroundInsets.left, y: backgroundInsets.top), size: CGSize(width: width - backgroundInsets.left - backgroundInsets.right, height: panelHeight - backgroundInsets.top - backgroundInsets.bottom)) - transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame) - - let placeholderSize = self.placeholderNode.measure(backgroundFrame.size) - transition.updateFrame(node: self.placeholderNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + inputInsets.left + 5.0, y: backgroundFrame.minY + floor((backgroundFrame.size.height - placeholderSize.height) / 2.0)), size: placeholderSize)) - - transition.updateFrame(node: self.textInputNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + inputInsets.left, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.size.width - inputInsets.left - inputInsets.right, height: backgroundFrame.size.height))) - - let characterLimitString: String - let characterLimitColor: UIColor - if self.text.count <= self.characterLimit { - let remaining = self.characterLimit - self.text.count - if remaining < 5 { - characterLimitString = "\(remaining)" - } else { - characterLimitString = " " - } - characterLimitColor = self.theme.list.itemPlaceholderTextColor - } else { - characterLimitString = "\(self.characterLimit - self.text.count)" - characterLimitColor = self.theme.list.itemDestructiveColor - } - - let characterLimitSize = self.characterLimitView.update( - transition: .immediate, - component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: characterLimitString, font: Font.regular(13.0), textColor: characterLimitColor)) - )), - environment: {}, - containerSize: CGSize(width: 100.0, height: 100.0) +public func businessLinkNameAlertController( + context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, + value: String?, + apply: @escaping (String?) -> Void +) -> ViewController { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let strings = presentationData.strings + + let inputState = AlertInputFieldComponent.ExternalState() + + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: strings.Business_Links_LinkNameTitle) ) - if let characterLimitComponentView = self.characterLimitView.view { - if characterLimitComponentView.superview == nil { - self.view.addSubview(characterLimitComponentView) - } - characterLimitComponentView.frame = CGRect(origin: CGPoint(x: width - 23.0 - characterLimitSize.width, y: 18.0), size: characterLimitSize) - } - - return panelHeight - } - - func activateInput() { - self.textInputNode.becomeFirstResponder() - } - - func deactivateInput() { - self.textInputNode.resignFirstResponder() - } - - @objc func editableTextNodeDidUpdateText(_ editableTextNode: ASEditableTextNode) { - self.updateTextNodeText(animated: true) - self.textChanged?(editableTextNode.textView.text) - self.placeholderNode.isHidden = !(editableTextNode.textView.text ?? "").isEmpty - } - - func editableTextNode(_ editableTextNode: ASEditableTextNode, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { - if text == "\n" { - self.complete?() - return false - } - if text.unicodeScalars.contains(where: { c in - return !self.validCharacterSets.contains(where: { set in - return set.contains(c) - }) - }) { - return false - } - return true - } - - private func calculateTextFieldMetrics(width: CGFloat) -> CGFloat { - let backgroundInsets = self.backgroundInsets - let inputInsets = self.inputInsets - - let unboundTextFieldHeight = max(34.0, ceil(self.textInputNode.measure(CGSize(width: width - backgroundInsets.left - backgroundInsets.right - inputInsets.left - inputInsets.right, height: CGFloat.greatestFiniteMagnitude)).height)) - - return min(61.0, max(34.0, unboundTextFieldHeight)) - } - - private func updateTextNodeText(animated: Bool) { - let backgroundInsets = self.backgroundInsets - - let textFieldHeight = self.calculateTextFieldMetrics(width: self.bounds.size.width) - - let panelHeight = textFieldHeight + backgroundInsets.top + backgroundInsets.bottom - if !self.bounds.size.height.isEqual(to: panelHeight) { - self.updateHeight?() - } - } - - @objc func clearPressed() { - self.textInputNode.attributedText = nil - self.deactivateInput() - } -} - -public final class BusinessLinkNameAlertContentNode: AlertContentNode { - private let context: AccountContext - private var theme: AlertControllerTheme - private let strings: PresentationStrings - private let text: String - private let subtext: String - private let titleFont: PromptControllerTitleFont - - private let textView = ComponentView() - private let subtextView = ComponentView() - - fileprivate let inputFieldNode: PromptInputFieldNode - - private let actionNodesSeparator: ASDisplayNode - private let actionNodes: [TextAlertContentActionNode] - private let actionVerticalSeparators: [ASDisplayNode] - - private let disposable = MetaDisposable() - - private var validLayout: CGSize? - private var errorText: String? - - private let hapticFeedback = HapticFeedback() - - var complete: (() -> Void)? - - override public var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled - } - - init(context: AccountContext, theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction], text: String, subtext: String, titleFont: PromptControllerTitleFont, value: String?, characterLimit: Int) { - self.context = context - self.theme = theme - self.strings = strings - self.text = text - self.subtext = subtext - self.titleFont = titleFont - - self.inputFieldNode = PromptInputFieldNode(theme: ptheme, placeholder: strings.Business_Links_LinkNameInputPlaceholder, characterLimit: characterLimit) - self.inputFieldNode.text = value ?? "" - - self.actionNodesSeparator = ASDisplayNode() - self.actionNodesSeparator.isLayerBacked = true - - self.actionNodes = actions.map { action -> TextAlertContentActionNode in - return TextAlertContentActionNode(theme: theme, action: action) - } - - var actionVerticalSeparators: [ASDisplayNode] = [] - if actions.count > 1 { - for _ in 0 ..< actions.count - 1 { - let separatorNode = ASDisplayNode() - separatorNode.isLayerBacked = true - actionVerticalSeparators.append(separatorNode) - } - } - self.actionVerticalSeparators = actionVerticalSeparators - - super.init() - - self.addSubnode(self.inputFieldNode) - - self.addSubnode(self.actionNodesSeparator) - - for actionNode in self.actionNodes { - self.addSubnode(actionNode) - } - self.actionNodes.last?.actionEnabled = true - - for separatorNode in self.actionVerticalSeparators { - self.addSubnode(separatorNode) - } - - self.inputFieldNode.updateHeight = { [weak self] in - if let strongSelf = self { - if let _ = strongSelf.validLayout { - strongSelf.requestLayout?(.immediate) - } - } - } - - self.inputFieldNode.textChanged = { [weak self] text in - if let strongSelf = self, let lastNode = strongSelf.actionNodes.last { - lastNode.actionEnabled = text.count <= characterLimit - strongSelf.requestLayout?(.immediate) - } - } - - self.updateTheme(theme) - - self.inputFieldNode.complete = { [weak self] in - guard let self else { - return - } - if let lastNode = self.actionNodes.last, lastNode.actionEnabled { - self.complete?() - } - } - } - - deinit { - self.disposable.dispose() - } - - var value: String { - return self.inputFieldNode.text - } - - public func setErrorText(errorText: String?) { - if self.errorText != errorText { - self.errorText = errorText - self.requestLayout?(.immediate) - } - - if errorText != nil { - HapticFeedback().error() - self.inputFieldNode.layer.addShakeAnimation() - } - } - - override public func updateTheme(_ theme: AlertControllerTheme) { - self.theme = theme - - self.actionNodesSeparator.backgroundColor = theme.separatorColor - for actionNode in self.actionNodes { - actionNode.updateTheme(theme) - } - for separatorNode in self.actionVerticalSeparators { - separatorNode.backgroundColor = theme.separatorColor - } - - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) - } - } - - override public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - var size = size - size.width = min(size.width, 270.0) - let measureSize = CGSize(width: size.width - 16.0 * 2.0, height: CGFloat.greatestFiniteMagnitude) - - let hadValidLayout = self.validLayout != nil - - self.validLayout = size - - var origin: CGPoint = CGPoint(x: 0.0, y: 16.0) - let spacing: CGFloat = 5.0 - let subtextSpacing: CGFloat = -1.0 - - let textSize = self.textView.update( - transition: .immediate, - component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: self.text, font: Font.semibold(17.0), textColor: self.theme.primaryColor)), - horizontalAlignment: .center, - maximumNumberOfLines: 0 - )), - environment: {}, - containerSize: CGSize(width: measureSize.width, height: 1000.0) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(strings.Business_Links_LinkNameText)) ) - let textFrame = CGRect(origin: CGPoint(x: floor((size.width - textSize.width) * 0.5), y: origin.y), size: textSize) - if let textComponentView = self.textView.view { - if textComponentView.superview == nil { - textComponentView.layer.anchorPoint = CGPoint() - self.view.addSubview(textComponentView) - } - textComponentView.bounds = CGRect(origin: CGPoint(), size: textFrame.size) - transition.updatePosition(layer: textComponentView.layer, position: textFrame.origin) - } - origin.y += textSize.height + 6.0 + subtextSpacing - - let subtextSize = self.subtextView.update( - transition: .immediate, - component: AnyComponent(BalancedTextComponent( - text: .plain(NSAttributedString(string: self.errorText ?? self.subtext, font: Font.regular(13.0), textColor: self.errorText != nil ? self.theme.destructiveColor : self.theme.primaryColor)), - horizontalAlignment: .center, - maximumNumberOfLines: 0 - )), - environment: {}, - containerSize: CGSize(width: measureSize.width, height: 1000.0) - ) - let subtextFrame = CGRect(origin: CGPoint(x: floor((size.width - subtextSize.width) * 0.5), y: origin.y), size: subtextSize) - if let subtextComponentView = self.subtextView.view { - if subtextComponentView.superview == nil { - subtextComponentView.layer.anchorPoint = CGPoint() - self.view.addSubview(subtextComponentView) - } - subtextComponentView.bounds = CGRect(origin: CGPoint(), size: subtextFrame.size) - transition.updatePosition(layer: subtextComponentView.layer, position: subtextFrame.origin) - } - origin.y += subtextSize.height + 6.0 + spacing - - let actionButtonHeight: CGFloat = 44.0 - var minActionsWidth: CGFloat = 0.0 - let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) - let actionTitleInsets: CGFloat = 8.0 - - var effectiveActionLayout = TextAlertContentActionLayout.horizontal - for actionNode in self.actionNodes { - let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) - if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 { - effectiveActionLayout = .vertical - } - switch effectiveActionLayout { - case .horizontal: - minActionsWidth += actionTitleSize.width + actionTitleInsets - case .vertical: - minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) - } - } - - let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 9.0, right: 18.0) - - var contentWidth = max(textSize.width, minActionsWidth) - contentWidth = max(subtextSize.width, minActionsWidth) - contentWidth = max(contentWidth, 234.0) - - var actionsHeight: CGFloat = 0.0 - switch effectiveActionLayout { - case .horizontal: - actionsHeight = actionButtonHeight - case .vertical: - actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) - } - - let resultWidth = contentWidth + insets.left + insets.right - - let inputFieldWidth = resultWidth - let inputFieldHeight = self.inputFieldNode.updateLayout(width: inputFieldWidth, transition: transition) - let inputHeight = inputFieldHeight - let inputFieldFrame = CGRect(x: 0.0, y: origin.y, width: resultWidth, height: inputFieldHeight) - transition.updateFrame(node: self.inputFieldNode, frame: inputFieldFrame) - transition.updateAlpha(node: self.inputFieldNode, alpha: inputHeight > 0.0 ? 1.0 : 0.0) - - let resultSize = CGSize(width: resultWidth, height: textSize.height + subtextSpacing + subtextSize.height + spacing + inputHeight + actionsHeight + insets.top + insets.bottom) - - transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - - var actionOffset: CGFloat = 0.0 - let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) - var separatorIndex = -1 - var nodeIndex = 0 - for actionNode in self.actionNodes { - if separatorIndex >= 0 { - let separatorNode = self.actionVerticalSeparators[separatorIndex] - switch effectiveActionLayout { - case .horizontal: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) - case .vertical: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - } - } - separatorIndex += 1 - - let currentActionWidth: CGFloat - switch effectiveActionLayout { - case .horizontal: - if nodeIndex == self.actionNodes.count - 1 { - currentActionWidth = resultSize.width - actionOffset - } else { - currentActionWidth = actionWidth - } - case .vertical: - currentActionWidth = resultSize.width - } - - let actionNodeFrame: CGRect - switch effectiveActionLayout { - case .horizontal: - actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += currentActionWidth - case .vertical: - actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += actionButtonHeight - } - - transition.updateFrame(node: actionNode, frame: actionNodeFrame) - - nodeIndex += 1 - } - - if !hadValidLayout { - self.inputFieldNode.activateInput() - } - - return resultSize - } - - func animateError() { - self.inputFieldNode.layer.addShakeAnimation() - self.hapticFeedback.error() - } -} + )) -public enum PromptControllerTitleFont { - case regular - case bold -} - -public func businessLinkNameAlertController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, text: String, subtext: String, titleFont: PromptControllerTitleFont = .regular, value: String?, characterLimit: Int = 1000, apply: @escaping (String?) -> Void) -> AlertController { - let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } - - var dismissImpl: ((Bool) -> Void)? var applyImpl: (() -> Void)? - - let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - dismissImpl?(true) - apply(nil) - }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Done, action: { - applyImpl?() - })] - - let contentNode = BusinessLinkNameAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: actions, text: text, subtext: subtext, titleFont: titleFont, value: value, characterLimit: characterLimit) - contentNode.complete = { - applyImpl?() + content.append(AnyComponentWithIdentity( + id: "input", + component: AnyComponent( + AlertInputFieldComponent( + context: context, + initialValue: value, + placeholder: strings.Business_Links_LinkNameInputPlaceholder, + characterLimit: 32, + hasClearButton: false, + isInitiallyFocused: true, + externalState: inputState, + returnKeyAction: { + applyImpl?() + } + ) + ) + )) + + var effectiveUpdatedPresentationData: (PresentationData, Signal) + if let updatedPresentationData { + effectiveUpdatedPresentationData = updatedPresentationData + } else { + effectiveUpdatedPresentationData = (presentationData, context.sharedContext.presentationData) } - applyImpl = { [weak contentNode] in - guard let contentNode = contentNode else { - return - } - apply(contentNode.value) + + let alertController = AlertScreen( + configuration: AlertScreen.Configuration(allowInputInset: true), + content: content, + actions: [ + .init(title: strings.Common_Cancel), + .init(title: strings.Common_Done, type: .default, action: { + applyImpl?() + }) + ], + updatedPresentationData: effectiveUpdatedPresentationData + ) + applyImpl = { + apply(inputState.value) } - - let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode) - let presentationDataDisposable = (updatedPresentationData?.signal ?? context.sharedContext.presentationData).start(next: { [weak controller, weak contentNode] presentationData in - controller?.theme = AlertControllerTheme(presentationData: presentationData) - contentNode?.inputFieldNode.updateTheme(presentationData.theme) - }) - controller.dismissed = { _ in - presentationDataDisposable.dispose() - } - dismissImpl = { [weak controller] animated in - contentNode.inputFieldNode.deactivateInput() - if animated { - controller?.dismissAnimated() - } else { - controller?.dismiss() - } - } - return controller + return alertController } diff --git a/submodules/TelegramUI/Components/Settings/BusinessLocationSetupScreen/Sources/BusinessLocationSetupScreen.swift b/submodules/TelegramUI/Components/Settings/BusinessLocationSetupScreen/Sources/BusinessLocationSetupScreen.swift index f1836fed..6f0654f8 100644 --- a/submodules/TelegramUI/Components/Settings/BusinessLocationSetupScreen/Sources/BusinessLocationSetupScreen.swift +++ b/submodules/TelegramUI/Components/Settings/BusinessLocationSetupScreen/Sources/BusinessLocationSetupScreen.swift @@ -127,8 +127,7 @@ final class BusinessLocationSetupScreenComponent: Component { let businessLocation = self.currentBusinessLocation() if businessLocation != component.initialValue { - let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } - self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: environment.strings.BusinessLocationSetup_AlertUnsavedChanges_Text, actions: [ + self.environment?.controller()?.present(textAlertController(context: component.context, title: nil, text: environment.strings.BusinessLocationSetup_AlertUnsavedChanges_Text, actions: [ TextAlertAction(type: .genericAction, title: environment.strings.Common_Cancel, action: { }), TextAlertAction(type: .destructiveAction, title: environment.strings.BusinessLocationSetup_AlertUnsavedChanges_ResetAction, action: { @@ -250,8 +249,7 @@ final class BusinessLocationSetupScreenComponent: Component { let businessLocation = self.currentBusinessLocation() if businessLocation != nil && address.isEmpty { - let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } - self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: environment.strings.BusinessLocationSetup_ErrorAddressEmpty_Text, actions: [ + self.environment?.controller()?.present(textAlertController(context: component.context, title: nil, text: environment.strings.BusinessLocationSetup_ErrorAddressEmpty_Text, actions: [ TextAlertAction(type: .genericAction, title: environment.strings.Common_OK, action: { }) ]), in: .window(.root)) diff --git a/submodules/TelegramUI/Components/Settings/ChatbotSetupScreen/BUILD b/submodules/TelegramUI/Components/Settings/ChatbotSetupScreen/BUILD index c83d5538..210ac682 100644 --- a/submodules/TelegramUI/Components/Settings/ChatbotSetupScreen/BUILD +++ b/submodules/TelegramUI/Components/Settings/ChatbotSetupScreen/BUILD @@ -34,6 +34,7 @@ swift_library( "//submodules/AvatarNode", "//submodules/TelegramUI/Components/PlainButtonComponent", "//submodules/TelegramUI/Components/Stories/PeerListItemComponent", + "//submodules/TelegramUI/Components/AlertComponent", "//submodules/ShimmerEffect", ], visibility = [ diff --git a/submodules/TelegramUI/Components/Settings/ChatbotSetupScreen/Sources/ChatbotSetupScreen.swift b/submodules/TelegramUI/Components/Settings/ChatbotSetupScreen/Sources/ChatbotSetupScreen.swift index c7aa5fd0..f6807785 100644 --- a/submodules/TelegramUI/Components/Settings/ChatbotSetupScreen/Sources/ChatbotSetupScreen.swift +++ b/submodules/TelegramUI/Components/Settings/ChatbotSetupScreen/Sources/ChatbotSetupScreen.swift @@ -23,6 +23,7 @@ import LottieComponent import Markdown import PeerListItemComponent import AvatarNode +import AlertComponent private let checkIcon: UIImage = { return generateImage(CGSize(width: 12.0, height: 10.0), rotatedContext: { size, context in @@ -527,14 +528,20 @@ final class ChatbotSetupScreenComponent: Component { } else { text = environment.strings.ChatbotSetup_Gift_Warning_CombinedText(botUsername).string } - let alertController = textAlertController(context: component.context, title: environment.strings.ChatbotSetup_Gift_Warning_Title, text: text, actions: [ - TextAlertAction(type: .genericAction, title: environment.strings.Common_Cancel, action: { - completion(false) - }), - TextAlertAction(type: .defaultAction, title: environment.strings.ChatbotSetup_Gift_Warning_Proceed, action: { - completion(true) - }) - ], parseMarkdown: true) + + let alertController = AlertScreen( + context: component.context, + title: environment.strings.ChatbotSetup_Gift_Warning_Title, + text: text, + actions: [ + .init(title: environment.strings.Common_Cancel, action: { + completion(false) + }), + .init(title: environment.strings.ChatbotSetup_Gift_Warning_Proceed, type: .default, action: { + completion(true) + }), + ] + ) alertController.dismissed = { byOutsideTap in if byOutsideTap { completion(false) @@ -785,7 +792,7 @@ final class ChatbotSetupScreenComponent: Component { self.botRights = [.reply, .readMessages, .deleteSentMessages, .deleteReceivedMessages] self.state?.updated(transition: .spring(duration: 0.3)) } else { - self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.ChatbotSetup_ErrorBotNotBusinessCapable, actions: [ + self.environment?.controller()?.present(textAlertController(context: component.context, title: nil, text: presentationData.strings.ChatbotSetup_ErrorBotNotBusinessCapable, actions: [ TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { }) ]), in: .window(.root)) diff --git a/submodules/TelegramUI/Components/Settings/CollectibleItemInfoScreen/Sources/CollectibleItemInfoScreen.swift b/submodules/TelegramUI/Components/Settings/CollectibleItemInfoScreen/Sources/CollectibleItemInfoScreen.swift index 8ad146fd..4e3dd73d 100644 --- a/submodules/TelegramUI/Components/Settings/CollectibleItemInfoScreen/Sources/CollectibleItemInfoScreen.swift +++ b/submodules/TelegramUI/Components/Settings/CollectibleItemInfoScreen/Sources/CollectibleItemInfoScreen.swift @@ -522,7 +522,7 @@ private final class CollectibleItemInfoScreenContentComponent: Component { if environment.safeInsets.bottom.isZero { contentHeight += 16.0 } else { - contentHeight += environment.safeInsets.bottom + 14.0 + contentHeight += environment.safeInsets.bottom + 1.0 } return CGSize(width: availableSize.width, height: contentHeight) diff --git a/submodules/TelegramUI/Components/Settings/LanguageSelectionScreen/Sources/LanguageSelectionScreen.swift b/submodules/TelegramUI/Components/Settings/LanguageSelectionScreen/Sources/LanguageSelectionScreen.swift index 9d698608..c2f3c3ac 100644 --- a/submodules/TelegramUI/Components/Settings/LanguageSelectionScreen/Sources/LanguageSelectionScreen.swift +++ b/submodules/TelegramUI/Components/Settings/LanguageSelectionScreen/Sources/LanguageSelectionScreen.swift @@ -85,7 +85,7 @@ public class LanguageSelectionScreen: ViewController { private func updateThemeAndStrings() { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData), transition: .immediate) self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search) self.title = self.presentationData.strings.Settings_AppLanguage self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) diff --git a/submodules/TelegramUI/Components/Settings/LanguageSelectionScreen/Sources/LanguageSelectionScreenNode.swift b/submodules/TelegramUI/Components/Settings/LanguageSelectionScreen/Sources/LanguageSelectionScreenNode.swift index 5cf8f197..357421ef 100644 --- a/submodules/TelegramUI/Components/Settings/LanguageSelectionScreen/Sources/LanguageSelectionScreenNode.swift +++ b/submodules/TelegramUI/Components/Settings/LanguageSelectionScreen/Sources/LanguageSelectionScreenNode.swift @@ -540,7 +540,8 @@ final class LanguageSelectionScreenNode: ViewControllerTracingNode { inline: true, cancel: { [weak self] in self?.requestDeactivateSearch() - } + }, + fieldStyle: placeholderNode.fieldStyle ) self.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: navigationBarHeight, transition: .immediate) diff --git a/submodules/TelegramUI/Components/Settings/PasskeysScreen/Sources/PasskeysScreen.swift b/submodules/TelegramUI/Components/Settings/PasskeysScreen/Sources/PasskeysScreen.swift index ba88ee22..02ee5142 100644 --- a/submodules/TelegramUI/Components/Settings/PasskeysScreen/Sources/PasskeysScreen.swift +++ b/submodules/TelegramUI/Components/Settings/PasskeysScreen/Sources/PasskeysScreen.swift @@ -179,14 +179,21 @@ final class PasskeysScreenComponent: Component { guard let component = self.component, let environment = self.environment, let controller = environment.controller() else { return } - let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }) - controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: environment.strings.Passkeys_DeleteAlert_Title, text: environment.strings.Passkeys_DeleteAlert_Text, actions: [TextAlertAction(type: .genericAction, title: environment.strings.Common_Cancel, action: { - }), TextAlertAction(type: .destructiveAction, title: environment.strings.Passkeys_DeleteAlert_Action, action: { [weak self] in - guard let self else { - return - } - self.deletePasskey(id: id) - })]), in: .window(.root)) + let alertController = textAlertController( + context: component.context, + title: environment.strings.Passkeys_DeleteAlert_Title, + text: environment.strings.Passkeys_DeleteAlert_Text, + actions: [ + TextAlertAction(type: .genericAction, title: environment.strings.Common_Cancel, action: {}), + TextAlertAction(type: .destructiveAction, title: environment.strings.Passkeys_DeleteAlert_Action, action: { [weak self] in + guard let self else { + return + } + self.deletePasskey(id: id) + }) + ] + ) + controller.present(alertController, in: .window(.root)) } private func deletePasskey(id: String) { diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorItem/Sources/PeerNameColorItem.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorItem/Sources/PeerNameColorItem.swift index c9306e02..6c3100f8 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorItem/Sources/PeerNameColorItem.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorItem/Sources/PeerNameColorItem.swift @@ -370,7 +370,7 @@ public final class PeerNameColorItemNode: ListViewItemNode, ItemListItemNode { self.maskNode = ASImageNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.containerNode) } diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/BackButton.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/BackButton.swift index 271cae3d..ff04a480 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/BackButton.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/BackButton.swift @@ -149,7 +149,7 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { case .back: text = presentationData.strings.Common_Back accessibilityText = presentationData.strings.Common_Back - icon = NavigationBar.backArrowImage(color: .white) + icon = navigationBarBackArrowImage(color: .white) case .edit: text = presentationData.strings.Common_Edit accessibilityText = text diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/ChannelAppearanceScreen.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/ChannelAppearanceScreen.swift index 22d93e09..25a60b74 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/ChannelAppearanceScreen.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/ChannelAppearanceScreen.swift @@ -39,6 +39,7 @@ import Markdown import GroupStickerPackSetupController import PeerNameColorItem import EmojiActionIconComponent +import EdgeEffect final class ChannelAppearanceScreenComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment @@ -169,13 +170,13 @@ final class ChannelAppearanceScreenComponent: Component { } final class View: UIView, UIScrollViewDelegate { + private let edgeEffectView: EdgeEffectView private let topOverscrollLayer = SimpleLayer() private let scrollView: ScrollView private let actionButton = ComponentView() private let bottomPanelBackgroundView: BlurredBackgroundView private let bottomPanelSeparator: SimpleLayer - private let backButton = PeerInfoHeaderNavigationButton() private let navigationTitle = ComponentView() private let previewSection = ComponentView() @@ -227,6 +228,8 @@ final class ChannelAppearanceScreenComponent: Component { private weak var emojiStatusSelectionController: ViewController? override init(frame: CGRect) { + self.edgeEffectView = EdgeEffectView() + self.scrollView = ScrollView() self.scrollView.showsVerticalScrollIndicator = true self.scrollView.showsHorizontalScrollIndicator = false @@ -249,14 +252,10 @@ final class ChannelAppearanceScreenComponent: Component { self.scrollView.layer.addSublayer(self.topOverscrollLayer) + self.addSubview(self.edgeEffectView) + self.addSubview(self.bottomPanelBackgroundView) self.layer.addSublayer(self.bottomPanelSeparator) - - self.backButton.action = { [weak self] _, _ in - if let self, let controller = self.environment?.controller() { - controller.navigationController?.popViewController(animated: true) - } - } } required init?(coder: NSCoder) { @@ -291,20 +290,26 @@ final class ChannelAppearanceScreenComponent: Component { } let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } - self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.Channel_Appearance_UnsavedChangesAlertTitle, text: presentationData.strings.Channel_Appearance_UnsavedChangesAlertText, actions: [ - TextAlertAction(type: .genericAction, title: presentationData.strings.Channel_Appearance_UnsavedChangesAlertDiscard, action: { [weak self] in - guard let self else { - return - } - self.environment?.controller()?.dismiss() - }), - TextAlertAction(type: .defaultAction, title: presentationData.strings.Channel_Appearance_UnsavedChangesAlertApply, action: { [weak self] in - guard let self else { - return - } - self.applySettings() - }) - ]), in: .window(.root)) + let alertController = textAlertController( + context: component.context, + title: presentationData.strings.Channel_Appearance_UnsavedChangesAlertTitle, + text: presentationData.strings.Channel_Appearance_UnsavedChangesAlertText, + actions: [ + TextAlertAction(type: .genericAction, title: presentationData.strings.Channel_Appearance_UnsavedChangesAlertDiscard, action: { [weak self] in + guard let self else { + return + } + self.environment?.controller()?.dismiss() + }), + TextAlertAction(type: .defaultAction, title: presentationData.strings.Channel_Appearance_UnsavedChangesAlertApply, action: { [weak self] in + guard let self else { + return + } + self.applySettings() + }) + ] + ) + self.environment?.controller()?.present(alertController, in: .window(.root)) return false } @@ -343,6 +348,8 @@ final class ChannelAppearanceScreenComponent: Component { transition.setAlpha(view: navigationTitleView, alpha: navigationAlpha) } + transition.setAlpha(view: self.edgeEffectView, alpha: navigationAlpha) + let bottomNavigationAlphaDistance: CGFloat = 16.0 let bottomNavigationAlpha: CGFloat = max(0.0, min(1.0, (self.scrollView.contentSize.height - self.scrollView.bounds.maxY) / bottomNavigationAlphaDistance)) @@ -534,7 +541,15 @@ final class ChannelAppearanceScreenComponent: Component { } let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } - self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + let alertController = textAlertController( + context: component.context, + title: nil, + text: presentationData.strings.Login_UnknownError, + actions: [ + TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {}) + ] + ) + self.environment?.controller()?.present(alertController, in: .window(.root)) self.isApplyingSettings = false self.state?.updated(transition: .immediate) @@ -992,6 +1007,10 @@ final class ChannelAppearanceScreenComponent: Component { } self.requiredBoostSubject = requiredBoostSubject + let edgeEffectHeight: CGFloat = environment.navigationHeight + 8.0 + let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: edgeEffectHeight)) + transition.setFrame(view: self.edgeEffectView, frame: edgeEffectFrame) + self.edgeEffectView.update(content: environment.theme.list.blocksBackgroundColor, blur: true, rect: edgeEffectFrame, edge: .top, edgeSize: min(30, edgeEffectFrame.height), transition: transition) let headerColor: UIColor if let profileColor { @@ -1002,7 +1021,6 @@ final class ChannelAppearanceScreenComponent: Component { } self.topOverscrollLayer.backgroundColor = headerColor.cgColor - let backSize = self.backButton.update(key: .back, presentationData: component.context.sharedContext.currentPresentationData.with { $0 }, height: 44.0) var scrolledUp = self.scrolledUp if profileColor == nil { scrolledUp = false @@ -1011,14 +1029,6 @@ final class ChannelAppearanceScreenComponent: Component { if let controller = self.environment?.controller() as? ChannelAppearanceScreen { controller.statusBar.updateStatusBarStyle(scrolledUp ? .White : .Ignore, animated: true) } - - self.backButton.updateContentsColor(backgroundColor: scrolledUp ? UIColor(white: 0.0, alpha: 0.1) : .clear, contentsColor: scrolledUp ? .white : environment.theme.rootController.navigationBar.accentTextColor, canBeExpanded: !scrolledUp, transition: .animated(duration: 0.2, curve: .easeInOut)) - self.backButton.frame = CGRect(origin: CGPoint(x: environment.safeInsets.left + 16.0, y: environment.navigationHeight - 44.0), size: backSize) - if self.backButton.view.superview == nil { - if let controller = self.environment?.controller(), let navigationBar = controller.navigationBar { - navigationBar.view.addSubview(self.backButton.view) - } - } let navigationTitleSize = self.navigationTitle.update( transition: transition, @@ -1897,12 +1907,11 @@ public class ChannelAppearanceScreen: ViewControllerComponentContainer { context: context, peerId: peerId, boostStatus: boostStatus - ), navigationBarAppearance: .default, theme: .default, updatedPresentationData: updatedPresentationData) + ), navigationBarAppearance: .transparent, theme: .default, updatedPresentationData: updatedPresentationData) let presentationData = context.sharedContext.currentPresentationData.with { $0 } self.title = "" self.navigationItem.backBarButtonItem = UIBarButtonItem(title: presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) - self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: UIView()) self.ready.set(.never()) diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/EmojiPickerItem.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/EmojiPickerItem.swift index e4f962bb..ac2ea87e 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/EmojiPickerItem.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/EmojiPickerItem.swift @@ -99,7 +99,7 @@ final class EmojiPickerItemNode: ListViewItemNode { self.maskNode = ASImageNode() self.maskNode.isUserInteractionEnabled = false - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.clipsToBounds = true } diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/GiftListItemComponent.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/GiftListItemComponent.swift index 3db8c9a3..4f3676d0 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/GiftListItemComponent.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/GiftListItemComponent.swift @@ -218,7 +218,7 @@ final class GiftListItemComponent: Component { )) for gift in component.starGifts { - guard case let .generic(gift) = gift, let title = gift.title else { + guard case let .generic(gift) = gift, let title = gift.title, let resale = gift.availability?.resale, resale > 0 else { continue } tabSelectorItems.append(TabSelectorComponent.Item( diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift index f305e4f5..1c58240b 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift @@ -186,7 +186,7 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode { self.containerNode = ASDisplayNode() self.containerNode.subnodeTransform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.clipsToBounds = true self.isUserInteractionEnabled = false diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorProfilePreviewItem.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorProfilePreviewItem.swift index 47518758..dd7f7564 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorProfilePreviewItem.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorProfilePreviewItem.swift @@ -142,7 +142,7 @@ final class PeerNameColorProfilePreviewItemNode: ListViewItemNode { let avatarFont = avatarPlaceholderFont(size: floor(100.0 * 16.0 / 37.0)) self.avatarNode = AvatarNode(font: avatarFont) - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.clipsToBounds = true self.isUserInteractionEnabled = false diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/UserApperanceScreen.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/UserApperanceScreen.swift index f3918590..f1f1f24b 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/UserApperanceScreen.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/UserApperanceScreen.swift @@ -55,17 +55,17 @@ final class UserAppearanceScreenComponent: Component { } let context: AccountContext + let overNavigationContainer: UIView init( - context: AccountContext + context: AccountContext, + overNavigationContainer: UIView ) { self.context = context + self.overNavigationContainer = overNavigationContainer } static func ==(lhs: UserAppearanceScreenComponent, rhs: UserAppearanceScreenComponent) -> Bool { - if lhs.context !== rhs.context { - return false - } return true } @@ -166,8 +166,6 @@ final class UserAppearanceScreenComponent: Component { private let actionButton = ComponentView() private let edgeEffectView: EdgeEffectView - private let backButton = PeerInfoHeaderNavigationButton() - private let tabSelector = ComponentView() enum Section: Int32 { case profile @@ -263,12 +261,6 @@ final class UserAppearanceScreenComponent: Component { self.containerView.addSubview(self.previewShadowView) self.addSubview(self.edgeEffectView) - - self.backButton.action = { [weak self] _, _ in - if let self, let controller = self.environment?.controller() as? UserAppearanceScreen { - controller.backPressed() - } - } } required init?(coder: NSCoder) { @@ -298,21 +290,28 @@ final class UserAppearanceScreenComponent: Component { if !resolvedState.changes.isEmpty { let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } - self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.Channel_Appearance_UnsavedChangesAlertTitle, text: presentationData.strings.Channel_Appearance_UnsavedChangesAlertText, actions: [ - TextAlertAction(type: .genericAction, title: presentationData.strings.Channel_Appearance_UnsavedChangesAlertDiscard, action: { [weak self] in - guard let self else { - return - } - self.environment?.controller()?.dismiss() - }), - TextAlertAction(type: .defaultAction, title: presentationData.strings.Channel_Appearance_UnsavedChangesAlertApply, action: { [weak self] in - guard let self else { - return - } - self.applySettings() - }) - ]), in: .window(.root)) + let alertController = textAlertController( + context: component.context, + title: presentationData.strings.Channel_Appearance_UnsavedChangesAlertTitle, + text: presentationData.strings.Channel_Appearance_UnsavedChangesAlertText, + actions: [ + TextAlertAction(type: .genericAction, title: presentationData.strings.Channel_Appearance_UnsavedChangesAlertDiscard, action: { [weak self] in + guard let self else { + return + } + self.environment?.controller()?.dismiss() + }), + TextAlertAction(type: .defaultAction, title: presentationData.strings.Channel_Appearance_UnsavedChangesAlertApply, action: { [weak self] in + guard let self else { + return + } + self.applySettings() + }) + ] + ) + self.environment?.controller()?.present(alertController, in: .window(.root)) + return false } @@ -564,7 +563,7 @@ final class UserAppearanceScreenComponent: Component { self.isApplyingSettings = false self.applySettings() - Queue.mainQueue().after(0.5) { + Queue.mainQueue().after(2.5) { switch finalPrice.currency { case .stars: component.context.starsContext?.load(force: true) @@ -800,7 +799,16 @@ final class UserAppearanceScreenComponent: Component { } let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } - self.environment?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + + let alertController = textAlertController( + context: component.context, + title: nil, + text: presentationData.strings.Login_UnknownError, + actions: [ + TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {}) + ] + ) + self.environment?.controller()?.present(alertController, in: .window(.root)) self.isApplyingSettings = false self.state?.updated(transition: .immediate) @@ -963,6 +971,8 @@ final class UserAppearanceScreenComponent: Component { self.component = component self.state = state + transition.setFrame(view: component.overNavigationContainer, frame: CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: environment.navigationHeight))) + let theme = environment.theme var animateTabChange = false @@ -1072,16 +1082,6 @@ final class UserAppearanceScreenComponent: Component { .withUpdatedProfileBackgroundEmojiId(resolvedState.backgroundFileId) ) } - - let backSize = self.backButton.update(key: .back, presentationData: component.context.sharedContext.currentPresentationData.with { $0 }, height: 44.0) - - self.backButton.updateContentsColor(backgroundColor: .clear, contentsColor: environment.theme.rootController.navigationBar.accentTextColor, canBeExpanded: true, transition: .animated(duration: 0.2, curve: .easeInOut)) - self.backButton.frame = CGRect(origin: CGPoint(x: environment.safeInsets.left + 16.0, y: environment.navigationHeight - 44.0), size: backSize) - if self.backButton.view.superview == nil { - if let controller = self.environment?.controller(), let navigationBar = controller.navigationBar { - navigationBar.view.addSubview(self.backButton.view) - } - } var previewTransition = transition let transitionScale = (availableSize.height - 3.0) / availableSize.height @@ -1148,10 +1148,10 @@ final class UserAppearanceScreenComponent: Component { environment: {}, containerSize: CGSize(width: availableSize.width, height: 44.0) ) - let tabSelectorFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - tabSelectorSize.width) / 2.0), y: environment.statusBarHeight + floorToScreenPixels((environment.navigationHeight - environment.statusBarHeight - tabSelectorSize.height) / 2.0)), size: tabSelectorSize) + let tabSelectorFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - tabSelectorSize.width) / 2.0), y: environment.statusBarHeight + 2.0 + floorToScreenPixels((environment.navigationHeight - environment.statusBarHeight - tabSelectorSize.height) / 2.0)), size: tabSelectorSize) if let tabSelectorView = self.tabSelector.view { if tabSelectorView.superview == nil { - self.addSubview(tabSelectorView) + component.overNavigationContainer.addSubview(tabSelectorView) } transition.setFrame(view: tabSelectorView, frame: tabSelectorFrame) } @@ -1932,6 +1932,8 @@ final class UserAppearanceScreenComponent: Component { public class UserAppearanceScreen: ViewControllerComponentContainer { private let context: AccountContext + private let overNavigationContainer: UIView + private var didSetReady: Bool = false public init( @@ -1940,8 +1942,11 @@ public class UserAppearanceScreen: ViewControllerComponentContainer { ) { self.context = context + self.overNavigationContainer = SparseContainerView() + super.init(context: context, component: UserAppearanceScreenComponent( - context: context + context: context, + overNavigationContainer: self.overNavigationContainer ), navigationBarAppearance: .default, theme: .default, updatedPresentationData: updatedPresentationData) self.automaticallyControlPresentationContextLayout = false @@ -1951,7 +1956,6 @@ public class UserAppearanceScreen: ViewControllerComponentContainer { let presentationData = context.sharedContext.currentPresentationData.with { $0 } self.title = "" self.navigationItem.backBarButtonItem = UIBarButtonItem(title: presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) - self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: UIView()) self.ready.set(.never()) @@ -1969,6 +1973,10 @@ public class UserAppearanceScreen: ViewControllerComponentContainer { return componentView.attemptNavigation(complete: complete) } + + if let navigationBar = self.navigationBar { + navigationBar.customOverBackgroundContentView.insertSubview(self.overNavigationContainer, at: 0) + } } required public init(coder aDecoder: NSCoder) { diff --git a/submodules/TelegramUI/Components/Settings/PeerSelectionScreen/Sources/PeerSelectionScreen.swift b/submodules/TelegramUI/Components/Settings/PeerSelectionScreen/Sources/PeerSelectionScreen.swift index b99ea5e3..f41221d7 100644 --- a/submodules/TelegramUI/Components/Settings/PeerSelectionScreen/Sources/PeerSelectionScreen.swift +++ b/submodules/TelegramUI/Components/Settings/PeerSelectionScreen/Sources/PeerSelectionScreen.swift @@ -218,7 +218,7 @@ final class PeerSelectionScreenComponent: Component { private var channels: [PeerSelectionScreen.ChannelInfo]? private var channelsDisposable: Disposable? - private var isSearchDisplayControllerActive: Bool = false + private var isSearchDisplayControllerActive: ChatListNavigationBar.ActiveSearch? private var searchQuery: String = "" private let searchQueryComponentSeparationCharacterSet: CharacterSet @@ -298,8 +298,7 @@ final class PeerSelectionScreenComponent: Component { } ))) : nil, rightButtons: rightButtons, - backTitle: isModal ? nil : strings.Common_Back, - backPressed: { [weak self] in + backPressed: isModal ? nil : { [weak self] in guard let self else { return } @@ -318,14 +317,15 @@ final class PeerSelectionScreenComponent: Component { strings: strings, statusBarHeight: statusBarHeight, sideInset: insets.left, - isSearchActive: self.isSearchDisplayControllerActive, - isSearchEnabled: true, + search: ChatListNavigationBar.Search(isEnabled: true), + activeSearch: self.isSearchDisplayControllerActive, primaryContent: headerContent, secondaryContent: nil, secondaryTransition: 0.0, storySubscriptions: nil, storiesIncludeHidden: false, uploadProgress: [:], + headerPanels: nil, tabsNode: nil, tabsNodeIsSearch: false, accessoryPanelContainer: nil, @@ -335,7 +335,7 @@ final class PeerSelectionScreenComponent: Component { return } - self.isSearchDisplayControllerActive = true + self.isSearchDisplayControllerActive = ChatListNavigationBar.ActiveSearch(isExternal: false) self.state?.updated(transition: .spring(duration: 0.4)) }, openStatusSetup: { _ in @@ -385,7 +385,7 @@ final class PeerSelectionScreenComponent: Component { let resultingOffset = mainOffset var offset = resultingOffset - if self.isSearchDisplayControllerActive { + if self.isSearchDisplayControllerActive != nil { offset = 0.0 } @@ -468,7 +468,7 @@ final class PeerSelectionScreenComponent: Component { self.navigationHeight = navigationHeight var removedSearchBar: SearchBarNode? - if self.isSearchDisplayControllerActive { + if self.isSearchDisplayControllerActive != nil { let searchBarNode: SearchBarNode var searchBarTransition = transition if let current = self.searchBarNode { @@ -478,8 +478,9 @@ final class PeerSelectionScreenComponent: Component { let searchBarTheme = SearchBarNodeTheme(theme: environment.theme, hasSeparator: false) searchBarNode = SearchBarNode( theme: searchBarTheme, + presentationTheme: environment.theme, strings: environment.strings, - fieldStyle: .modern, + fieldStyle: .glass, displayBackground: false ) searchBarNode.placeholderString = NSAttributedString(string: environment.strings.Common_Search, font: Font.regular(17.0), textColor: searchBarTheme.placeholder) @@ -488,7 +489,7 @@ final class PeerSelectionScreenComponent: Component { guard let self else { return } - self.isSearchDisplayControllerActive = false + self.isSearchDisplayControllerActive = nil self.state?.updated(transition: .spring(duration: 0.4)) } searchBarNode.textUpdated = { [weak self] query, _ in diff --git a/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/ItemListReactionItem.swift b/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/ItemListReactionItem.swift index 523d0247..a155b1c6 100644 --- a/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/ItemListReactionItem.swift +++ b/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/ItemListReactionItem.swift @@ -151,7 +151,7 @@ public class ItemListReactionItemNode: ListViewItemNode, ItemListItemNode { self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.titleNode) self.view.addSubview(self.iconView) @@ -372,7 +372,7 @@ public class ItemListReactionItemNode: ListViewItemNode, ItemListItemNode { if let animationContent = animationContent { let iconBoundingSize = CGSize(width: 28.0, height: 28.0) - let iconOffsetX: CGFloat = 0.0 + let iconOffsetX: CGFloat = -6.0 let iconSize = strongSelf.iconView.update( transition: .immediate, component: AnyComponent(EmojiStatusComponent( diff --git a/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/ReactionChatPreviewItem.swift b/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/ReactionChatPreviewItem.swift index e7257534..f8ebea85 100644 --- a/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/ReactionChatPreviewItem.swift +++ b/submodules/TelegramUI/Components/Settings/QuickReactionSetupController/Sources/ReactionChatPreviewItem.swift @@ -116,7 +116,7 @@ class ReactionChatPreviewItemNode: ListViewItemNode { self.containerNode = ASDisplayNode() self.containerNode.subnodeTransform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.clippingNode) self.clippingNode.addSubnode(self.containerNode) diff --git a/submodules/TelegramUI/Components/Settings/QuickReplyNameAlertController/BUILD b/submodules/TelegramUI/Components/Settings/QuickReplyNameAlertController/BUILD index a9f96d66..99a8b1bb 100644 --- a/submodules/TelegramUI/Components/Settings/QuickReplyNameAlertController/BUILD +++ b/submodules/TelegramUI/Components/Settings/QuickReplyNameAlertController/BUILD @@ -21,6 +21,8 @@ swift_library( "//submodules/Components/MultilineTextComponent", "//submodules/Components/BalancedTextComponent", "//submodules/TelegramUI/Components/EmojiStatusComponent", + "//submodules/TelegramUI/Components/AlertComponent", + "//submodules/TelegramUI/Components/AlertComponent/AlertInputFieldComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Settings/QuickReplyNameAlertController/Sources/QuickReplyNameAlertController.swift b/submodules/TelegramUI/Components/Settings/QuickReplyNameAlertController/Sources/QuickReplyNameAlertController.swift index 9629f3f3..7966d579 100644 --- a/submodules/TelegramUI/Components/Settings/QuickReplyNameAlertController/Sources/QuickReplyNameAlertController.swift +++ b/submodules/TelegramUI/Components/Settings/QuickReplyNameAlertController/Sources/QuickReplyNameAlertController.swift @@ -11,6 +11,8 @@ import ComponentFlow import MultilineTextComponent import BalancedTextComponent import EmojiStatusComponent +import AlertComponent +import AlertInputFieldComponent private final class PromptInputFieldNode: ASDisplayNode, ASEditableTextNodeDelegate { private var theme: PresentationTheme @@ -500,6 +502,100 @@ public enum PromptControllerTitleFont { } public func quickReplyNameAlertController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, text: String, subtext: String, titleFont: PromptControllerTitleFont = .regular, value: String?, characterLimit: Int = 1000, apply: @escaping (String?) -> Void) -> AlertController { +// let presentationData = context.sharedContext.currentPresentationData.with { $0 } +// let strings = presentationData.strings +// +// let inputState = AlertInputFieldComponent.ExternalState() +// +// let doneIsEnabled: Signal = inputState.valueSignal +// |> map { value in +// return !value.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty +// } +// +// let doneInProgressValuePromise = ValuePromise(false) +// let doneInProgress = doneInProgressValuePromise.get() +// +// var content: [AnyComponentWithIdentity] = [] +// content.append(AnyComponentWithIdentity( +// id: "title", +// component: AnyComponent( +// AlertTitleComponent(title: strings.WebBrowser_Exceptions_Create_Title) +// ) +// )) +// content.append(AnyComponentWithIdentity( +// id: "text", +// component: AnyComponent( +// AlertTextComponent(content: .plain(strings.WebBrowser_Exceptions_Create_Text)) +// ) +// )) +// +// let domainRegex = try? NSRegularExpression(pattern: "^(https?://)?([a-zA-Z0-9-]+\\.?)*([a-zA-Z]*)?(:)?(/)?$", options: []) +// let pathRegex = try? NSRegularExpression(pattern: "^(https?://)?([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}/", options: []) +// var applyImpl: (() -> Void)? +// content.append(AnyComponentWithIdentity( +// id: "input", +// component: AnyComponent( +// AlertInputFieldComponent( +// context: context, +// initialValue: nil, +// placeholder: strings.QuickReply_ShortcutPlaceholder, +// characterLimit: characterLimit, +// hasClearButton: false, +// keyboardType: .URL, +// autocapitalizationType: .none, +// autocorrectionType: .no, +// isInitiallyFocused: true, +// externalState: inputState, +// shouldChangeText: { updatedText in +// guard let domainRegex, let pathRegex else { +// return true +// } +// let domainMatches = domainRegex.matches(in: updatedText, options: [], range: NSRange(location: 0, length: updatedText.utf16.count)) +// let pathMatches = pathRegex.matches(in: updatedText, options: [], range: NSRange(location: 0, length: updatedText.utf16.count)) +// if domainMatches.count > 0, pathMatches.count == 0 { +// return true +// } else { +// return false +// } +// }, +// returnKeyAction: { +// applyImpl?() +// } +// ) +// ) +// )) +// +// var effectiveUpdatedPresentationData: (PresentationData, Signal) +// if let updatedPresentationData { +// effectiveUpdatedPresentationData = updatedPresentationData +// } else { +// effectiveUpdatedPresentationData = (presentationData, context.sharedContext.presentationData) +// } +// +// let alertController = AlertScreen( +// configuration: AlertScreen.Configuration(allowInputInset: true), +// content: content, +// actions: [ +// .init(title: strings.Common_Cancel, action: { +// apply(nil) +// }), +// .init(title: strings.Common_Done, type: .default, action: { +// applyImpl?() +// }, autoDismiss: false, isEnabled: doneIsEnabled, progress: doneInProgress) +// ], +// updatedPresentationData: effectiveUpdatedPresentationData +// ) +// applyImpl = { +// let updatedLink = explicitUrl(inputState.value) +// if !updatedLink.isEmpty && isValidUrl(updatedLink, validSchemes: ["http": true, "https": true]) { +// doneInProgressValuePromise.set(true) +// apply(updatedLink) +// } else { +// inputState.animateError() +// } +// } +// return alertController + let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } var dismissImpl: ((Bool) -> Void)? diff --git a/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorController.swift b/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorController.swift index f4c58a78..762ea62d 100644 --- a/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorController.swift +++ b/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorController.swift @@ -374,7 +374,7 @@ public final class ThemeAccentColorController: ViewController { }, ready: self._ready) self.controllerNode.themeUpdated = { [weak self] theme in if let strongSelf = self { - strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationTheme: theme, presentationStrings: strongSelf.presentationData.strings)) + strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationTheme: theme, presentationStrings: strongSelf.presentationData.strings), transition: .immediate) strongSelf.segmentedTitleView.theme = theme } } diff --git a/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift b/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift index 9f766877..b4b90573 100644 --- a/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift +++ b/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift @@ -871,7 +871,6 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, ASScrollViewDelegate }, openChatFolderUpdates: {}, hideChatFolderUpdates: { }, openStories: { _, _ in }, openStarsTopup: { _ in - }, dismissNotice: { _ in }, editPeer: { _ in }, openWebApp: { _ in }, openPhotoSetup: { diff --git a/submodules/TelegramUI/Components/Settings/ThemeCarouselItem/Sources/ThemeCarouselItem.swift b/submodules/TelegramUI/Components/Settings/ThemeCarouselItem/Sources/ThemeCarouselItem.swift index a34c56bc..7f8a5804 100644 --- a/submodules/TelegramUI/Components/Settings/ThemeCarouselItem/Sources/ThemeCarouselItem.swift +++ b/submodules/TelegramUI/Components/Settings/ThemeCarouselItem/Sources/ThemeCarouselItem.swift @@ -259,7 +259,7 @@ private final class ThemeCarouselThemeItemIconNode: ListViewItemNode { self.activateAreaNode = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.containerNode) self.containerNode.addSubnode(self.imageNode) @@ -725,7 +725,7 @@ public class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode { self.listNode = ListView() self.listNode.transform = CATransform3DMakeRotation(-CGFloat.pi / 2.0, 0.0, 0.0, 1.0) - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.containerNode) self.addSubnode(self.listNode) diff --git a/submodules/TelegramUI/Components/Settings/ThemeSettingsThemeItem/Sources/ThemeSettingsThemeItem.swift b/submodules/TelegramUI/Components/Settings/ThemeSettingsThemeItem/Sources/ThemeSettingsThemeItem.swift index 7fd01443..4108dd42 100644 --- a/submodules/TelegramUI/Components/Settings/ThemeSettingsThemeItem/Sources/ThemeSettingsThemeItem.swift +++ b/submodules/TelegramUI/Components/Settings/ThemeSettingsThemeItem/Sources/ThemeSettingsThemeItem.swift @@ -229,7 +229,7 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode { self.activateAreaNode = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.containerNode) self.containerNode.addSubnode(self.imageNode) @@ -549,7 +549,7 @@ public class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode { self.listNode = ListView() self.listNode.transform = CATransform3DMakeRotation(-CGFloat.pi / 2.0, 0.0, 0.0, 1.0) - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.containerNode) self.addSubnode(self.listNode) diff --git a/submodules/TelegramUI/Components/Settings/TimezoneSelectionScreen/Sources/TimezoneSelectionScreen.swift b/submodules/TelegramUI/Components/Settings/TimezoneSelectionScreen/Sources/TimezoneSelectionScreen.swift index d83ae3e0..1b8e8a4e 100644 --- a/submodules/TelegramUI/Components/Settings/TimezoneSelectionScreen/Sources/TimezoneSelectionScreen.swift +++ b/submodules/TelegramUI/Components/Settings/TimezoneSelectionScreen/Sources/TimezoneSelectionScreen.swift @@ -35,7 +35,9 @@ public class TimezoneSelectionScreen: ViewController { self.presentationData = context.sharedContext.currentPresentationData.with { $0 } - super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData)) + super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData, style: .glass)) + + self._hasGlassStyle = true self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style @@ -82,7 +84,7 @@ public class TimezoneSelectionScreen: ViewController { private func updateThemeAndStrings() { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData, style: .glass), transition: .immediate) self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search) self.title = self.presentationData.strings.Settings_AppLanguage self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) diff --git a/submodules/TelegramUI/Components/Settings/TimezoneSelectionScreen/Sources/TimezoneSelectionScreenNode.swift b/submodules/TelegramUI/Components/Settings/TimezoneSelectionScreen/Sources/TimezoneSelectionScreenNode.swift index e20cc58d..7db20975 100644 --- a/submodules/TelegramUI/Components/Settings/TimezoneSelectionScreen/Sources/TimezoneSelectionScreenNode.swift +++ b/submodules/TelegramUI/Components/Settings/TimezoneSelectionScreen/Sources/TimezoneSelectionScreenNode.swift @@ -109,7 +109,7 @@ private final class TimezoneListSearchContainerNode: SearchDisplayControllerCont self.presentationDataPromise = Promise(self.presentationData) self.dimNode = ASDisplayNode() - self.dimNode.backgroundColor = UIColor.black.withAlphaComponent(0.5) + self.dimNode.backgroundColor = .clear self.listNode = ListView() self.listNode.accessibilityPageScrolledString = { row, count in @@ -523,7 +523,7 @@ final class TimezoneSelectionScreenNode: ViewControllerTracingNode { self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: TimezoneListSearchContainerNode(context: self.context, timeZoneList: timeZoneList, action: self.action), inline: true, cancel: { [weak self] in self?.requestDeactivateSearch() - }) + }, fieldStyle: placeholderNode.fieldStyle) self.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: navigationBarHeight, transition: .immediate) self.searchDisplayController?.activate(insertSubnode: { [weak self, weak placeholderNode] subnode, isSearchBar in diff --git a/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryController.swift b/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryController.swift index 0d63c6d7..64cae777 100644 --- a/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryController.swift +++ b/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryController.swift @@ -396,7 +396,7 @@ public class WallpaperGalleryController: ViewController { self.title = self.presentationData.strings.WallpaperPreview_Title } self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData), transition: .immediate) self.toolbarNode?.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings) self.patternPanelNode?.updateTheme(self.presentationData.theme) diff --git a/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperPatternPanelNode.swift b/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperPatternPanelNode.swift index c767e899..4e6e46b1 100644 --- a/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperPatternPanelNode.swift +++ b/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperPatternPanelNode.swift @@ -131,7 +131,7 @@ private final class WallpaperPatternItemNode : ListViewItemNode { init() { self.wallpaperNode = SettingsThemeWallpaperNode(displayLoading: true) - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.wallpaperNode) } diff --git a/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeColorsGridController.swift b/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeColorsGridController.swift index 77ed39bb..c59e3649 100644 --- a/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeColorsGridController.swift +++ b/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeColorsGridController.swift @@ -158,7 +158,7 @@ public final class ThemeColorsGridController: ViewController, AttachmentContaina self.mode = mode self.presentationData = context.sharedContext.currentPresentationData.with { $0 } - super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData)) + super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData, style: .glass)) self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style @@ -216,7 +216,7 @@ public final class ThemeColorsGridController: ViewController, AttachmentContaina private func updateThemeAndStrings() { self.title = self.presentationData.strings.WallpaperColors_Title self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData, style: .glass), transition: .immediate) if self.isNodeLoaded { self.controllerNode.updatePresentationData(self.presentationData) diff --git a/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeGridController.swift b/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeGridController.swift index bd601204..af7ea2f9 100644 --- a/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeGridController.swift +++ b/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeGridController.swift @@ -45,8 +45,6 @@ public final class ThemeGridController: ViewController { private let presentationDataPromise = Promise() private var presentationDataDisposable: Disposable? - private var searchContentNode: NavigationBarSearchContentNode? - private var isEmpty: Bool? private var editingMode: Bool = false @@ -67,7 +65,7 @@ public final class ThemeGridController: ViewController { self.presentationData = context.sharedContext.currentPresentationData.with { $0 } self.presentationDataPromise.set(.single(self.presentationData)) - super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData)) + super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData, style: .glass)) switch mode { case .generic: @@ -81,9 +79,6 @@ public final class ThemeGridController: ViewController { self.scrollToTop = { [weak self] in if let strongSelf = self { - if let searchContentNode = strongSelf.searchContentNode { - searchContentNode.updateExpansionProgress(1.0, animated: true) - } strongSelf.controllerNode.scrollToTop() } } @@ -128,8 +123,7 @@ public final class ThemeGridController: ViewController { } self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) - self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Wallpaper_Search) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData, style: .glass), transition: .immediate) if self.isNodeLoaded { self.controllerNode.updatePresentationData(self.presentationData) @@ -182,7 +176,6 @@ public final class ThemeGridController: ViewController { } else { uploadCustomWallpaper(context: strongSelf.context, wallpaper: wallpaper, mode: options, editedImage: editedImage, cropRect: cropRect, brightness: brightness, completion: { [weak self, weak controller] in if let strongSelf = self { - strongSelf.deactivateSearch(animated: false) strongSelf.controllerNode.scrollToTop(animated: false) } if let controller = controller { @@ -392,9 +385,6 @@ public final class ThemeGridController: ViewController { } }) self.controllerNode.navigationBar = self.navigationBar - self.controllerNode.requestDeactivateSearch = { [weak self] in - self?.deactivateSearch(animated: true) - } self.controllerNode.requestWallpaperRemoval = { [weak self] in if let self { self.completion(.remove) @@ -403,10 +393,6 @@ public final class ThemeGridController: ViewController { } self.controllerNode.gridNode.visibleContentOffsetChanged = { [weak self] offset in if let strongSelf = self { - if let searchContentNode = strongSelf.searchContentNode { - searchContentNode.updateGridVisibleContentOffset(offset) - } - var previousContentOffsetValue: CGFloat? if let previousContentOffset = strongSelf.previousContentOffset, case let .known(value) = previousContentOffset { previousContentOffsetValue = value @@ -427,12 +413,6 @@ public final class ThemeGridController: ViewController { strongSelf.previousContentOffset = offset } } - - self.controllerNode.gridNode.scrollingCompleted = { [weak self] in - if let strongSelf = self, let searchContentNode = strongSelf.searchContentNode { - let _ = strongSelf.controllerNode.fixNavigationSearchableGridNodeScrolling(searchNode: searchContentNode) - } - } self._ready.set(self.controllerNode.ready.get()) @@ -492,38 +472,9 @@ public final class ThemeGridController: ViewController { self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.cleanNavigationHeight, transition: transition) } - func activateSearch() { - if self.displayNavigationBar { - let _ = (self.controllerNode.ready.get() - |> take(1) - |> deliverOnMainQueue).start(completed: { [weak self] in - guard let strongSelf = self else { - return - } - if let scrollToTop = strongSelf.scrollToTop { - scrollToTop() - } - if let searchContentNode = strongSelf.searchContentNode { - strongSelf.controllerNode.activateSearch(placeholderNode: searchContentNode.placeholderNode) - } - strongSelf.setDisplayNavigationBar(false, transition: .animated(duration: 0.5, curve: .spring)) - }) - } - } - - func deactivateSearch(animated: Bool) { - if !self.displayNavigationBar { - self.setDisplayNavigationBar(true, transition: animated ? .animated(duration: 0.5, curve: .spring) : .immediate) - if let searchContentNode = self.searchContentNode { - self.controllerNode.deactivateSearch(placeholderNode: searchContentNode.placeholderNode, animated: animated) - } - } - } - @objc func editPressed() { self.editingMode = true self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.donePressed)) - self.searchContentNode?.setIsEnabled(false, animated: true) self.controllerNode.updateState { state in var state = state state.editing = true @@ -534,7 +485,6 @@ public final class ThemeGridController: ViewController { @objc func donePressed() { self.editingMode = false self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed)) - self.searchContentNode?.setIsEnabled(true, animated: true) self.controllerNode.updateState { state in var state = state state.editing = false diff --git a/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeGridControllerNode.swift b/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeGridControllerNode.swift index 039b41bb..5d59dd66 100644 --- a/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeGridControllerNode.swift +++ b/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeGridControllerNode.swift @@ -162,7 +162,6 @@ final class ThemeGridControllerNode: ASDisplayNode { private let emptyStateUpdated: (Bool) -> Void private let resetWallpapers: () -> Void - var requestDeactivateSearch: (() -> Void)? var requestWallpaperRemoval: (() -> Void)? let ready = ValuePromise() @@ -958,75 +957,6 @@ final class ThemeGridControllerNode: ASDisplayNode { } } - func activateSearch(placeholderNode: SearchBarPlaceholderNode) { - guard let (containerLayout, navigationBarHeight) = self.validLayout, let navigationBar = self.navigationBar, self.searchDisplayController == nil else { - return - } - - self.searchDisplayController = SearchDisplayController(presentationData: self.presentationData, contentNode: ThemeGridSearchContentNode(context: context, openResult: { [weak self] result in - if let strongSelf = self { - strongSelf.presentPreviewController(.contextResult(result)) - } - }), cancel: { [weak self] in - self?.requestDeactivateSearch?() - }) - - self.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: navigationBarHeight, transition: .immediate) - self.searchDisplayController?.activate(insertSubnode: { [weak self, weak placeholderNode] subnode, isSearchBar in - if let strongSelf = self, let strongPlaceholderNode = placeholderNode { - if isSearchBar { - strongPlaceholderNode.supernode?.insertSubnode(subnode, aboveSubnode: strongPlaceholderNode) - } else { - strongSelf.insertSubnode(subnode, belowSubnode: navigationBar) - } - } - }, placeholder: placeholderNode) - } - - func deactivateSearch(placeholderNode: SearchBarPlaceholderNode, animated: Bool) { - if let searchDisplayController = self.searchDisplayController { - searchDisplayController.deactivate(placeholder: placeholderNode, animated: animated) - self.searchDisplayController = nil - } - } - - func fixNavigationSearchableGridNodeScrolling(searchNode: NavigationBarSearchContentNode) -> Bool { - if searchNode.expansionProgress > 0.0 && searchNode.expansionProgress < 1.0 { - let scrollToItem: GridNodeScrollToItem - let targetProgress: CGFloat - - let duration: Double = 0.3 - let curve = ContainedViewLayoutTransitionCurve.slide - let transition: ContainedViewLayoutTransition = .animated(duration: duration, curve: curve) - let timingFunction = curve.timingFunction - let mediaTimingFunction = curve.mediaTimingFunction - - if searchNode.expansionProgress < 0.6 { - scrollToItem = GridNodeScrollToItem(index: 0, position: .top(navigationBarSearchContentHeight), transition: transition, directionHint: .up, adjustForSection: true, adjustForTopInset: true) - targetProgress = 0.0 - } else { - scrollToItem = GridNodeScrollToItem(index: 0, position: .top(0.0), transition: transition, directionHint: .up, adjustForSection: true, adjustForTopInset: true) - targetProgress = 1.0 - } - - let previousOffset = (self.gridNode.scrollView.contentOffset.y + self.gridNode.scrollView.contentInset.top) - searchNode.updateExpansionProgress(targetProgress, animated: true) - - self.gridNode.transaction(GridNodeTransaction(deleteItems: [], insertItems: [], updateItems: [], scrollToItem: scrollToItem, updateLayout: nil, itemTransition: .immediate, stationaryItems: .none, updateFirstIndexInSectionOffset: nil, updateOpaqueState: nil, synchronousLoads: false), completion: { _ in }) - - let offset = (self.gridNode.scrollView.contentOffset.y + self.gridNode.scrollView.contentInset.top) - previousOffset - - self.backgroundNode.layer.animatePosition(from: self.backgroundNode.layer.position.offsetBy(dx: 0.0, dy: offset), to: self.backgroundNode.layer.position, duration: duration, timingFunction: timingFunction, mediaTimingFunction: mediaTimingFunction) - self.separatorNode.layer.animatePosition(from: self.separatorNode.layer.position.offsetBy(dx: 0.0, dy: offset), to: self.separatorNode.layer.position, duration: duration, timingFunction: timingFunction, mediaTimingFunction: mediaTimingFunction) - self.colorItemNode.layer.animatePosition(from: self.colorItemNode.layer.position.offsetBy(dx: 0.0, dy: offset), to: self.colorItemNode.layer.position, duration: duration, timingFunction: timingFunction, mediaTimingFunction: mediaTimingFunction) - self.galleryItemNode.layer.animatePosition(from: self.galleryItemNode.layer.position.offsetBy(dx: 0.0, dy: offset), to: self.galleryItemNode.layer.position, duration: duration, timingFunction: timingFunction, mediaTimingFunction: mediaTimingFunction) - self.descriptionItemNode.layer.animatePosition(from: self.descriptionItemNode.layer.position.offsetBy(dx: 0.0, dy: offset), to: self.descriptionItemNode.layer.position, duration: duration, timingFunction: timingFunction, mediaTimingFunction: mediaTimingFunction) - - return true - } - return false - } - func scrollToTop(animated: Bool = true) { if let searchDisplayController = self.searchDisplayController { searchDisplayController.contentNode.scrollToTop() diff --git a/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeGridSearchColorsItem.swift b/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeGridSearchColorsItem.swift index 2056b25c..842c9003 100644 --- a/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeGridSearchColorsItem.swift +++ b/submodules/TelegramUI/Components/Settings/WallpaperGridScreen/Sources/ThemeGridSearchColorsItem.swift @@ -180,7 +180,7 @@ class ThemeGridSearchColorsItemNode: ListViewItemNode { self.separatorNode = ASDisplayNode() self.separatorNode.isLayerBacked = true - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.backgroundNode) self.addSubnode(self.separatorNode) diff --git a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/LiveStreamSettingsScreen.swift b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/LiveStreamSettingsScreen.swift index 3d36ecf2..d1ccccce 100644 --- a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/LiveStreamSettingsScreen.swift +++ b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/LiveStreamSettingsScreen.swift @@ -883,13 +883,13 @@ final class LiveStreamSettingsScreenComponent: Component { transition: transition, component: AnyComponent(GlassBarButtonComponent( size: barButtonSize, - backgroundColor: environment.theme.rootController.navigationBar.glassBarButtonBackgroundColor, + backgroundColor: nil, isDark: environment.theme.overallDarkAppearance, - state: .generic, + state: .glass, component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: environment.theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: environment.theme.chat.inputPanel.panelControlColor ) )), action: { [weak self] _ in diff --git a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift index 8bedc66e..44e173df 100644 --- a/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift +++ b/submodules/TelegramUI/Components/ShareWithPeersScreen/Sources/ShareWithPeersScreen.swift @@ -992,7 +992,7 @@ final class ShareWithPeersScreenComponent: Component { let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: environment.theme) let promptController = promptController( - sharedContext: component.context.sharedContext, + context: component.context, updatedPresentationData: (initial: presentationData, signal: .single(presentationData)), text: presentationData.strings.Stories_CreateAlbum_Title, titleFont: .bold, @@ -2772,7 +2772,7 @@ final class ShareWithPeersScreenComponent: Component { component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: environment.theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: environment.theme.chat.inputPanel.panelControlColor ) )), action: { [weak self] _ in diff --git a/submodules/TelegramUI/Components/Stars/BalanceNeededScreen/BUILD b/submodules/TelegramUI/Components/Stars/BalanceNeededScreen/BUILD index 1f4cce9b..d348f0bd 100644 --- a/submodules/TelegramUI/Components/Stars/BalanceNeededScreen/BUILD +++ b/submodules/TelegramUI/Components/Stars/BalanceNeededScreen/BUILD @@ -23,6 +23,8 @@ swift_library( "//submodules/TelegramUI/Components/ButtonComponent", "//submodules/TelegramUI/Components/LottieComponent", "//submodules/TelegramCore", + "//submodules/TelegramUI/Components/GlassBarButtonComponent", + ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Stars/BalanceNeededScreen/Sources/BalanceNeededScreen.swift b/submodules/TelegramUI/Components/Stars/BalanceNeededScreen/Sources/BalanceNeededScreen.swift index d712e9ea..56e8473c 100644 --- a/submodules/TelegramUI/Components/Stars/BalanceNeededScreen/Sources/BalanceNeededScreen.swift +++ b/submodules/TelegramUI/Components/Stars/BalanceNeededScreen/Sources/BalanceNeededScreen.swift @@ -14,6 +14,7 @@ import TelegramStringFormatting import BundleIconComponent import TelegramCore import TelegramPresentationData +import GlassBarButtonComponent private final class BalanceNeededSheetContentComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment @@ -49,9 +50,7 @@ private final class BalanceNeededSheetContentComponent: Component { private var component: BalanceNeededSheetContentComponent? private weak var state: EmptyComponentState? - - private var cachedCloseImage: (UIImage, PresentationTheme)? - + override init(frame: CGRect) { super.init(frame: frame) } @@ -71,28 +70,27 @@ private final class BalanceNeededSheetContentComponent: Component { let sideInset: CGFloat = 16.0 - let closeImage: UIImage - if let (image, theme) = self.cachedCloseImage, theme === environment.theme { - closeImage = image - } else { - closeImage = generateCloseButtonImage(backgroundColor: UIColor(rgb: 0x808084, alpha: 0.1), foregroundColor: environment.theme.actionSheet.inputClearButtonColor)! - self.cachedCloseImage = (closeImage, environment.theme) - } let closeButtonSize = self.closeButton.update( transition: .immediate, - component: AnyComponent(Button( - content: AnyComponent(Image(image: closeImage)), - action: { [weak self] in - guard let self, let component = self.component else { - return - } + component: AnyComponent(GlassBarButtonComponent( + size: CGSize(width: 40.0, height: 40.0), + backgroundColor: environment.theme.rootController.navigationBar.glassBarButtonBackgroundColor, + isDark: environment.theme.overallDarkAppearance, + state: .generic, + component: AnyComponentWithIdentity(id: "close", component: AnyComponent( + BundleIconComponent( + name: "Navigation/Close", + tintColor: environment.theme.chat.inputPanel.panelControlColor + ) + )), + action: { _ in component.dismiss() } )), environment: {}, - containerSize: CGSize(width: 30.0, height: 30.0) + containerSize: CGSize(width: 40.0, height: 40.0) ) - let closeButtonFrame = CGRect(origin: CGPoint(x: availableSize.width - closeButtonSize.width - 16.0, y: 12.0), size: closeButtonSize) + let closeButtonFrame = CGRect(origin: CGPoint(x: 16.0, y: 16.0), size: closeButtonSize) if let closeButtonView = self.closeButton.view { if closeButtonView.superview == nil { self.addSubview(closeButtonView) @@ -164,10 +162,12 @@ private final class BalanceNeededSheetContentComponent: Component { contentHeight += textSize.height contentHeight += 24.0 + let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: environment.safeInsets.bottom, innerDiameter: 52.0, sideInset: 30.0) let buttonSize = self.button.update( transition: transition, component: AnyComponent(ButtonComponent( background: ButtonComponent.Background( + style: .glass, color: environment.theme.list.itemCheckColors.fillColor, foreground: environment.theme.list.itemCheckColors.foregroundColor, pressedColor: environment.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.8) @@ -187,9 +187,9 @@ private final class BalanceNeededSheetContentComponent: Component { } )), environment: {}, - containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 50.0) + containerSize: CGSize(width: availableSize.width - buttonInsets.left - buttonInsets.right, height: 52.0) ) - let buttonFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: buttonSize) + let buttonFrame = CGRect(origin: CGPoint(x: buttonInsets.left, y: contentHeight), size: buttonSize) if let buttonView = self.button.view { if buttonView.superview == nil { self.addSubview(buttonView) @@ -197,13 +197,8 @@ private final class BalanceNeededSheetContentComponent: Component { transition.setFrame(view: buttonView, frame: buttonFrame) } contentHeight += buttonSize.height - - if environment.safeInsets.bottom.isZero { - contentHeight += 16.0 - } else { - contentHeight += environment.safeInsets.bottom + 8.0 - } - + contentHeight += buttonInsets.bottom + return CGSize(width: availableSize.width, height: contentHeight) } } @@ -307,6 +302,7 @@ private final class BalanceNeededScreenComponent: Component { }) } )), + style: .glass, backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor), animateOut: self.sheetAnimateOut )), diff --git a/submodules/TelegramUI/Components/Stars/StarsPurchaseScreen/Sources/StarsPurchaseScreen.swift b/submodules/TelegramUI/Components/Stars/StarsPurchaseScreen/Sources/StarsPurchaseScreen.swift index b0a69117..a733e61b 100644 --- a/submodules/TelegramUI/Components/Stars/StarsPurchaseScreen/Sources/StarsPurchaseScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsPurchaseScreen/Sources/StarsPurchaseScreen.swift @@ -504,6 +504,7 @@ private final class StarsPurchaseScreenComponent: CombinedComponent { typealias EnvironmentType = ViewControllerComponentContainer.Environment let context: AccountContext + let overNavigationContainer: UIView let starsContext: StarsContext let options: [Any] let purpose: StarsPurchasePurpose @@ -515,6 +516,7 @@ private final class StarsPurchaseScreenComponent: CombinedComponent { init( context: AccountContext, + overNavigationContainer: UIView, starsContext: StarsContext, options: [Any], purpose: StarsPurchasePurpose, @@ -525,6 +527,7 @@ private final class StarsPurchaseScreenComponent: CombinedComponent { completion: @escaping (Int64) -> Void ) { self.context = context + self.overNavigationContainer = overNavigationContainer self.starsContext = starsContext self.options = options self.purpose = purpose @@ -759,8 +762,6 @@ private final class StarsPurchaseScreenComponent: CombinedComponent { let scrollContent = Child(ScrollComponent.self) let star = Child(PremiumStarComponent.self) let avatar = Child(GiftAvatarComponent.self) - let topPanel = Child(BlurredBackgroundComponent.self) - let topSeparator = Child(Rectangle.self) let title = Child(MultilineTextComponent.self) let balanceTitle = Child(MultilineTextComponent.self) let balanceValue = Child(MultilineTextComponent.self) @@ -816,29 +817,13 @@ private final class StarsPurchaseScreenComponent: CombinedComponent { UIColor(rgb: 0xfdd219) ], particleColor: UIColor(rgb: 0xf9b004), - backgroundColor: environment.theme.list.blocksBackgroundColor + backgroundColor: nil ), availableSize: CGSize(width: min(414.0, context.availableSize.width), height: 220.0), transition: context.transition ) } - let topPanel = topPanel.update( - component: BlurredBackgroundComponent( - color: environment.theme.rootController.navigationBar.blurredBackgroundColor - ), - availableSize: CGSize(width: context.availableSize.width, height: environment.navigationHeight), - transition: context.transition - ) - - let topSeparator = topSeparator.update( - component: Rectangle( - color: environment.theme.rootController.navigationBar.separatorColor - ), - availableSize: CGSize(width: context.availableSize.width, height: UIScreenPixel), - transition: context.transition - ) - let titleText: String switch context.component.purpose { case .generic: @@ -948,14 +933,12 @@ private final class StarsPurchaseScreenComponent: CombinedComponent { .position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0)) ) - let topPanelAlpha: CGFloat let titleOffset: CGFloat let titleScale: CGFloat let titleOffsetDelta = (topInset + 160.0) - (environment.statusBarHeight + (environment.navigationHeight - environment.statusBarHeight) / 2.0) let titleAlpha: CGFloat if let topContentOffset = state.topContentOffset { - topPanelAlpha = min(20.0, max(0.0, topContentOffset - 95.0)) / 20.0 let topContentOffset = topContentOffset + max(0.0, min(1.0, topContentOffset / titleOffsetDelta)) * 10.0 titleOffset = topContentOffset let fraction = max(0.0, min(1.0, titleOffset / titleOffsetDelta)) @@ -963,42 +946,37 @@ private final class StarsPurchaseScreenComponent: CombinedComponent { titleAlpha = 1.0 } else { - topPanelAlpha = 0.0 titleScale = 1.0 titleOffset = 0.0 titleAlpha = 1.0 } - context.add(header + context.addWithExternalContainer(header .position(CGPoint(x: context.availableSize.width / 2.0, y: topInset + header.size.height / 2.0 - 30.0 - titleOffset * titleScale)) - .scale(titleScale) + .scale(titleScale), + container: context.component.overNavigationContainer ) - context.add(topPanel - .position(CGPoint(x: context.availableSize.width / 2.0, y: topPanel.size.height / 2.0)) - .opacity(topPanelAlpha) - ) - context.add(topSeparator - .position(CGPoint(x: context.availableSize.width / 2.0, y: topPanel.size.height)) - .opacity(topPanelAlpha) - ) - - context.add(title + context.addWithExternalContainer(title .position(CGPoint(x: context.availableSize.width / 2.0, y: max(topInset + 160.0 - titleOffset, environment.statusBarHeight + (environment.navigationHeight - environment.statusBarHeight) / 2.0))) .scale(titleScale) - .opacity(titleAlpha) + .opacity(titleAlpha), + container: context.component.overNavigationContainer ) let navigationHeight = environment.navigationHeight - environment.statusBarHeight let topBalanceOriginY = environment.statusBarHeight + (navigationHeight - balanceTitle.size.height - balanceValue.size.height) / 2.0 - context.add(balanceTitle - .position(CGPoint(x: context.availableSize.width - 16.0 - environment.safeInsets.right - balanceTitle.size.width / 2.0, y: topBalanceOriginY + balanceTitle.size.height / 2.0)) + context.addWithExternalContainer(balanceTitle + .position(CGPoint(x: context.availableSize.width - 16.0 - environment.safeInsets.right - balanceTitle.size.width / 2.0, y: topBalanceOriginY + balanceTitle.size.height / 2.0)), + container: context.component.overNavigationContainer ) - context.add(balanceValue - .position(CGPoint(x: context.availableSize.width - 16.0 - environment.safeInsets.right - balanceValue.size.width / 2.0, y: topBalanceOriginY + balanceTitle.size.height + balanceValue.size.height / 2.0)) + context.addWithExternalContainer(balanceValue + .position(CGPoint(x: context.availableSize.width - 16.0 - environment.safeInsets.right - balanceValue.size.width / 2.0, y: topBalanceOriginY + balanceTitle.size.height + balanceValue.size.height / 2.0)), + container: context.component.overNavigationContainer ) - context.add(balanceIcon - .position(CGPoint(x: context.availableSize.width - 16.0 - environment.safeInsets.right - balanceValue.size.width - balanceIcon.size.width / 2.0 - 2.0, y: topBalanceOriginY + balanceTitle.size.height + balanceValue.size.height / 2.0 - UIScreenPixel)) + context.addWithExternalContainer(balanceIcon + .position(CGPoint(x: context.availableSize.width - 16.0 - environment.safeInsets.right - balanceValue.size.width - balanceIcon.size.width / 2.0 - 2.0, y: topBalanceOriginY + balanceTitle.size.height + balanceValue.size.height / 2.0 - UIScreenPixel)), + container: context.component.overNavigationContainer ) return context.availableSize @@ -1010,6 +988,8 @@ public final class StarsPurchaseScreen: ViewControllerComponentContainer { fileprivate let context: AccountContext fileprivate let starsContext: StarsContext + private let overNavigationContainer: UIView + private var didSetReady = false private let _ready = Promise() public override var ready: Promise { @@ -1027,6 +1007,8 @@ public final class StarsPurchaseScreen: ViewControllerComponentContainer { ) { self.context = context self.starsContext = starsContext + + self.overNavigationContainer = SparseContainerView() var openAppExamplesImpl: (() -> Void)? var updateInProgressImpl: ((Bool) -> Void)? @@ -1034,6 +1016,7 @@ public final class StarsPurchaseScreen: ViewControllerComponentContainer { var completionImpl: ((Int64) -> Void)? super.init(context: context, component: StarsPurchaseScreenComponent( context: context, + overNavigationContainer: self.overNavigationContainer, starsContext: starsContext, options: options, purpose: purpose, @@ -1050,7 +1033,7 @@ public final class StarsPurchaseScreen: ViewControllerComponentContainer { completion: { stars in completionImpl?(stars) } - ), navigationBarAppearance: .transparent, presentationMode: .modal, theme: customTheme.flatMap { .custom($0) } ?? .default) + ), navigationBarAppearance: .default, presentationMode: .modal, theme: customTheme.flatMap { .custom($0) } ?? .default) let presentationData = context.sharedContext.currentPresentationData.with { $0 } @@ -1090,6 +1073,10 @@ public final class StarsPurchaseScreen: ViewControllerComponentContainer { completion(stars) } } + + if let navigationBar = self.navigationBar { + navigationBar.customOverBackgroundContentView.insertSubview(self.overNavigationContainer, at: 0) + } } required public init(coder aDecoder: NSCoder) { @@ -1129,10 +1116,10 @@ public final class StarsPurchaseScreen: ViewControllerComponentContainer { super.containerLayoutUpdated(layout, transition: transition) if !self.didSetReady { - if let view = self.node.hostView.findTaggedView(tag: PremiumStarComponent.View.Tag()) as? PremiumStarComponent.View { + if let view = findTaggedComponentViewImpl(view: self.node.view, tag: PremiumStarComponent.View.Tag()) as? PremiumStarComponent.View { self.didSetReady = true self._ready.set(view.ready) - } else if let view = self.node.hostView.findTaggedView(tag: GiftAvatarComponent.View.Tag()) as? GiftAvatarComponent.View { + } else if let view = findTaggedComponentViewImpl(view: self.node.view, tag: GiftAvatarComponent.View.Tag()) as? GiftAvatarComponent.View { self.didSetReady = true self._ready.set(view.ready) } diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/BUILD b/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/BUILD index 48c14fca..647e3f90 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/BUILD +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/BUILD @@ -39,6 +39,8 @@ swift_library( "//submodules/TelegramUI/Components/Premium/PremiumStarComponent", "//submodules/TelegramUI/Components/Gifts/GiftAnimationComponent", "//submodules/TelegramUI/Components/GlassBarButtonComponent", + "//submodules/TelegramUI/Components/Gifts/TableComponent", + "//submodules/TelegramUI/Components/Gifts/PeerTableCellComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift index c396f492..7677a5af 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionScreen/Sources/StarsTransactionScreen.swift @@ -28,6 +28,8 @@ import MiniAppListScreen import PremiumStarComponent import GiftAnimationComponent import GlassBarButtonComponent +import TableComponent +import PeerTableCellComponent private final class StarsTransactionSheetContent: CombinedComponent { typealias EnvironmentType = ViewControllerComponentContainer.Environment @@ -677,7 +679,7 @@ private final class StarsTransactionSheetContent: CombinedComponent { component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: theme.chat.inputPanel.panelControlColor ) )), action: { _ in @@ -936,9 +938,10 @@ private final class StarsTransactionSheetContent: CombinedComponent { component: AnyComponent( Button( content: AnyComponent( - PeerCellComponent( + PeerTableCellComponent( context: component.context, theme: theme, + strings: strings, peer: nil ) ), @@ -983,9 +986,10 @@ private final class StarsTransactionSheetContent: CombinedComponent { id: AnyHashable(0), component: AnyComponent(Button( content: AnyComponent( - PeerCellComponent( + PeerTableCellComponent( context: component.context, theme: theme, + strings: strings, peer: toPeer ) ), @@ -1026,9 +1030,10 @@ private final class StarsTransactionSheetContent: CombinedComponent { toComponent = AnyComponent( Button( content: AnyComponent( - PeerCellComponent( + PeerTableCellComponent( context: component.context, theme: theme, + strings: strings, peer: toPeer ) ), @@ -1164,9 +1169,10 @@ private final class StarsTransactionSheetContent: CombinedComponent { component: AnyComponent( Button( content: AnyComponent( - PeerCellComponent( + PeerTableCellComponent( context: component.context, theme: theme, + strings: strings, peer: toPeer ) ), @@ -1196,9 +1202,10 @@ private final class StarsTransactionSheetContent: CombinedComponent { component: AnyComponent( Button( content: AnyComponent( - PeerCellComponent( + PeerTableCellComponent( context: component.context, theme: theme, + strings: strings, peer: starRefPeer ) ), @@ -1227,9 +1234,10 @@ private final class StarsTransactionSheetContent: CombinedComponent { component: AnyComponent( Button( content: AnyComponent( - PeerCellComponent( + PeerTableCellComponent( context: component.context, theme: theme, + strings: strings, peer: toPeer ) ), @@ -1638,7 +1646,7 @@ private final class StarsTransactionSheetContent: CombinedComponent { style: .glass, color: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.1), foreground: theme.list.itemCheckColors.fillColor, - pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.8), + pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.8) ), content: AnyComponentWithIdentity( id: AnyHashable(0), @@ -2146,241 +2154,6 @@ public class StarsTransactionScreen: ViewControllerComponentContainer { } } -private final class TableComponent: CombinedComponent { - class Item: Equatable { - public let id: AnyHashable - public let title: String - public let component: AnyComponent - public let insets: UIEdgeInsets? - - public init(id: IdType, title: String, component: AnyComponent, insets: UIEdgeInsets? = nil) { - self.id = AnyHashable(id) - self.title = title - self.component = component - self.insets = insets - } - - public static func == (lhs: Item, rhs: Item) -> Bool { - if lhs.id != rhs.id { - return false - } - if lhs.title != rhs.title { - return false - } - if lhs.component != rhs.component { - return false - } - if lhs.insets != rhs.insets { - return false - } - return true - } - } - - private let theme: PresentationTheme - private let items: [Item] - - public init(theme: PresentationTheme, items: [Item]) { - self.theme = theme - self.items = items - } - - public static func ==(lhs: TableComponent, rhs: TableComponent) -> Bool { - if lhs.theme !== rhs.theme { - return false - } - if lhs.items != rhs.items { - return false - } - return true - } - - final class State: ComponentState { - var cachedLeftColumnImage: (UIImage, PresentationTheme)? - var cachedBorderImage: (UIImage, PresentationTheme)? - } - - func makeState() -> State { - return State() - } - - public static var body: Body { - let leftColumnBackground = Child(Image.self) - let verticalBorder = Child(Rectangle.self) - let titleChildren = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self) - let valueChildren = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self) - let borderChildren = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self) - let outerBorder = Child(Image.self) - - return { context in - let verticalPadding: CGFloat = 11.0 - let horizontalPadding: CGFloat = 12.0 - let borderWidth: CGFloat = 1.0 - let borderRadius: CGFloat = 14.0 - - let backgroundColor = context.component.theme.actionSheet.opaqueItemBackgroundColor - let borderColor = backgroundColor.mixedWith(context.component.theme.list.itemBlocksSeparatorColor, alpha: 0.6) - let secondaryBackgroundColor = context.component.theme.overallDarkAppearance ? context.component.theme.list.itemModalBlocksBackgroundColor : context.component.theme.list.itemInputField.backgroundColor - - var leftColumnWidth: CGFloat = 0.0 - - var updatedTitleChildren: [_UpdatedChildComponent] = [] - var updatedValueChildren: [(_UpdatedChildComponent, UIEdgeInsets)] = [] - var updatedBorderChildren: [_UpdatedChildComponent] = [] - - for item in context.component.items { - let titleChild = titleChildren[item.id].update( - component: AnyComponent(MultilineTextComponent( - text: .plain(NSAttributedString(string: item.title, font: Font.regular(15.0), textColor: context.component.theme.list.itemPrimaryTextColor)) - )), - availableSize: context.availableSize, - transition: context.transition - ) - updatedTitleChildren.append(titleChild) - - if titleChild.size.width > leftColumnWidth { - leftColumnWidth = titleChild.size.width - } - } - - leftColumnWidth = max(100.0, leftColumnWidth + horizontalPadding * 2.0) - let rightColumnWidth = context.availableSize.width - leftColumnWidth - - var i = 0 - var rowHeights: [Int: CGFloat] = [:] - var totalHeight: CGFloat = 0.0 - - for item in context.component.items { - let titleChild = updatedTitleChildren[i] - - let insets: UIEdgeInsets - if let customInsets = item.insets { - insets = customInsets - } else { - insets = UIEdgeInsets(top: 0.0, left: horizontalPadding, bottom: 0.0, right: horizontalPadding) - } - let valueChild = valueChildren[item.id].update( - component: item.component, - availableSize: CGSize(width: rightColumnWidth - insets.left - insets.right, height: context.availableSize.height), - transition: context.transition - ) - updatedValueChildren.append((valueChild, insets)) - - let rowHeight = max(40.0, max(titleChild.size.height, valueChild.size.height) + verticalPadding * 2.0) - rowHeights[i] = rowHeight - totalHeight += rowHeight - - if i < context.component.items.count - 1 { - let borderChild = borderChildren[item.id].update( - component: AnyComponent(Rectangle(color: borderColor)), - availableSize: CGSize(width: context.availableSize.width, height: borderWidth), - transition: context.transition - ) - updatedBorderChildren.append(borderChild) - } - - i += 1 - } - - let leftColumnImage: UIImage - if let (currentImage, theme) = context.state.cachedLeftColumnImage, theme === context.component.theme { - leftColumnImage = currentImage - } else { - leftColumnImage = generateImage(CGSize(width: borderRadius * 2.0 + 4.0, height: borderRadius * 2.0 + 4.0), rotatedContext: { size, context in - let bounds = CGRect(origin: .zero, size: CGSize(width: size.width + borderRadius, height: size.height)) - context.clear(bounds) - - let path = CGPath(roundedRect: bounds.insetBy(dx: borderWidth / 2.0, dy: borderWidth / 2.0), cornerWidth: borderRadius, cornerHeight: borderRadius, transform: nil) - context.setFillColor(secondaryBackgroundColor.cgColor) - context.addPath(path) - context.fillPath() - })!.stretchableImage(withLeftCapWidth: Int(borderRadius), topCapHeight: Int(borderRadius)) - context.state.cachedLeftColumnImage = (leftColumnImage, context.component.theme) - } - - let leftColumnBackground = leftColumnBackground.update( - component: Image(image: leftColumnImage), - availableSize: CGSize(width: leftColumnWidth, height: totalHeight), - transition: context.transition - ) - context.add( - leftColumnBackground - .position(CGPoint(x: leftColumnWidth / 2.0, y: totalHeight / 2.0)) - ) - - let borderImage: UIImage - if let (currentImage, theme) = context.state.cachedBorderImage, theme === context.component.theme { - borderImage = currentImage - } else { - borderImage = generateImage(CGSize(width: borderRadius * 2.0 + 4.0, height: borderRadius * 2.0 + 4.0), rotatedContext: { size, context in - let bounds = CGRect(origin: .zero, size: size) - context.clear(bounds) - - let path = CGPath(roundedRect: bounds.insetBy(dx: borderWidth / 2.0, dy: borderWidth / 2.0), cornerWidth: borderRadius, cornerHeight: borderRadius, transform: nil) - context.setBlendMode(.clear) - context.addPath(path) - context.fillPath() - - context.setBlendMode(.normal) - context.setStrokeColor(borderColor.cgColor) - context.setLineWidth(borderWidth) - context.addPath(path) - context.strokePath() - })!.stretchableImage(withLeftCapWidth: Int(borderRadius), topCapHeight: Int(borderRadius)) - context.state.cachedBorderImage = (borderImage, context.component.theme) - } - - let outerBorder = outerBorder.update( - component: Image(image: borderImage), - availableSize: CGSize(width: context.availableSize.width, height: totalHeight), - transition: context.transition - ) - context.add(outerBorder - .position(CGPoint(x: context.availableSize.width / 2.0, y: totalHeight / 2.0)) - ) - - let verticalBorder = verticalBorder.update( - component: Rectangle(color: borderColor), - availableSize: CGSize(width: borderWidth, height: totalHeight), - transition: context.transition - ) - context.add( - verticalBorder - .position(CGPoint(x: leftColumnWidth - borderWidth / 2.0, y: totalHeight / 2.0)) - ) - - i = 0 - var originY: CGFloat = 0.0 - for (titleChild, (valueChild, valueInsets)) in zip(updatedTitleChildren, updatedValueChildren) { - let rowHeight = rowHeights[i] ?? 0.0 - - let titleFrame = CGRect(origin: CGPoint(x: horizontalPadding, y: originY + verticalPadding), size: titleChild.size) - let valueFrame = CGRect(origin: CGPoint(x: leftColumnWidth + valueInsets.left, y: originY + verticalPadding), size: valueChild.size) - - context.add(titleChild - .position(titleFrame.center) - ) - - context.add(valueChild - .position(valueFrame.center) - ) - - if i < updatedBorderChildren.count { - let borderChild = updatedBorderChildren[i] - context.add(borderChild - .position(CGPoint(x: context.availableSize.width / 2.0, y: originY + rowHeight - borderWidth / 2.0)) - ) - } - - originY += rowHeight - i += 1 - } - - return CGSize(width: context.availableSize.width, height: totalHeight) - } - } -} - private final class PeerCellComponent: Component { let context: AccountContext let theme: PresentationTheme diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift index e07614b1..c4207b6e 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsStatisticsScreen.swift @@ -26,6 +26,7 @@ final class StarsStatisticsScreenComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment let context: AccountContext + let overNavigationContainer: UIView let peerId: EnginePeer.Id let revenueContext: StarsRevenueStatsContext let openTransaction: (StarsContext.State.Transaction) -> Void @@ -36,6 +37,7 @@ final class StarsStatisticsScreenComponent: Component { init( context: AccountContext, + overNavigationContainer: UIView, peerId: EnginePeer.Id, revenueContext: StarsRevenueStatsContext, openTransaction: @escaping (StarsContext.State.Transaction) -> Void, @@ -45,6 +47,7 @@ final class StarsStatisticsScreenComponent: Component { buyAds: @escaping () -> Void ) { self.context = context + self.overNavigationContainer = overNavigationContainer self.peerId = peerId self.revenueContext = revenueContext self.openTransaction = openTransaction @@ -125,10 +128,6 @@ final class StarsStatisticsScreenComponent: Component { private let scrollView: ScrollViewImpl private var currentSelectedPanelId: AnyHashable? - - private let navigationBackgroundView: BlurredBackgroundView - private let navigationSeparatorLayer: SimpleLayer - private let navigationSeparatorLayerContainer: SimpleLayer private let headerView = ComponentView() private let headerOffsetContainer: UIView @@ -175,14 +174,6 @@ final class StarsStatisticsScreenComponent: Component { self.headerOffsetContainer = UIView() self.headerOffsetContainer.isUserInteractionEnabled = false - self.navigationBackgroundView = BlurredBackgroundView(color: nil, enableBlur: true) - self.navigationBackgroundView.alpha = 0.0 - - self.navigationSeparatorLayer = SimpleLayer() - self.navigationSeparatorLayer.opacity = 0.0 - self.navigationSeparatorLayerContainer = SimpleLayer() - self.navigationSeparatorLayerContainer.opacity = 0.0 - self.scrollContainerView = UIView() self.scrollView = ScrollViewImpl() @@ -208,11 +199,6 @@ final class StarsStatisticsScreenComponent: Component { self.scrollView.addSubview(self.scrollContainerView) self.scrollContainerView.addSubview(self.transactionsBackground) - self.addSubview(self.navigationBackgroundView) - - self.navigationSeparatorLayerContainer.addSublayer(self.navigationSeparatorLayer) - self.layer.addSublayer(self.navigationSeparatorLayerContainer) - self.addSubview(self.headerOffsetContainer) } @@ -307,18 +293,10 @@ final class StarsStatisticsScreenComponent: Component { let isLockedAtPanels = scrollBounds.maxY == self.scrollView.contentSize.height if let _ = self.navigationMetrics { - let topContentOffset = self.scrollView.contentOffset.y - let navigationBackgroundAlpha = min(20.0, max(0.0, topContentOffset)) / 20.0 - - let animatedTransition = ComponentTransition(animation: .curve(duration: 0.18, curve: .easeInOut)) - animatedTransition.setAlpha(view: self.navigationBackgroundView, alpha: navigationBackgroundAlpha) - animatedTransition.setAlpha(layer: self.navigationSeparatorLayerContainer, alpha: navigationBackgroundAlpha) - let expansionDistance: CGFloat = 32.0 var expansionDistanceFactor: CGFloat = abs(scrollBounds.maxY - self.scrollView.contentSize.height) / expansionDistance expansionDistanceFactor = max(0.0, min(1.0, expansionDistanceFactor)) - transition.setAlpha(layer: self.navigationSeparatorLayer, alpha: expansionDistanceFactor) if let panelContainerView = self.panelContainer.view as? StarsTransactionsPanelContainerComponent.View { panelContainerView.updateNavigationMergeFactor(value: 1.0 - expansionDistanceFactor, transition: transition) } @@ -417,19 +395,7 @@ final class StarsStatisticsScreenComponent: Component { self.controller = environment.controller self.navigationMetrics = (environment.navigationHeight, environment.statusBarHeight) - - self.navigationSeparatorLayer.backgroundColor = environment.theme.rootController.navigationBar.separatorColor.cgColor - - let navigationFrame = CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: environment.navigationHeight)) - self.navigationBackgroundView.updateColor(color: environment.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate) - self.navigationBackgroundView.update(size: navigationFrame.size, transition: transition.containedViewLayoutTransition) - transition.setFrame(view: self.navigationBackgroundView, frame: navigationFrame) - - let navigationSeparatorFrame = CGRect(origin: CGPoint(x: 0.0, y: navigationFrame.maxY), size: CGSize(width: availableSize.width, height: UIScreenPixel)) - - transition.setFrame(layer: self.navigationSeparatorLayerContainer, frame: navigationSeparatorFrame) - transition.setFrame(layer: self.navigationSeparatorLayer, frame: CGRect(origin: CGPoint(), size: navigationSeparatorFrame.size)) - + self.backgroundColor = environment.theme.list.blocksBackgroundColor var contentHeight: CGFloat = 0.0 @@ -454,7 +420,7 @@ final class StarsStatisticsScreenComponent: Component { ) if let titleView = self.titleView.view { if titleView.superview == nil { - self.addSubview(titleView) + component.overNavigationContainer.addSubview(titleView) } let titlePosition = CGPoint(x: availableSize.width / 2.0, y: environment.statusBarHeight + (environment.navigationHeight - environment.statusBarHeight) / 2.0) transition.setPosition(view: titleView, position: titlePosition) @@ -847,6 +813,8 @@ public final class StarsStatisticsScreen: ViewControllerComponentContainer { private let peerId: EnginePeer.Id private let revenueContext: StarsRevenueStatsContext + private let overNavigationContainer: UIView + private weak var tooltipScreen: UndoOverlayController? private var timer: Foundation.Timer? @@ -857,6 +825,8 @@ public final class StarsStatisticsScreen: ViewControllerComponentContainer { self.peerId = peerId self.revenueContext = revenueContext + self.overNavigationContainer = SparseContainerView() + var buyImpl: (() -> Void)? var withdrawImpl: (() -> Void)? var buyAdsImpl: (() -> Void)? @@ -864,6 +834,7 @@ public final class StarsStatisticsScreen: ViewControllerComponentContainer { var openTransactionImpl: ((StarsContext.State.Transaction) -> Void)? super.init(context: context, component: StarsStatisticsScreenComponent( context: context, + overNavigationContainer: self.overNavigationContainer, peerId: peerId, revenueContext: revenueContext, openTransaction: { transaction in @@ -881,7 +852,7 @@ public final class StarsStatisticsScreen: ViewControllerComponentContainer { buyAds: { buyAdsImpl?() } - ), navigationBarAppearance: .transparent) + ), navigationBarAppearance: .default) self.navigationPresentation = .modalInLargeLayout @@ -1060,6 +1031,10 @@ public final class StarsStatisticsScreen: ViewControllerComponentContainer { } componentView.scrollToTop() } + + if let navigationBar = self.navigationBar { + navigationBar.customOverBackgroundContentView.insertSubview(self.overNavigationContainer, at: 0) + } } required public init(coder aDecoder: NSCoder) { diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift index 07bed879..91d7256e 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift @@ -102,10 +102,6 @@ final class StarsTransactionsScreenComponent: Component { private let scrollView: ScrollViewImpl private var currentSelectedPanelId: AnyHashable? - - private let navigationBackgroundView: BlurredBackgroundView - private let navigationSeparatorLayer: SimpleLayer - private let navigationSeparatorLayerContainer: SimpleLayer private let scrollContainerView: UIView @@ -160,14 +156,6 @@ final class StarsTransactionsScreenComponent: Component { private var cachedChevronImage: (UIImage, PresentationTheme)? override init(frame: CGRect) { - self.navigationBackgroundView = BlurredBackgroundView(color: nil, enableBlur: true) - self.navigationBackgroundView.alpha = 0.0 - - self.navigationSeparatorLayer = SimpleLayer() - self.navigationSeparatorLayer.opacity = 0.0 - self.navigationSeparatorLayerContainer = SimpleLayer() - self.navigationSeparatorLayerContainer.opacity = 0.0 - self.scrollContainerView = UIView() self.scrollView = ScrollViewImpl() @@ -191,11 +179,6 @@ final class StarsTransactionsScreenComponent: Component { self.addSubview(self.scrollView) self.scrollView.addSubview(self.scrollContainerView) - - self.addSubview(self.navigationBackgroundView) - - self.navigationSeparatorLayerContainer.addSublayer(self.navigationSeparatorLayer) - self.layer.addSublayer(self.navigationSeparatorLayerContainer) } required init?(coder: NSCoder) { @@ -296,7 +279,6 @@ final class StarsTransactionsScreenComponent: Component { var topContentOffset = self.scrollView.contentOffset.y - let navigationBackgroundAlpha = min(20.0, max(0.0, topContentOffset - 95.0)) / 20.0 topContentOffset = topContentOffset + max(0.0, min(1.0, topContentOffset / titleOffsetDelta)) * 10.0 titleOffset = topContentOffset let fraction = max(0.0, min(1.0, titleOffset / titleOffsetDelta)) @@ -317,15 +299,10 @@ final class StarsTransactionsScreenComponent: Component { headerTransition.setScale(view: titleView, scale: titleScale) } - let animatedTransition = ComponentTransition(animation: .curve(duration: 0.18, curve: .easeInOut)) - animatedTransition.setAlpha(view: self.navigationBackgroundView, alpha: navigationBackgroundAlpha) - animatedTransition.setAlpha(layer: self.navigationSeparatorLayerContainer, alpha: navigationBackgroundAlpha) - let expansionDistance: CGFloat = 32.0 var expansionDistanceFactor: CGFloat = abs(scrollBounds.maxY - self.scrollView.contentSize.height) / expansionDistance expansionDistanceFactor = max(0.0, min(1.0, expansionDistanceFactor)) - transition.setAlpha(layer: self.navigationSeparatorLayer, alpha: expansionDistanceFactor) if let panelContainerView = self.panelContainer.view as? StarsTransactionsPanelContainerComponent.View { panelContainerView.updateNavigationMergeFactor(value: 1.0 - expansionDistanceFactor, transition: transition) } @@ -435,18 +412,6 @@ final class StarsTransactionsScreenComponent: Component { self.navigationMetrics = (environment.navigationHeight, environment.statusBarHeight) - self.navigationSeparatorLayer.backgroundColor = environment.theme.rootController.navigationBar.separatorColor.cgColor - - let navigationFrame = CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: environment.navigationHeight)) - self.navigationBackgroundView.updateColor(color: environment.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate) - self.navigationBackgroundView.update(size: navigationFrame.size, transition: transition.containedViewLayoutTransition) - transition.setFrame(view: self.navigationBackgroundView, frame: navigationFrame) - - let navigationSeparatorFrame = CGRect(origin: CGPoint(x: 0.0, y: navigationFrame.maxY), size: CGSize(width: availableSize.width, height: UIScreenPixel)) - - transition.setFrame(layer: self.navigationSeparatorLayerContainer, frame: navigationSeparatorFrame) - transition.setFrame(layer: self.navigationSeparatorLayer, frame: CGRect(origin: CGPoint(), size: navigationSeparatorFrame.size)) - self.backgroundColor = environment.theme.list.blocksBackgroundColor var contentHeight: CGFloat = 0.0 @@ -519,7 +484,7 @@ final class StarsTransactionsScreenComponent: Component { UIColor(rgb: 0xfdd219) ], particleColor: UIColor(rgb: 0xf9b004), - backgroundColor: environment.theme.list.blocksBackgroundColor + backgroundColor: nil )) } @@ -532,7 +497,13 @@ final class StarsTransactionsScreenComponent: Component { let starFrame = CGRect(origin: .zero, size: starSize) if let starView = self.starView.view { if starView.superview == nil { - self.insertSubview(starView, aboveSubview: self.scrollView) + if let navigationBar = environment.controller()?.navigationBar { + if let titleView = self.titleView.view, titleView.superview != nil { + navigationBar.view.insertSubview(starView, belowSubview: titleView) + } else { + navigationBar.view.insertSubview(starView, aboveSubview: navigationBar.backgroundView) + } + } } starTransition.setBounds(view: starView, bounds: starFrame) } @@ -562,7 +533,9 @@ final class StarsTransactionsScreenComponent: Component { ) if let titleView = self.titleView.view { if titleView.superview == nil { - self.addSubview(titleView) + if let navigationBar = environment.controller()?.navigationBar { + navigationBar.view.insertSubview(titleView, aboveSubview: navigationBar.backgroundView) + } } starTransition.setBounds(view: titleView, bounds: CGRect(origin: .zero, size: titleSize)) } @@ -617,7 +590,13 @@ final class StarsTransactionsScreenComponent: Component { if let topBalanceTitleView = self.topBalanceTitleView.view { if topBalanceTitleView.superview == nil { topBalanceTitleView.alpha = 0.0 - self.addSubview(topBalanceTitleView) + if let navigationBar = environment.controller()?.navigationBar { + if let titleView = self.titleView.view, titleView.superview != nil { + navigationBar.view.insertSubview(topBalanceTitleView, aboveSubview: titleView) + } else { + navigationBar.view.insertSubview(topBalanceTitleView, aboveSubview: navigationBar.backgroundView) + } + } } starTransition.setFrame(view: topBalanceTitleView, frame: topBalanceTitleFrame) } @@ -626,7 +605,13 @@ final class StarsTransactionsScreenComponent: Component { if let topBalanceValueView = self.topBalanceValueView.view { if topBalanceValueView.superview == nil { topBalanceValueView.alpha = 0.0 - self.addSubview(topBalanceValueView) + if let navigationBar = environment.controller()?.navigationBar { + if let titleView = self.titleView.view, titleView.superview != nil { + navigationBar.view.insertSubview(topBalanceValueView, aboveSubview: titleView) + } else { + navigationBar.view.insertSubview(topBalanceValueView, aboveSubview: navigationBar.backgroundView) + } + } } starTransition.setFrame(view: topBalanceValueView, frame: topBalanceValueFrame) } @@ -638,7 +623,13 @@ final class StarsTransactionsScreenComponent: Component { if let topBalanceIconView = self.topBalanceIconView.view { if topBalanceIconView.superview == nil { topBalanceIconView.alpha = 0.0 - self.addSubview(topBalanceIconView) + if let navigationBar = environment.controller()?.navigationBar { + if let titleView = self.titleView.view, titleView.superview != nil { + navigationBar.view.insertSubview(topBalanceIconView, aboveSubview: titleView) + } else { + navigationBar.view.insertSubview(topBalanceIconView, aboveSubview: navigationBar.backgroundView) + } + } } starTransition.setFrame(view: topBalanceIconView, frame: topBalanceIconFrame) } @@ -844,7 +835,7 @@ final class StarsTransactionsScreenComponent: Component { footer: nil, items: [ AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemComponentAdaptor( - itemGenerator: ItemListDisclosureItem(presentationData: ItemListPresentationData(presentationData), icon: PresentationResourcesSettings.earnStars, title: environment.strings.Monetization_EarnStarsInfo_Title, titleBadge: presentationData.strings.Settings_New, label: environment.strings.Monetization_EarnStarsInfo_Text, labelStyle: .multilineDetailText, sectionId: 0, style: .blocks, action: { + itemGenerator: ItemListDisclosureItem(presentationData: ItemListPresentationData(presentationData), icon: PresentationResourcesSettings.earnStars, title: environment.strings.Monetization_EarnStarsInfo_Title, titleBadge: nil, label: environment.strings.Monetization_EarnStarsInfo_Text, labelStyle: .multilineDetailText, sectionId: 0, style: .blocks, action: { }), params: ListViewItemLayoutParams(width: availableSize.width, leftInset: 0.0, rightInset: 0.0, availableHeight: 10000.0, isStandalone: true), action: { [weak self] in @@ -1261,7 +1252,7 @@ public final class StarsTransactionsScreen: ViewControllerComponentContainer { gift: { giftImpl?() } - ), navigationBarAppearance: .transparent) + ), navigationBarAppearance: .default) self.navigationPresentation = .modalInLargeLayout diff --git a/submodules/TelegramUI/Components/Stars/StarsTransferScreen/Sources/StarsTransferScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransferScreen/Sources/StarsTransferScreen.swift index 050dccbc..d5157826 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransferScreen/Sources/StarsTransferScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransferScreen/Sources/StarsTransferScreen.swift @@ -348,7 +348,7 @@ private final class SheetContent: CombinedComponent { component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: theme.chat.inputPanel.panelControlColor ) )), action: { _ in @@ -655,7 +655,9 @@ private final class SheetContent: CombinedComponent { controller?.complete(paid: success) controller?.dismissAnimated() - starsContext.load(force: true) + Queue.mainQueue().after(2.5) { + starsContext.load(force: true) + } }) } ), diff --git a/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/BUILD b/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/BUILD index 4e7000f2..01c08ce4 100644 --- a/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/BUILD +++ b/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/BUILD @@ -42,6 +42,8 @@ swift_library( "//submodules/TelegramUI/Components/Stars/BalanceNeededScreen", "//submodules/TelegramUI/Components/GlassBarButtonComponent", "//submodules/TelegramUI/Components/GlassBackgroundComponent", + "//submodules/TelegramUI/Components/AlertComponent", + "//submodules/TelegramUI/Components/AlertComponent/AlertInputFieldComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsRevenueWithdrawalController.swift b/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsRevenueWithdrawalController.swift index cc62907d..41b92175 100644 --- a/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsRevenueWithdrawalController.swift +++ b/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsRevenueWithdrawalController.swift @@ -6,106 +6,141 @@ import TelegramPresentationData import PresentationDataUtils import AccountContext import PasswordSetupUI -import Markdown -import OwnershipTransferController +import ComponentFlow +import AlertComponent +import AlertInputFieldComponent public func confirmStarsRevenueWithdrawalController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peerId: EnginePeer.Id, amount: Int64, present: @escaping (ViewController, Any?) -> Void, completion: @escaping (String) -> Void) -> ViewController { - let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let strings = presentationData.strings + + let inputState = AlertInputFieldComponent.ExternalState() + + let doneIsEnabled: Signal = inputState.valueSignal + |> map { value in + return !value.isEmpty + } + + let doneInProgressPromise = ValuePromise(false) + + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: strings.Monetization_Withdraw_EnterPassword_Title) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(strings.Monetization_Withdraw_EnterPassword_Text)) + ) + )) + + var applyImpl: (() -> Void)? + content.append(AnyComponentWithIdentity( + id: "input", + component: AnyComponent( + AlertInputFieldComponent( + context: context, + placeholder: strings.Channel_OwnershipTransfer_PasswordPlaceholder, + isSecureTextEntry: true, + isInitiallyFocused: true, + externalState: inputState, + returnKeyAction: { + applyImpl?() + } + ) + ) + )) + + var effectiveUpdatedPresentationData: (PresentationData, Signal) + if let updatedPresentationData { + effectiveUpdatedPresentationData = updatedPresentationData + } else { + effectiveUpdatedPresentationData = (presentationData, context.sharedContext.presentationData) + } var dismissImpl: (() -> Void)? - var proceedImpl: (() -> Void)? - - let disposable = MetaDisposable() - - let contentNode = ChannelOwnershipTransferAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, title: presentationData.strings.Monetization_Withdraw_EnterPassword_Title, text: presentationData.strings.Monetization_Withdraw_EnterPassword_Text, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - dismissImpl?() - }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Monetization_Withdraw_EnterPassword_Done, action: { - proceedImpl?() - })]) - - contentNode.complete = { - proceedImpl?() - } - - let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode) - let presentationDataDisposable = (updatedPresentationData?.signal ?? context.sharedContext.presentationData).start(next: { [weak controller, weak contentNode] presentationData in - controller?.theme = AlertControllerTheme(presentationData: presentationData) - contentNode?.theme = presentationData.theme - }) - controller.dismissed = { _ in - presentationDataDisposable.dispose() - disposable.dispose() - } - dismissImpl = { [weak controller, weak contentNode] in - contentNode?.dismissInput() - controller?.dismissAnimated() - } - proceedImpl = { [weak contentNode] in - guard let contentNode = contentNode else { - return - } - contentNode.updateIsChecking(true) - - let signal = context.engine.peers.requestStarsRevenueWithdrawalUrl(peerId: peerId, ton: false, amount: amount, password: contentNode.password) - disposable.set((signal |> deliverOnMainQueue).start(next: { url in + let alertController = AlertScreen( + configuration: AlertScreen.Configuration(allowInputInset: true), + content: content, + actions: [ + .init(title: strings.Common_Cancel), + .init(title: strings.Monetization_Withdraw_EnterPassword_Done, type: .default, action: { + applyImpl?() + }, autoDismiss: false, isEnabled: doneIsEnabled, progress: doneInProgressPromise.get()) + ], + updatedPresentationData: effectiveUpdatedPresentationData + ) + applyImpl = { + doneInProgressPromise.set(true) + + let _ = (context.engine.peers.requestStarsRevenueWithdrawalUrl(peerId: peerId, ton: false, amount: amount, password: inputState.value) + |> deliverOnMainQueue).start(next: { url in dismissImpl?() completion(url) - }, error: { [weak contentNode] error in + }, error: { error in var errorTextAndActions: (String, [TextAlertAction])? switch error { - case .invalidPassword: - contentNode?.animateError() - case .limitExceeded: - errorTextAndActions = (presentationData.strings.TwoStepAuth_FloodError, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) - default: - errorTextAndActions = (presentationData.strings.Login_UnknownError, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) + case .invalidPassword: + inputState.animateError() + case .limitExceeded: + errorTextAndActions = (strings.TwoStepAuth_FloodError, [TextAlertAction(type: .defaultAction, title: strings.Common_OK, action: {})]) + default: + errorTextAndActions = (strings.Login_UnknownError, [TextAlertAction(type: .defaultAction, title: strings.Common_OK, action: {})]) } - contentNode?.updateIsChecking(false) - + doneInProgressPromise.set(false) + if let (text, actions) = errorTextAndActions { dismissImpl?() present(textAlertController(context: context, title: nil, text: text, actions: actions), nil) } - })) + }) } - - return controller + dismissImpl = { [weak alertController] in + alertController?.dismiss(completion: nil) + } + return alertController } public func starsRevenueWithdrawalController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peerId: EnginePeer.Id, amount: Int64, initialError: RequestStarsRevenueWithdrawalError, present: @escaping (ViewController, Any?) -> Void, completion: @escaping (String) -> Void) -> ViewController { let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } - let theme = AlertControllerTheme(presentationData: presentationData) + let strings = presentationData.strings - var title: NSAttributedString? = NSAttributedString(string: presentationData.strings.OwnershipTransfer_SecurityCheck, font: Font.semibold(presentationData.listsFontSize.itemListBaseFontSize), textColor: theme.primaryColor, paragraphAlignment: .center) - - var text = presentationData.strings.Monetization_Withdraw_SecurityRequirements - let textFontSize = presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0 - - var actions: [TextAlertAction] = [] + var title: String? = strings.OwnershipTransfer_SecurityCheck + var text = strings.Monetization_Withdraw_SecurityRequirements + + var actions: [AlertScreen.Action] = [ + .init(title: strings.Common_OK, type: .default) + ] switch initialError { - case .requestPassword: + case .requestPassword: return confirmStarsRevenueWithdrawalController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, amount: amount, present: present, completion: completion) - case .twoStepAuthTooFresh, .authSessionTooFresh: - text = text + presentationData.strings.Monetization_Withdraw_ComeBackLater - actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})] - case .twoStepAuthMissing: - actions = [TextAlertAction(type: .genericAction, title: presentationData.strings.OwnershipTransfer_SetupTwoStepAuth, action: { + case .twoStepAuthTooFresh, .authSessionTooFresh: + text = text + strings.Monetization_Withdraw_ComeBackLater + case .twoStepAuthMissing: + actions = [ + .init(title: strings.OwnershipTransfer_SetupTwoStepAuth, type: .default, action: { let controller = SetupTwoStepVerificationController(context: context, initialState: .automatic, stateUpdated: { update, shouldDismiss, controller in if shouldDismiss { controller.dismiss() } }) present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) - }), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {})] - default: - title = nil - text = presentationData.strings.Login_UnknownError - actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})] + }), + .init(title: strings.Common_Cancel) + ] + default: + title = nil + text = strings.Login_UnknownError } - let body = MarkdownAttributeSet(font: Font.regular(textFontSize), textColor: theme.primaryColor) - let bold = MarkdownAttributeSet(font: Font.semibold(textFontSize), textColor: theme.primaryColor) - let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .center) - - return richTextAlertController(context: context, title: title, text: attributedText, actions: actions) + return AlertScreen( + context: context, + configuration: AlertScreen.Configuration(actionAlignment: .vertical), + title: title, + text: text, + actions: actions + ) } diff --git a/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift b/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift index 17df8810..c8dbc216 100644 --- a/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift @@ -95,7 +95,7 @@ private final class SheetContent: CombinedComponent { component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: theme.chat.inputPanel.panelControlColor ) )), action: { _ in @@ -167,7 +167,11 @@ private final class SheetContent: CombinedComponent { maxAmount = StarsAmount(value: resaleConfiguration.starGiftResaleMaxStarsAmount, nanos: 0) case .ton: amountTitle = environment.strings.Stars_SellGift_TonAmountTitle + #if DEBUG + minAmount = StarsAmount(value: 48000000000, nanos: 0) + #else minAmount = StarsAmount(value: resaleConfiguration.starGiftResaleMinTonAmount, nanos: 0) + #endif maxAmount = StarsAmount(value: resaleConfiguration.starGiftResaleMaxTonAmount, nanos: 0) } case let .paidMessages(_, minAmountValue, _, _, _): @@ -618,7 +622,14 @@ private final class SheetContent: CombinedComponent { } if state.currency == .stars { if let amount = state.amount, let tonUsdRate = withdrawConfiguration.tonUsdRate, let usdWithdrawRate = withdrawConfiguration.usdWithdrawRate { - state.amount = StarsAmount(value: max(min(convertStarsToTon(amount, tonUsdRate: tonUsdRate, starsUsdRate: usdWithdrawRate), resaleConfiguration.starGiftResaleMaxTonAmount), resaleConfiguration.starGiftResaleMinTonAmount), nanos: 0) + var convertedValue = min(convertStarsToTon(amount, tonUsdRate: tonUsdRate, starsUsdRate: usdWithdrawRate), resaleConfiguration.starGiftResaleMaxTonAmount) + if convertedValue < resaleConfiguration.starGiftResaleMinTonAmount { + convertedValue = resaleConfiguration.starGiftResaleMinTonAmount + if case .starGiftResell = component.mode, let controller = controller() as? StarsWithdrawScreen { + controller.presentMinAmountTooltip(convertedValue, currency: .ton) + } + } + state.amount = StarsAmount(value: convertedValue, nanos: 0) } else { state.amount = StarsAmount(value: 0, nanos: 0) } @@ -1255,6 +1266,8 @@ private final class StarsWithdrawSheetComponent: CombinedComponent { let sheet = Child(SheetComponent<(EnvironmentType)>.self) let animateOut = StoredActionSlot(Action.self) + let sheetExternalState = SheetComponent.ExternalState() + return { context in let environment = context.environment[EnvironmentType.self] @@ -1281,6 +1294,7 @@ private final class StarsWithdrawSheetComponent: CombinedComponent { followContentSizeChanges: false, clipsContent: true, isScrollEnabled: false, + externalState: sheetExternalState, animateOut: animateOut ), environment: { @@ -1313,6 +1327,29 @@ private final class StarsWithdrawSheetComponent: CombinedComponent { .position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0)) ) + if let controller = controller(), !controller.automaticallyControlPresentationContextLayout { + var sideInset: CGFloat = 0.0 + var bottomInset: CGFloat = max(environment.safeInsets.bottom, sheetExternalState.contentHeight) + if case .regular = environment.metrics.widthClass { + sideInset = floor((context.availableSize.width - 430.0) / 2.0) - 12.0 + bottomInset = (context.availableSize.height - sheetExternalState.contentHeight) / 2.0 + sheetExternalState.contentHeight + } + + let layout = ContainerViewLayout( + size: context.availableSize, + metrics: environment.metrics, + deviceMetrics: environment.deviceMetrics, + intrinsicInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: bottomInset, right: 0.0), + safeInsets: UIEdgeInsets(top: 0.0, left: max(sideInset, environment.safeInsets.left), bottom: 0.0, right: max(sideInset, environment.safeInsets.right)), + additionalInsets: .zero, + statusBarHeight: environment.statusBarHeight, + inputHeight: nil, + inputHeightIsInteractivellyChanging: false, + inVoiceOver: false + ) + controller.presentationContext.containerLayoutUpdated(layout, transition: context.transition.containedViewLayoutTransition) + } + return context.availableSize } } @@ -1357,6 +1394,7 @@ public final class StarsWithdrawScreen: ViewControllerComponentContainer { ) self.navigationPresentation = .flatModal + self.automaticallyControlPresentationContextLayout = false } required public init(coder aDecoder: NSCoder) { @@ -1422,10 +1460,8 @@ public final class StarsWithdrawScreen: ViewControllerComponentContainer { round: false, undoText: nil ), - elevatedLayout: false, - position: .top, action: { _ in return true}) - self.present(resultController, in: .window(.root)) + self.present(resultController, in: .current) if let view = self.node.hostView.findTaggedView(tag: amountTag) as? AmountFieldComponent.View { view.animateError() @@ -1769,8 +1805,15 @@ public final class AmountFieldComponent: Component { guard let component = self.component, let value = component.value else { return } - self.textField.text = "\(value)" - self.placeholderView.view?.isHidden = self.textField.text?.isEmpty ?? false + var text = "" + switch component.currency { + case .stars: + text = "\(value)" + case .ton: + text = "\(formatTonAmountText(value, dateTimeFormat: PresentationDateTimeFormat(timeFormat: component.dateTimeFormat.timeFormat, dateFormat: component.dateTimeFormat.dateFormat, dateSeparator: "", dateSuffix: "", requiresFullYear: false, decimalSeparator: ".", groupingSeparator: ""), maxDecimalPositions: nil))" + } + self.textField.text = text + self.placeholderView.view?.isHidden = !(self.textField.text ?? "").isEmpty } func update(component: AmountFieldComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { @@ -1790,7 +1833,7 @@ public final class AmountFieldComponent: Component { text = "\(formatTonAmountText(value, dateTimeFormat: PresentationDateTimeFormat(timeFormat: component.dateTimeFormat.timeFormat, dateFormat: component.dateTimeFormat.dateFormat, dateSeparator: "", dateSuffix: "", requiresFullYear: false, decimalSeparator: ".", groupingSeparator: ""), maxDecimalPositions: nil))" } self.textField.text = text - self.placeholderView.view?.isHidden = text.isEmpty + self.placeholderView.view?.isHidden = !text.isEmpty } else { self.textField.text = "" } diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/BUILD b/submodules/TelegramUI/Components/StorageUsageScreen/BUILD index 096451d9..4b984b59 100644 --- a/submodules/TelegramUI/Components/StorageUsageScreen/BUILD +++ b/submodules/TelegramUI/Components/StorageUsageScreen/BUILD @@ -45,6 +45,7 @@ swift_library( "//submodules/GalleryData", "//submodules/SegmentedControlNode", "//submodules/TelegramUIPreferences", + "//submodules/TelegramUI/Components/GlassBackgroundComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/DataUsageScreen.swift b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/DataUsageScreen.swift index bac4438d..50ed55b1 100644 --- a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/DataUsageScreen.swift +++ b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/DataUsageScreen.swift @@ -24,22 +24,26 @@ import GalleryData import AnimatedTextComponent import TelegramUIPreferences import SegmentControlComponent +import GlassBackgroundComponent final class DataUsageScreenComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment let context: AccountContext + let overNavigationContainer: UIView let statsSet: StatsSet let mediaAutoDownloadSettings: MediaAutoDownloadSettings let makeAutodownloadSettingsController: (Bool) -> ViewController init( context: AccountContext, + overNavigationContainer: UIView, statsSet: StatsSet, mediaAutoDownloadSettings: MediaAutoDownloadSettings, makeAutodownloadSettingsController: @escaping (Bool) -> ViewController ) { self.context = context + self.overNavigationContainer = overNavigationContainer self.statsSet = statsSet self.mediaAutoDownloadSettings = mediaAutoDownloadSettings self.makeAutodownloadSettingsController = makeAutodownloadSettingsController @@ -313,12 +317,9 @@ final class DataUsageScreenComponent: Component { private var mediaAutoDownloadSettings: MediaAutoDownloadSettings = .defaultSettings private var mediaAutoDownloadSettingsDisposable: Disposable? - private let navigationBackgroundView: BlurredBackgroundView - private let navigationSeparatorLayer: SimpleLayer - private let navigationSeparatorLayerContainer: SimpleLayer - private let headerView = ComponentView() private let headerOffsetContainer: HeaderContainer + private let headerContentContainer: HeaderContainer private let headerDescriptionView = ComponentView() private var doneLabel: ComponentView? @@ -356,14 +357,7 @@ final class DataUsageScreenComponent: Component { override init(frame: CGRect) { self.headerOffsetContainer = HeaderContainer() - - self.navigationBackgroundView = BlurredBackgroundView(color: nil, enableBlur: true) - self.navigationBackgroundView.alpha = 0.0 - - self.navigationSeparatorLayer = SimpleLayer() - self.navigationSeparatorLayer.opacity = 1.0 - self.navigationSeparatorLayerContainer = SimpleLayer() - self.navigationSeparatorLayerContainer.opacity = 0.0 + self.headerContentContainer = HeaderContainer() self.scrollContainerView = UIView() @@ -396,12 +390,7 @@ final class DataUsageScreenComponent: Component { self.scrollContainerView.addSubview(self.autoDownloadSettingsContainerView) - self.addSubview(self.navigationBackgroundView) - - self.navigationSeparatorLayerContainer.addSublayer(self.navigationSeparatorLayer) - self.layer.addSublayer(self.navigationSeparatorLayerContainer) - - self.addSubview(self.headerOffsetContainer) + self.headerOffsetContainer.addSubview(self.headerContentContainer) } required init?(coder: NSCoder) { @@ -447,41 +436,19 @@ final class DataUsageScreenComponent: Component { headerOffset = min(headerOffset, minOffset) let animatedTransition = ComponentTransition(animation: .curve(duration: 0.2, curve: .easeInOut)) - let navigationBackgroundAlpha: CGFloat = abs(headerOffset - minOffset) < 4.0 ? 1.0 : 0.0 let navigationButtonAlpha: CGFloat = scrollBounds.minY >= navigationMetrics.navigationHeight ? 0.0 : 1.0 - animatedTransition.setAlpha(view: self.navigationBackgroundView, alpha: navigationBackgroundAlpha) - animatedTransition.setAlpha(layer: self.navigationSeparatorLayerContainer, alpha: navigationBackgroundAlpha) - - /*let expansionDistance: CGFloat = 32.0 - var expansionDistanceFactor: CGFloat = abs(scrollBounds.maxY - self.scrollView.contentSize.height) / expansionDistance - expansionDistanceFactor = max(0.0, min(1.0, expansionDistanceFactor)) - - transition.setAlpha(layer: self.navigationSeparatorLayer, alpha: expansionDistanceFactor)*/ - - /*var offsetFraction: CGFloat = abs(headerOffset - minOffset) / 60.0 - offsetFraction = min(1.0, max(0.0, offsetFraction)) - transition.setScale(view: headerView, scale: 1.0 * offsetFraction + 0.8 * (1.0 - offsetFraction))*/ - transition.setBounds(view: self.headerOffsetContainer, bounds: CGRect(origin: CGPoint(x: 0.0, y: headerOffset), size: self.headerOffsetContainer.bounds.size)) + let headerContentsAlpha: CGFloat + let offsetFraction = abs(headerOffset - minOffset) / 60.0 + headerContentsAlpha = min(1.0, max(0.0, offsetFraction)) + + transition.setAlpha(view: self.headerContentContainer, alpha: headerContentsAlpha) + if let controller = self.controller?(), let backButtonNode = controller.navigationBar?.backButtonNode { backButtonNode.updateManualAlpha(alpha: navigationButtonAlpha, transition: animatedTransition.containedViewLayoutTransition) - - /*if backButtonNode.alpha != navigationButtonAlpha { - if backButtonNode.isHidden { - backButtonNode.alpha = 0.0 - backButtonNode.isHidden = false - } - animatedTransition.setAlpha(layer: backButtonNode.layer, alpha: navigationButtonAlpha, completion: { [weak backButtonNode] completed in - if let backButtonNode, completed { - if navigationButtonAlpha.isZero { - backButtonNode.isHidden = true - } - } - }) - }*/ } } } @@ -539,17 +506,9 @@ final class DataUsageScreenComponent: Component { self.navigationMetrics = (environment.navigationHeight, environment.statusBarHeight) - self.navigationSeparatorLayer.backgroundColor = environment.theme.rootController.navigationBar.separatorColor.cgColor - - let navigationFrame = CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: environment.navigationHeight)) - self.navigationBackgroundView.updateColor(color: environment.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate) - self.navigationBackgroundView.update(size: navigationFrame.size, transition: transition.containedViewLayoutTransition) - transition.setFrame(view: self.navigationBackgroundView, frame: navigationFrame) - - let navigationSeparatorFrame = CGRect(origin: CGPoint(x: 0.0, y: navigationFrame.maxY), size: CGSize(width: availableSize.width, height: UIScreenPixel)) - - transition.setFrame(layer: self.navigationSeparatorLayerContainer, frame: navigationSeparatorFrame) - transition.setFrame(layer: self.navigationSeparatorLayer, frame: CGRect(origin: CGPoint(), size: navigationSeparatorFrame.size)) + if self.headerOffsetContainer.superview == nil { + component.overNavigationContainer.addSubview(self.headerOffsetContainer) + } self.backgroundColor = environment.theme.list.blocksBackgroundColor @@ -709,7 +668,7 @@ final class DataUsageScreenComponent: Component { let pieChartFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: pieChartSize) if let pieChartComponentView = self.pieChartView.view { if pieChartComponentView.superview == nil { - self.scrollView.addSubview(pieChartComponentView) + self.headerContentContainer.addSubview(pieChartComponentView) } pieChartTransition.setFrame(view: pieChartComponentView, frame: pieChartFrame) @@ -740,7 +699,7 @@ final class DataUsageScreenComponent: Component { if let doneLabelView = doneLabel.view { var animateIn = false if doneLabelView.superview == nil { - self.scrollView.addSubview(doneLabelView) + self.headerContentContainer.addSubview(doneLabelView) animateIn = true } doneLabelTransition.setFrame(view: doneLabelView, frame: doneLabelFrame) @@ -755,7 +714,7 @@ final class DataUsageScreenComponent: Component { if let doneSupLabelView = doneSupLabel.view { var animateIn = false if doneSupLabelView.superview == nil { - self.scrollView.addSubview(doneSupLabelView) + self.headerContentContainer.addSubview(doneSupLabelView) animateIn = true } doneLabelTransition.setFrame(view: doneSupLabelView, frame: doneSupLabelFrame) @@ -796,7 +755,7 @@ final class DataUsageScreenComponent: Component { let headerViewFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - headerViewSize.width) / 2.0), y: contentHeight), size: headerViewSize) if let headerComponentView = self.headerView.view { if headerComponentView.superview == nil { - self.scrollContainerView.addSubview(headerComponentView) + self.headerOffsetContainer.addSubview(headerComponentView) } transition.setPosition(view: headerComponentView, position: headerViewFrame.center) headerComponentView.bounds = CGRect(origin: CGPoint(), size: headerViewFrame.size) @@ -838,7 +797,7 @@ final class DataUsageScreenComponent: Component { let headerDescriptionFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - headerDescriptionSize.width) / 2.0), y: contentHeight), size: headerDescriptionSize) if let headerDescriptionComponentView = self.headerDescriptionView.view { if headerDescriptionComponentView.superview == nil { - self.scrollContainerView.addSubview(headerDescriptionComponentView) + self.headerContentContainer.addSubview(headerDescriptionComponentView) } transition.setPosition(view: headerDescriptionComponentView, position: headerDescriptionFrame.center) headerDescriptionComponentView.bounds = CGRect(origin: CGPoint(), size: headerDescriptionFrame.size) @@ -892,7 +851,7 @@ final class DataUsageScreenComponent: Component { ) if let chartTotalLabelView = self.chartTotalLabel.view { if chartTotalLabelView.superview == nil { - self.scrollContainerView.addSubview(chartTotalLabelView) + self.headerContentContainer.addSubview(chartTotalLabelView) } let chartAreaHeight: CGFloat @@ -926,7 +885,7 @@ final class DataUsageScreenComponent: Component { self.state?.updated(transition: ComponentTransition(animation: .curve(duration: 0.4, curve: .spring)).withUserData(AnimationHint(value: .modeChanged))) })), environment: {}, - containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0) + containerSize: CGSize(width: availableSize.width - (environment.safeInsets.left + 16.0 + 44.0 + 10.0) * 2.0, height: 100.0) ) let segmentedControlFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - segmentedSize.width) * 0.5), y: contentHeight), size: segmentedSize) if let segmentedControlComponentView = self.segmentedControlView.view { @@ -1252,6 +1211,8 @@ final class DataUsageScreenComponent: Component { public final class DataUsageScreen: ViewControllerComponentContainer { private let context: AccountContext + private let overNavigationContainer: UIView + private let readyValue = Promise() override public var ready: Promise { return self.readyValue @@ -1260,8 +1221,14 @@ public final class DataUsageScreen: ViewControllerComponentContainer { public init(context: AccountContext, stats: NetworkUsageStats, mediaAutoDownloadSettings: MediaAutoDownloadSettings, makeAutodownloadSettingsController: @escaping (Bool) -> ViewController) { self.context = context + self.overNavigationContainer = SparseContainerView() + //let componentReady = Promise() - super.init(context: context, component: DataUsageScreenComponent(context: context, statsSet: DataUsageScreenComponent.StatsSet(stats: stats), mediaAutoDownloadSettings: mediaAutoDownloadSettings, makeAutodownloadSettingsController: makeAutodownloadSettingsController), navigationBarAppearance: .transparent) + super.init(context: context, component: DataUsageScreenComponent(context: context, overNavigationContainer: self.overNavigationContainer, statsSet: DataUsageScreenComponent.StatsSet(stats: stats), mediaAutoDownloadSettings: mediaAutoDownloadSettings, makeAutodownloadSettingsController: makeAutodownloadSettingsController), navigationBarAppearance: .default) + + if let navigationBar = self.navigationBar { + navigationBar.customOverBackgroundContentView.insertSubview(self.overNavigationContainer, at: 0) + } //self.readyValue.set(componentReady.get() |> timeout(0.3, queue: .mainQueue(), alternate: .single(true))) self.readyValue.set(.single(true)) diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift index b6629a6d..16098f33 100644 --- a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift +++ b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift @@ -24,6 +24,7 @@ import TelegramStringFormatting import GalleryData import AnimatedTextComponent import BottomButtonPanelComponent +import GlassBackgroundComponent #if DEBUG import os.signpost @@ -116,17 +117,20 @@ final class StorageUsageScreenComponent: Component { typealias EnvironmentType = ViewControllerComponentContainer.Environment let context: AccountContext + let overNavigationContainer: UIView let makeStorageUsageExceptionsScreen: (CacheStorageSettings.PeerStorageCategory) -> ViewController? let peer: EnginePeer? let ready: Promise init( context: AccountContext, + overNavigationContainer: UIView, makeStorageUsageExceptionsScreen: @escaping (CacheStorageSettings.PeerStorageCategory) -> ViewController?, peer: EnginePeer?, ready: Promise ) { self.context = context + self.overNavigationContainer = overNavigationContainer self.makeStorageUsageExceptionsScreen = makeStorageUsageExceptionsScreen self.peer = peer self.ready = ready @@ -740,9 +744,7 @@ final class StorageUsageScreenComponent: Component { private var isOtherCategoryExpanded: Bool = false - private let navigationBackgroundView: BlurredBackgroundView - private let navigationSeparatorLayer: SimpleLayer - private let navigationSeparatorLayerContainer: SimpleLayer + private let navigationRightButtonsBackground: GlassBackgroundView private let navigationEditButton = ComponentView() private let navigationDoneButton = ComponentView() @@ -799,16 +801,7 @@ final class StorageUsageScreenComponent: Component { private var keepScreenActiveDisposable: Disposable? override init(frame: CGRect) { - self.headerOffsetContainer = UIView() - self.headerOffsetContainer.isUserInteractionEnabled = false - - self.navigationBackgroundView = BlurredBackgroundView(color: nil, enableBlur: true) - self.navigationBackgroundView.alpha = 0.0 - - self.navigationSeparatorLayer = SimpleLayer() - self.navigationSeparatorLayer.opacity = 0.0 - self.navigationSeparatorLayerContainer = SimpleLayer() - self.navigationSeparatorLayerContainer.opacity = 0.0 + self.headerOffsetContainer = SparseContainerView() self.scrollContainerView = UIView() @@ -821,6 +814,8 @@ final class StorageUsageScreenComponent: Component { self.headerProgressBackgroundLayer = SimpleLayer() self.headerProgressForegroundLayer = SimpleLayer() + self.navigationRightButtonsBackground = GlassBackgroundView() + super.init(frame: frame) self.scrollView.delaysContentTouches = true @@ -846,13 +841,6 @@ final class StorageUsageScreenComponent: Component { self.scrollView.layer.addSublayer(self.headerProgressBackgroundLayer) self.scrollView.layer.addSublayer(self.headerProgressForegroundLayer) - - self.addSubview(self.navigationBackgroundView) - - self.navigationSeparatorLayerContainer.addSublayer(self.navigationSeparatorLayer) - self.layer.addSublayer(self.navigationSeparatorLayerContainer) - - self.addSubview(self.headerOffsetContainer) } required init?(coder: NSCoder) { @@ -946,9 +934,6 @@ final class StorageUsageScreenComponent: Component { let animatedTransition = ComponentTransition(animation: .curve(duration: 0.18, curve: .easeInOut)) let navigationBackgroundAlpha: CGFloat = abs(headerOffset - minOffset) < 4.0 ? 1.0 : 0.0 - animatedTransition.setAlpha(view: self.navigationBackgroundView, alpha: navigationBackgroundAlpha) - animatedTransition.setAlpha(layer: self.navigationSeparatorLayerContainer, alpha: navigationBackgroundAlpha) - var buttonsMasterAlpha: CGFloat = 1.0 if let component = self.component, component.peer != nil { buttonsMasterAlpha = 0.0 @@ -960,20 +945,12 @@ final class StorageUsageScreenComponent: Component { } } - let isSelectingPeers = self.aggregatedData?.isSelectingPeers ?? false - - if let navigationEditButtonView = self.navigationEditButton.view { - animatedTransition.setAlpha(view: navigationEditButtonView, alpha: (isSelectingPeers ? 0.0 : 1.0) * buttonsMasterAlpha * navigationBackgroundAlpha) - } - if let navigationDoneButtonView = self.navigationDoneButton.view { - animatedTransition.setAlpha(view: navigationDoneButtonView, alpha: (isSelectingPeers ? 1.0 : 0.0) * buttonsMasterAlpha * navigationBackgroundAlpha) - } + animatedTransition.setAlpha(view: self.navigationRightButtonsBackground, alpha: buttonsMasterAlpha * navigationBackgroundAlpha) let expansionDistance: CGFloat = 32.0 var expansionDistanceFactor: CGFloat = abs(scrollBounds.maxY - self.scrollView.contentSize.height) / expansionDistance expansionDistanceFactor = max(0.0, min(1.0, expansionDistanceFactor)) - transition.setAlpha(layer: self.navigationSeparatorLayer, alpha: expansionDistanceFactor) if let panelContainerView = self.panelContainer.view as? StorageUsagePanelContainerComponent.View { panelContainerView.updateNavigationMergeFactor(value: 1.0 - expansionDistanceFactor, transition: transition) } @@ -983,6 +960,17 @@ final class StorageUsageScreenComponent: Component { transition.setScale(view: headerView, scale: 1.0 * offsetFraction + 0.8 * (1.0 - offsetFraction)) transition.setBounds(view: self.headerOffsetContainer, bounds: CGRect(origin: CGPoint(x: 0.0, y: headerOffset), size: self.headerOffsetContainer.bounds.size)) + + let headerContentsAlpha = offsetFraction + if let chartAvatarNode = self.chartAvatarNode { + transition.setAlpha(view: chartAvatarNode.view, alpha: headerContentsAlpha) + } + if let pieChartComponentView = self.pieChartView.view { + transition.setAlpha(view: pieChartComponentView, alpha: headerContentsAlpha) + } + if let chartTotalLabelView = self.chartTotalLabel.view { + transition.setAlpha(view: chartTotalLabelView, alpha: headerContentsAlpha) + } } let _ = self.panelContainer.updateEnvironment( @@ -1109,6 +1097,13 @@ final class StorageUsageScreenComponent: Component { self.reloadStats(firstTime: true, completion: {}) } + if self.headerOffsetContainer.superview == nil { + component.overNavigationContainer.addSubview(self.headerOffsetContainer) + } + if self.navigationRightButtonsBackground.superview == nil { + component.overNavigationContainer.addSubview(self.navigationRightButtonsBackground) + } + var wasLockedAtPanels = false if let panelContainerView = self.panelContainer.view, let navigationMetrics = self.navigationMetrics { if self.scrollView.bounds.minY > 0.0 && abs(self.scrollView.bounds.minY - (panelContainerView.frame.minY - navigationMetrics.navigationHeight)) <= UIScreenPixel { @@ -1147,22 +1142,11 @@ final class StorageUsageScreenComponent: Component { self.navigationMetrics = (environment.navigationHeight, environment.statusBarHeight) - self.navigationSeparatorLayer.backgroundColor = environment.theme.rootController.navigationBar.separatorColor.cgColor - - let navigationFrame = CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: environment.navigationHeight)) - self.navigationBackgroundView.updateColor(color: environment.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate) - self.navigationBackgroundView.update(size: navigationFrame.size, transition: transition.containedViewLayoutTransition) - transition.setFrame(view: self.navigationBackgroundView, frame: navigationFrame) - - let navigationSeparatorFrame = CGRect(origin: CGPoint(x: 0.0, y: navigationFrame.maxY), size: CGSize(width: availableSize.width, height: UIScreenPixel)) - - transition.setFrame(layer: self.navigationSeparatorLayerContainer, frame: navigationSeparatorFrame) - transition.setFrame(layer: self.navigationSeparatorLayer, frame: CGRect(origin: CGPoint(), size: navigationSeparatorFrame.size)) - let navigationEditButtonSize = self.navigationEditButton.update( transition: transition, component: AnyComponent(Button( - content: AnyComponent(Text(text: environment.strings.Common_Edit, font: Font.regular(17.0), color: environment.theme.rootController.navigationBar.accentTextColor)), + content: AnyComponent(Text(text: environment.strings.Common_Edit, font: Font.regular(17.0), color: environment.theme.chat.inputPanel.panelControlColor)), + contentInsets: UIEdgeInsets(top: 0.0, left: 6.0, bottom: 0.0, right: 6.0), action: { [weak self] in guard let self else { return @@ -1172,21 +1156,22 @@ final class StorageUsageScreenComponent: Component { self.state?.updated(transition: ComponentTransition(animation: .curve(duration: 0.4, curve: .spring))) } } - ).minSize(CGSize(width: 16.0, height: environment.navigationHeight - environment.statusBarHeight))), + ).minSize(CGSize(width: 44.0, height: 44.0))), environment: {}, - containerSize: CGSize(width: 150.0, height: environment.navigationHeight - environment.statusBarHeight) + containerSize: CGSize(width: 150.0, height: 44.0) ) if let navigationEditButtonView = self.navigationEditButton.view { if navigationEditButtonView.superview == nil { - self.addSubview(navigationEditButtonView) + self.navigationRightButtonsBackground.contentView.addSubview(navigationEditButtonView) } - transition.setFrame(view: navigationEditButtonView, frame: CGRect(origin: CGPoint(x: availableSize.width - 12.0 - environment.safeInsets.right - navigationEditButtonSize.width, y: environment.statusBarHeight), size: navigationEditButtonSize)) + transition.setFrame(view: navigationEditButtonView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: navigationEditButtonSize)) } let navigationDoneButtonSize = self.navigationDoneButton.update( transition: transition, component: AnyComponent(Button( - content: AnyComponent(Text(text: environment.strings.Common_Done, font: Font.semibold(17.0), color: environment.theme.rootController.navigationBar.accentTextColor)), + content: AnyComponent(Text(text: environment.strings.Common_Done, font: Font.semibold(17.0), color: environment.theme.chat.inputPanel.panelControlColor)), + contentInsets: UIEdgeInsets(top: 0.0, left: 6.0, bottom: 0.0, right: 6.0), action: { [weak self] in guard let self, let aggregatedData = self.aggregatedData else { return @@ -1195,19 +1180,39 @@ final class StorageUsageScreenComponent: Component { aggregatedData.clearPeerSelection() self.state?.updated(transition: ComponentTransition(animation: .curve(duration: 0.4, curve: .spring))) } - ).minSize(CGSize(width: 16.0, height: environment.navigationHeight - environment.statusBarHeight))), + ).minSize(CGSize(width: 44.0, height: 44.0))), environment: {}, - containerSize: CGSize(width: 150.0, height: environment.navigationHeight - environment.statusBarHeight) + containerSize: CGSize(width: 150.0, height: 44.0) ) if let navigationDoneButtonView = self.navigationDoneButton.view { if navigationDoneButtonView.superview == nil { - self.addSubview(navigationDoneButtonView) + self.navigationRightButtonsBackground.contentView.addSubview(navigationDoneButtonView) } - transition.setFrame(view: navigationDoneButtonView, frame: CGRect(origin: CGPoint(x: availableSize.width - 12.0 - environment.safeInsets.right - navigationDoneButtonSize.width, y: environment.statusBarHeight), size: navigationDoneButtonSize)) + transition.setFrame(view: navigationDoneButtonView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: navigationDoneButtonSize)) } let navigationRightButtonMaxWidth: CGFloat = max(navigationEditButtonSize.width, navigationDoneButtonSize.width) + var rightButtonsWidth: CGFloat = 0.0 + let isSelectingPeers = self.aggregatedData?.isSelectingPeers ?? false + if let navigationEditButtonView = self.navigationEditButton.view { + if !isSelectingPeers { + rightButtonsWidth += navigationEditButtonSize.width + } + transition.setAlpha(view: navigationEditButtonView, alpha: isSelectingPeers ? 0.0 : 1.0) + } + if let navigationDoneButtonView = self.navigationDoneButton.view { + if isSelectingPeers { + rightButtonsWidth += navigationDoneButtonSize.width + } + transition.setAlpha(view: navigationDoneButtonView, alpha: isSelectingPeers ? 1.0 : 0.0) + } + + let navigationRightButtonsBackgroundSize = CGSize(width: max(44.0, rightButtonsWidth), height: 44.0) + self.navigationRightButtonsBackground.update(size: navigationRightButtonsBackgroundSize, cornerRadius: 44.0 * 0.5, isDark: environment.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: environment.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition) + let navigationRightButtonsBackgroundFrame = CGRect(origin: CGPoint(x: availableSize.width - environment.safeInsets.right - 16.0 - navigationRightButtonsBackgroundSize.width, y: environment.statusBarHeight + 2.0 + floor((environment.navigationHeight - environment.statusBarHeight - 44.0) * 0.5)), size: navigationRightButtonsBackgroundSize) + transition.setFrame(view: self.navigationRightButtonsBackground, frame: navigationRightButtonsBackgroundFrame) + self.backgroundColor = environment.theme.list.blocksBackgroundColor var contentHeight: CGFloat = 0.0 @@ -1436,7 +1441,7 @@ final class StorageUsageScreenComponent: Component { let pieChartFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: pieChartSize) if let pieChartComponentView = self.pieChartView.view { if pieChartComponentView.superview == nil { - self.scrollView.addSubview(pieChartComponentView) + self.headerOffsetContainer.addSubview(pieChartComponentView) } pieChartTransition.setFrame(view: pieChartComponentView, frame: pieChartFrame) @@ -1637,7 +1642,7 @@ final class StorageUsageScreenComponent: Component { } else { chartAvatarNode = AvatarNode(font: avatarPlaceholderFont(size: 17.0)) self.chartAvatarNode = chartAvatarNode - self.scrollContainerView.addSubview(chartAvatarNode.view) + self.headerOffsetContainer.addSubview(chartAvatarNode.view) chartAvatarNode.frame = avatarFrame if peer.id == component.context.account.peerId { @@ -1681,7 +1686,7 @@ final class StorageUsageScreenComponent: Component { ) if let chartTotalLabelView = self.chartTotalLabel.view { if chartTotalLabelView.superview == nil { - self.scrollContainerView.addSubview(chartTotalLabelView) + self.headerOffsetContainer.addSubview(chartTotalLabelView) } let totalLabelFrame = CGRect(origin: CGPoint(x: pieChartFrame.minX + floor((pieChartFrame.width - chartTotalLabelSize.width) / 2.0), y: pieChartFrame.minY + floor((pieChartFrame.height - chartTotalLabelSize.height) / 2.0)), size: chartTotalLabelSize) transition.setFrame(view: chartTotalLabelView, frame: totalLabelFrame) @@ -3303,6 +3308,8 @@ final class StorageUsageScreenComponent: Component { public final class StorageUsageScreen: ViewControllerComponentContainer { private let context: AccountContext + private let overNavigationContainer: UIView + private let readyValue = Promise() override public var ready: Promise { return self.readyValue @@ -3313,13 +3320,19 @@ public final class StorageUsageScreen: ViewControllerComponentContainer { public init(context: AccountContext, makeStorageUsageExceptionsScreen: @escaping (CacheStorageSettings.PeerStorageCategory) -> ViewController?, peer: EnginePeer? = nil) { self.context = context + self.overNavigationContainer = SparseContainerView() + let componentReady = Promise() - super.init(context: context, component: StorageUsageScreenComponent(context: context, makeStorageUsageExceptionsScreen: makeStorageUsageExceptionsScreen, peer: peer, ready: componentReady), navigationBarAppearance: .transparent) + super.init(context: context, component: StorageUsageScreenComponent(context: context, overNavigationContainer: self.overNavigationContainer, makeStorageUsageExceptionsScreen: makeStorageUsageExceptionsScreen, peer: peer, ready: componentReady), navigationBarAppearance: .default) if peer != nil { self.navigationPresentation = .modal } + if let navigationBar = self.navigationBar { + navigationBar.customOverBackgroundContentView.insertSubview(self.overNavigationContainer, at: 0) + } + self.readyValue.set(componentReady.get() |> timeout(0.3, queue: .mainQueue(), alternate: .single(true))) } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/BUILD b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/BUILD index 54ef6207..797719a4 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/BUILD +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/BUILD @@ -114,6 +114,7 @@ swift_library( "//submodules/Components/HierarchyTrackingLayer", "//submodules/Utils/LokiRng", "//submodules/TelegramUI/Components/PeerNameTextComponent", + "//submodules/TelegramUI/Components/AlertComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift index 75fe51d2..02d96ffa 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift @@ -357,7 +357,8 @@ final class StoryItemContentComponent: Component { useLargeThumbnail: false, autoFetchFullSizeThumbnail: false, tempFilePath: nil, - captureProtected: component.item.isForwardingDisabled, + // MISC: Bypass story screenshot protection + captureProtected: MiscSettingsManager.shared.shouldBypassScreenshotProtection ? false : component.item.isForwardingDisabled, hintDimensions: file.dimensions?.cgSize, storeAfterDownload: nil, displayImage: false, @@ -777,7 +778,8 @@ final class StoryItemContentComponent: Component { availableReactions: component.availableReactions, entityFiles: component.entityFiles, size: size, - isCaptureProtected: component.item.isForwardingDisabled, + // MISC: Bypass story screenshot protection + isCaptureProtected: MiscSettingsManager.shared.shouldBypassScreenshotProtection ? false : component.item.isForwardingDisabled, attemptSynchronous: synchronousLoad, isActive: self.progressMode.mode == .play, transition: transition diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemImageView.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemImageView.swift index a5dcac69..fff8de95 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemImageView.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemImageView.swift @@ -67,7 +67,10 @@ final class StoryItemImageView: UIView { } func update(context: AccountContext, strings: PresentationStrings, peer: EnginePeer, storyId: Int32, media: EngineMedia, size: CGSize, isCaptureProtected: Bool, attemptSynchronous: Bool, transition: ComponentTransition) { - self.backgroundColor = isCaptureProtected ? UIColor(rgb: 0x181818) : nil + // MISC: Bypass story screenshot protection + let effectiveCaptureProtected = MiscSettingsManager.shared.shouldBypassScreenshotProtection ? false : isCaptureProtected + + self.backgroundColor = effectiveCaptureProtected ? UIColor(rgb: 0x181818) : nil var dimensions: CGSize? @@ -87,11 +90,11 @@ final class StoryItemImageView: UIView { if attemptSynchronous, let path = context.account.postbox.mediaBox.completedResourcePath(id: representation.resource.id, pathExtension: nil) { if #available(iOS 15.0, *) { if let image = UIImage(contentsOfFile: path)?.preparingForDisplay() { - self.updateImage(image: image, isCaptureProtected: isCaptureProtected) + self.updateImage(image: image, isCaptureProtected: effectiveCaptureProtected) } } else { if let image = UIImage(contentsOfFile: path)?.precomposed() { - self.updateImage(image: image, isCaptureProtected: isCaptureProtected) + self.updateImage(image: image, isCaptureProtected: effectiveCaptureProtected) } } self.isContentLoaded = true @@ -99,7 +102,7 @@ final class StoryItemImageView: UIView { } else { if let thumbnailData = image.immediateThumbnailData.flatMap(decodeTinyThumbnail), let thumbnailImage = UIImage(data: thumbnailData) { if let image = blurredImage(thumbnailImage, radius: 10.0, iterations: 3) { - self.updateImage(image: image, isCaptureProtected: isCaptureProtected) + self.updateImage(image: image, isCaptureProtected: effectiveCaptureProtected) } } @@ -131,7 +134,7 @@ final class StoryItemImageView: UIView { return } if let image { - self.updateImage(image: image, isCaptureProtected: isCaptureProtected) + self.updateImage(image: image, isCaptureProtected: effectiveCaptureProtected) self.isContentLoaded = true self.didLoadContents?() } @@ -148,11 +151,11 @@ final class StoryItemImageView: UIView { if attemptSynchronous, FileManager.default.fileExists(atPath: cachedPath) { if #available(iOS 15.0, *) { if let image = UIImage(contentsOfFile: cachedPath)?.preparingForDisplay() { - self.updateImage(image: image, isCaptureProtected: isCaptureProtected) + self.updateImage(image: image, isCaptureProtected: effectiveCaptureProtected) } } else { if let image = UIImage(contentsOfFile: cachedPath)?.precomposed() { - self.updateImage(image: image, isCaptureProtected: isCaptureProtected) + self.updateImage(image: image, isCaptureProtected: effectiveCaptureProtected) } } self.isContentLoaded = true @@ -160,7 +163,7 @@ final class StoryItemImageView: UIView { } else { if let thumbnailData = file.immediateThumbnailData.flatMap(decodeTinyThumbnail), let thumbnailImage = UIImage(data: thumbnailData) { if let image = blurredImage(thumbnailImage, radius: 10.0, iterations: 3) { - self.updateImage(image: image, isCaptureProtected: isCaptureProtected) + self.updateImage(image: image, isCaptureProtected: effectiveCaptureProtected) } } @@ -213,7 +216,7 @@ final class StoryItemImageView: UIView { return } if let image { - self.updateImage(image: image, isCaptureProtected: isCaptureProtected) + self.updateImage(image: image, isCaptureProtected: effectiveCaptureProtected) self.isContentLoaded = true self.didLoadContents?() } @@ -225,7 +228,7 @@ final class StoryItemImageView: UIView { if isMediaUpdated, let thumbnailData = peer.smallProfileImage?.immediateThumbnailData { if let thumbnailData = decodeTinyThumbnail(data: thumbnailData), let thumbnailImage = UIImage(data: thumbnailData) { if let image = blurredImage(thumbnailImage, radius: 10.0, iterations: 3) { - self.updateImage(image: image, isCaptureProtected: isCaptureProtected) + self.updateImage(image: image, isCaptureProtected: effectiveCaptureProtected) } } } @@ -246,7 +249,7 @@ final class StoryItemImageView: UIView { } } - if isCaptureProtected { + if effectiveCaptureProtected { let captureProtectedInfo: ComponentView var captureProtectedInfoTransition = transition if let current = self.captureProtectedInfo { diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index 57c21f08..a037a504 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -7575,7 +7575,7 @@ public final class StoryItemSetContainerComponent: Component { let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme) let promptController = promptController( - sharedContext: component.context.sharedContext, + context: component.context, updatedPresentationData: (initial: presentationData, signal: .single(presentationData)), text: presentationData.strings.Stories_CreateAlbum_Title, titleFont: .bold, diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift index e5bd1753..619f13e4 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift @@ -54,6 +54,7 @@ import ChatSendStarsScreen import AnimatedTextComponent import ChatSendAsContextMenu import ShareWithPeersScreen +import AlertComponent private var ObjCKey_DeinitWatcher: Int? @@ -359,7 +360,6 @@ final class StoryItemSetContainerSendMessage: @unchecked(Sendable) { pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, - importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, @@ -637,17 +637,16 @@ final class StoryItemSetContainerSendMessage: @unchecked(Sendable) { let theme = component.theme let updatedPresentationData: (initial: PresentationData, signal: Signal) = (component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: theme), component.context.sharedContext.presentationData |> map { $0.withUpdated(theme: theme) }) - let alertController = textAlertController( - context: component.context, - updatedPresentationData: updatedPresentationData, + let alertController = AlertScreen( title: component.strings.Story_AlertStealthModeActiveTitle, text: component.strings.Story_AlertStealthModeActiveText, actions: [ - TextAlertAction(type: .defaultAction, title: component.strings.Common_Cancel, action: {}), - TextAlertAction(type: .genericAction, title: component.strings.Story_AlertStealthModeActiveAction, action: { + .init(title: component.strings.Common_Cancel, type: .default), + .init(title: component.strings.Story_AlertStealthModeActiveAction, type: .generic, action: { action() }) - ] + ], + updatedPresentationData: updatedPresentationData ) alertController.dismissed = { [weak self, weak view] _ in guard let self, let view else { @@ -761,7 +760,7 @@ final class StoryItemSetContainerSendMessage: @unchecked(Sendable) { let absMaxEmojiCount = paramSets.paramSets.max(by: { $0.minStars < $1.minStars })?.maxEmojiCount ?? 10 if emojiCount > absMaxEmojiCount { let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: component.theme) - view.component?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.LiveStream_ErrorMaxAllowedEmoji_Text(Int32(absMaxEmojiCount)), actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + view.component?.controller()?.present(textAlertController(context: component.context, updatedPresentationData: (presentationData, .single(presentationData)), title: nil, text: presentationData.strings.LiveStream_ErrorMaxAllowedEmoji_Text(Int32(absMaxEmojiCount)), actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) return } @@ -2410,18 +2409,16 @@ final class StoryItemSetContainerSendMessage: @unchecked(Sendable) { component.controller()?.push(controller) } }, presentSelectionLimitExceeded: { [weak view] in - guard let view else { + guard let view, let component = view.component else { return } - let text: String if slowModeEnabled { text = presentationData.strings.Chat_SlowmodeAttachmentLimitReached } else { text = presentationData.strings.Chat_AttachmentLimitReached } - - view.component?.controller()?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + component.controller()?.present(textAlertController(context: component.context, updatedPresentationData: (presentationData, .single(presentationData)), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) }, presentSchedulePicker: { [weak view] media, done in if let strongSelf = self, let view { strongSelf.presentScheduleTimePicker(view: view, peer: peer, style: media ? .media : .default, completion: { time, repeatPeriod in diff --git a/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift b/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift index 544deae7..6512a4d5 100644 --- a/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryPeerListComponent/Sources/StoryPeerListComponent.swift @@ -761,7 +761,7 @@ public final class StoryPeerListComponent: Component { let collapsedItemWidth: CGFloat = 24.0 let collapsedItemDistance: CGFloat = 14.0 - let collapsedItemOffsetY: CGFloat = -54.0 + let collapsedItemOffsetY: CGFloat = -66.0 let titleContentSpacing: CGFloat = 8.0 let collapsedItemCount: CGFloat = CGFloat(min(self.sortedItems.count - collapseStartIndex, 3)) @@ -1409,7 +1409,7 @@ public final class StoryPeerListComponent: Component { } if let titleIndicatorSize, let titleIndicatorView = self.titleIndicatorView?.view { - let titleIndicatorFrame = CGRect(origin: CGPoint(x: titleContentOffset - titleIndicatorSize.width - 9.0, y: collapsedItemOffsetY + 2.0 + floor((56.0 - titleIndicatorSize.height) * 0.5)), size: titleIndicatorSize) + let titleIndicatorFrame = CGRect(origin: CGPoint(x: titleContentOffset - titleIndicatorSize.width - 9.0, y: collapsedItemOffsetY + 14.0 + floor((56.0 - titleIndicatorSize.height) * 0.5)), size: titleIndicatorSize) if titleIndicatorView.superview == nil { self.addSubview(titleIndicatorView) } @@ -1427,7 +1427,7 @@ public final class StoryPeerListComponent: Component { titleIndicatorView.alpha = indicatorAlpha } - let titleFrame = CGRect(origin: CGPoint(x: titleContentOffset + titleLockOffset, y: collapsedItemOffsetY + 2.0 + floor((56.0 - titleSize.height) * 0.5)), size: titleSize) + let titleFrame = CGRect(origin: CGPoint(x: titleContentOffset + titleLockOffset, y: collapsedItemOffsetY + 14.0 + floor((56.0 - titleSize.height) * 0.5)), size: titleSize) if let image = self.titleView.image { self.titleView.center = CGPoint(x: titleFrame.minX, y: titleFrame.midY) self.titleView.bounds = CGRect(origin: CGPoint(), size: image.size) @@ -1494,7 +1494,7 @@ public final class StoryPeerListComponent: Component { if let titleIconSize, let titleIconView = self.titleIconView?.view { titleContentOffset += titleIconSpacing - let titleIconFrame = CGRect(origin: CGPoint(x: titleContentOffset - 3.0 + titleIconSpacing + (collapsedState.titleWidth - (titleIconSpacing + titleIconSize.width)) * (1.0 - collapsedState.activityFraction), y: collapsedItemOffsetY + 2.0 + floor((56.0 - titleIconSize.height) * 0.5)), size: titleIconSize) + let titleIconFrame = CGRect(origin: CGPoint(x: titleContentOffset - 3.0 + titleIconSpacing + (collapsedState.titleWidth - (titleIconSpacing + titleIconSize.width)) * (1.0 - collapsedState.activityFraction), y: collapsedItemOffsetY + 14.0 + floor((56.0 - titleIconSize.height) * 0.5)), size: titleIconSize) if titleIconView.superview == nil { self.addSubview(titleIconView) @@ -1729,7 +1729,7 @@ public final class StoryPeerListComponent: Component { let itemLayout = ItemLayout( containerSize: availableSize, - containerInsets: UIEdgeInsets(top: 4.0, left: component.sideInset - 4.0, bottom: 0.0, right: component.sideInset - 4.0), + containerInsets: UIEdgeInsets(top: 16.0, left: component.sideInset - 4.0, bottom: 0.0, right: component.sideInset - 4.0), itemSize: CGSize(width: 60.0, height: 77.0), itemSpacing: 14.0, itemCount: self.sortedItems.count diff --git a/submodules/TelegramUI/Components/Stories/StoryStealthModeSheetScreen/Sources/StoryStealthModeSheetScreen.swift b/submodules/TelegramUI/Components/Stories/StoryStealthModeSheetScreen/Sources/StoryStealthModeSheetScreen.swift index cfb2959f..d7ab2478 100644 --- a/submodules/TelegramUI/Components/Stories/StoryStealthModeSheetScreen/Sources/StoryStealthModeSheetScreen.swift +++ b/submodules/TelegramUI/Components/Stories/StoryStealthModeSheetScreen/Sources/StoryStealthModeSheetScreen.swift @@ -227,7 +227,7 @@ private final class StoryStealthModeSheetContentComponent: Component { component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: environment.theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: environment.theme.chat.inputPanel.panelControlColor ) )), action: { _ in diff --git a/submodules/TelegramUI/Components/SuggestedPostApproveAlert/Sources/SuggestedPostApproveAlert.swift b/submodules/TelegramUI/Components/SuggestedPostApproveAlert/Sources/SuggestedPostApproveAlert.swift index 8a39ab81..97a677e5 100644 --- a/submodules/TelegramUI/Components/SuggestedPostApproveAlert/Sources/SuggestedPostApproveAlert.swift +++ b/submodules/TelegramUI/Components/SuggestedPostApproveAlert/Sources/SuggestedPostApproveAlert.swift @@ -410,7 +410,18 @@ private final class SuggestedPostAlertImpl: AlertController { } } -public func SuggestedPostApproveAlert(presentationData: PresentationData, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, parseMarkdown: Bool = false, dismissOnOutsideTap: Bool = true, linkAction: (([NSAttributedString.Key: Any], Int) -> Void)? = nil, toastText: String?) -> AlertController { +public func SuggestedPostApproveAlert( + presentationData: PresentationData, + title: String?, + text: String, + actions: [TextAlertAction], + actionLayout: TextAlertContentActionLayout = .horizontal, + allowInputInset: Bool = true, + parseMarkdown: Bool = false, + dismissOnOutsideTap: Bool = true, + linkAction : (([NSAttributedString.Key: Any], Int) -> Void)? = nil, + toastText: String? +) -> AlertController { let theme = AlertControllerTheme(presentationData: presentationData) var dismissImpl: (() -> Void)? diff --git a/submodules/TelegramUI/Components/TabBarComponent/BUILD b/submodules/TelegramUI/Components/TabBarComponent/BUILD index bf1656c7..ad24d1f7 100644 --- a/submodules/TelegramUI/Components/TabBarComponent/BUILD +++ b/submodules/TelegramUI/Components/TabBarComponent/BUILD @@ -22,6 +22,8 @@ swift_library( "//submodules/UIKitRuntimeUtils", "//submodules/TelegramUI/Components/LiquidLens", "//submodules/AppBundle", + "//submodules/SearchBarNode", + "//submodules/TelegramUI/Components/TabSelectionRecognizer", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/TabBarComponent/Sources/TabBarComponent.swift b/submodules/TelegramUI/Components/TabBarComponent/Sources/TabBarComponent.swift index 585e15ed..9d97455e 100644 --- a/submodules/TelegramUI/Components/TabBarComponent/Sources/TabBarComponent.swift +++ b/submodules/TelegramUI/Components/TabBarComponent/Sources/TabBarComponent.swift @@ -12,92 +12,236 @@ import BundleIconComponent import TextBadgeComponent import LiquidLens import AppBundle +import SearchBarNode +import TabSelectionRecognizer -private final class TabSelectionRecognizer: UIGestureRecognizer { - private var initialLocation: CGPoint? - private var currentLocation: CGPoint? - - override init(target: Any?, action: Selector?) { - super.init(target: target, action: action) - - self.delaysTouchesBegan = false - self.delaysTouchesEnded = false - } - - override func reset() { - super.reset() - - self.initialLocation = nil - } - - override func touchesBegan(_ touches: Set, with event: UIEvent) { - super.touchesBegan(touches, with: event) - - if self.initialLocation == nil { - self.initialLocation = touches.first?.location(in: self.view) - } - self.currentLocation = self.initialLocation - - self.state = .began - } - - override func touchesEnded(_ touches: Set, with event: UIEvent) { - super.touchesEnded(touches, with: event) - - self.state = .ended - } - - override func touchesCancelled(_ touches: Set, with event: UIEvent) { - super.touchesCancelled(touches, with: event) - - self.state = .cancelled - } - - override func touchesMoved(_ touches: Set, with event: UIEvent) { - super.touchesMoved(touches, with: event) - - self.currentLocation = touches.first?.location(in: self.view) - - self.state = .changed - } - - func translation(in: UIView?) -> CGPoint { - if let initialLocation = self.initialLocation, let currentLocation = self.currentLocation { - return CGPoint(x: currentLocation.x - initialLocation.x, y: currentLocation.y - initialLocation.y) - } - return CGPoint() - } -} +public final class NavigationSearchView: UIView { + private struct Params: Equatable { + let size: CGSize + let theme: PresentationTheme + let strings: PresentationStrings + let isActive: Bool + + init(size: CGSize, theme: PresentationTheme, strings: PresentationStrings, isActive: Bool) { + self.size = size + self.theme = theme + self.strings = strings + self.isActive = isActive + } + + static func ==(lhs: Params, rhs: Params) -> Bool { + if lhs.size != rhs.size { + return false + } + if lhs.theme !== rhs.theme { + return false + } + if lhs.strings !== rhs.strings { + return false + } + if lhs.isActive != rhs.isActive { + return false + } + return true + } + } + + private let action: () -> Void + private let closeAction: () -> Void -public final class TabBarSearchView: UIView { private let backgroundView: GlassBackgroundView - private let iconView: GlassBackgroundView.ContentImageView + private let iconView: UIImageView + private(set) var searchBarNode: SearchBarNode? - override public init(frame: CGRect) { - self.backgroundView = GlassBackgroundView() - self.iconView = GlassBackgroundView.ContentImageView() + private var close: (background: GlassBackgroundView, icon: UIImageView)? - super.init(frame: frame) + private var params: Params? + + public init(action: @escaping () -> Void, closeAction: @escaping () -> Void) { + self.action = action + self.closeAction = closeAction + + self.backgroundView = GlassBackgroundView() + self.backgroundView.contentView.clipsToBounds = true + self.iconView = UIImageView() + + super.init(frame: CGRect()) self.addSubview(self.backgroundView) self.backgroundView.contentView.addSubview(self.iconView) + + self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.onTapGesture(_:)))) + } + + @objc private func onTapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + self.action() + } + } + + @objc private func onCloseTapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + self.closeAction() + } } required public init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - public func update(size: CGSize, isDark: Bool, tintColor: GlassBackgroundView.TintColor, iconColor: UIColor, transition: ComponentTransition) { - transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: size)) - self.backgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: isDark, tintColor: tintColor, transition: transition) + public func update(size: CGSize, theme: PresentationTheme, strings: PresentationStrings, isActive: Bool, transition: ComponentTransition) { + let params = Params(size: size, theme: theme, strings: strings, isActive: isActive) + if self.params == params { + return + } + self.params = params + self.update(params: params, transition: transition) + } + + private func update(params: Params, transition: ComponentTransition) { + let backgroundSize: CGSize + if params.isActive { + backgroundSize = CGSize(width: params.size.width - 48.0 - 8.0, height: params.size.height) + } else { + backgroundSize = CGSize(width: params.size.width, height: params.size.height) + } + + let previousBackgroundFrame = self.backgroundView.frame + + transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: backgroundSize)) + let alphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.25) + + self.backgroundView.update(size: backgroundSize, cornerRadius: backgroundSize.height * 0.5, isDark: params.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: params.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition) if self.iconView.image == nil { self.iconView.image = UIImage(bundleImageName: "Navigation/Search")?.withRenderingMode(.alwaysTemplate) } - self.iconView.tintColor = iconColor + transition.setTintColor(view: self.iconView, color: params.isActive ? params.theme.rootController.navigationSearchBar.inputIconColor : params.theme.chat.inputPanel.panelControlColor) if let image = self.iconView.image { - transition.setFrame(view: self.iconView, frame: CGRect(origin: CGPoint(x: floor((size.width - image.size.width) * 0.5), y: floor((size.height - image.size.height) * 0.5)), size: image.size)) + let imageSize: CGSize + let iconFrame: CGRect + if params.isActive { + let iconFraction: CGFloat = 0.8 + imageSize = CGSize(width: image.size.width * iconFraction, height: image.size.height * iconFraction) + iconFrame = CGRect(origin: CGPoint(x: 12.0, y: floor((params.size.height - imageSize.height) * 0.5)), size: imageSize) + } else { + iconFrame = CGRect(origin: CGPoint(x: floor((backgroundSize.width - image.size.width) * 0.5), y: floor((params.size.height - image.size.height) * 0.5)), size: image.size) + } + transition.setPosition(view: self.iconView, position: iconFrame.center) + transition.setBounds(view: self.iconView, bounds: CGRect(origin: CGPoint(), size: iconFrame.size)) + } + + if params.isActive { + let searchBarNode: SearchBarNode + var searchBarNodeTransition = transition + if let current = self.searchBarNode { + searchBarNode = current + } else { + searchBarNodeTransition = searchBarNodeTransition.withAnimation(.none) + searchBarNode = SearchBarNode( + theme: SearchBarNodeTheme( + background: .clear, + separator: .clear, + inputFill: .clear, + primaryText: params.theme.chat.inputPanel.panelControlColor, + placeholder: params.theme.chat.inputPanel.inputPlaceholderColor, + inputIcon: params.theme.chat.inputPanel.inputControlColor, + inputClear: params.theme.chat.inputPanel.panelControlColor, + accent: params.theme.chat.inputPanel.panelControlAccentColor, + keyboard: params.theme.rootController.keyboardColor + ), + presentationTheme: params.theme, + strings: params.strings, + fieldStyle: .inlineNavigation, + icon: .loupe, + forceSeparator: false, + displayBackground: false, + cancelText: nil + ) + searchBarNode.placeholderString = NSAttributedString(string: params.strings.Common_Search, font: Font.regular(17.0), textColor: params.theme.chat.inputPanel.inputPlaceholderColor) + self.searchBarNode = searchBarNode + self.backgroundView.contentView.addSubview(searchBarNode.view) + searchBarNode.view.alpha = 0.0 + } + let searchBarFrame = CGRect(origin: CGPoint(x: 36.0, y: 0.0), size: CGSize(width: backgroundSize.width - 36.0 - 4.0, height: params.size.height)) + transition.setFrame(view: searchBarNode.view, frame: searchBarFrame) + searchBarNode.updateLayout(boundingSize: searchBarFrame.size, leftInset: 0.0, rightInset: 0.0, transition: transition.containedViewLayoutTransition) + alphaTransition.setAlpha(view: searchBarNode.view, alpha: 1.0) + } else { + if let searchBarNode = self.searchBarNode { + self.searchBarNode = nil + let searchBarNodeView = searchBarNode.view + alphaTransition.setAlpha(view: searchBarNode.view, alpha: 0.0, completion: { [weak searchBarNodeView] _ in + searchBarNodeView?.removeFromSuperview() + }) + } + } + + if params.isActive { + let closeFrame = CGRect(origin: CGPoint(x: params.size.width - 48.0, y: 0.0), size: CGSize(width: 48.0, height: 48.0)) + + let close: (background: GlassBackgroundView, icon: UIImageView) + var closeTransition = transition + if let current = self.close { + close = current + } else { + closeTransition = closeTransition.withAnimation(.none) + close = (GlassBackgroundView(), UIImageView()) + self.close = close + + close.icon.image = generateImage(CGSize(width: 40.0, height: 40.0), contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + context.setLineWidth(2.0) + context.setLineCap(.round) + context.setStrokeColor(UIColor.white.cgColor) + + context.beginPath() + context.move(to: CGPoint(x: 12.0, y: 12.0)) + context.addLine(to: CGPoint(x: size.width - 12.0, y: size.height - 12.0)) + context.move(to: CGPoint(x: size.width - 12.0, y: 12.0)) + context.addLine(to: CGPoint(x: 12.0, y: size.height - 12.0)) + context.strokePath() + })?.withRenderingMode(.alwaysTemplate) + + close.background.contentView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.onCloseTapGesture(_:)))) + + close.background.contentView.addSubview(close.icon) + self.insertSubview(close.background, at: 0) + + if let image = close.icon.image { + close.icon.frame = image.size.centered(in: CGRect(origin: CGPoint(), size: closeFrame.size)) + } + + close.background.frame = closeFrame.size.centered(in: previousBackgroundFrame) + close.background.update(size: close.background.bounds.size, cornerRadius: close.background.bounds.height * 0.5, isDark: params.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: params.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: .immediate) + ComponentTransition.immediate.setScale(view: close.background, scale: 0.001) + } + + close.icon.tintColor = params.theme.chat.inputPanel.panelControlColor + + transition.setPosition(view: close.background, position: closeFrame.center) + transition.setBounds(view: close.background, bounds: CGRect(origin: CGPoint(), size: closeFrame.size)) + transition.setScale(view: close.background, scale: 1.0) + + if let image = close.icon.image { + transition.setFrame(view: close.icon, frame: image.size.centered(in: CGRect(origin: CGPoint(), size: closeFrame.size))) + } + + close.background.update(size: closeFrame.size, cornerRadius: closeFrame.height * 0.5, isDark: params.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: params.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: closeTransition) + } else { + if let close = self.close { + self.close = nil + let closeBackground = close.background + let closeFrame = CGSize(width: 48.0, height: 48.0).centered(in: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: params.size)) + transition.setPosition(view: closeBackground, position: closeFrame.center) + transition.setBounds(view: closeBackground, bounds: CGRect(origin: CGPoint(), size: closeFrame.size)) + closeBackground.update(size: closeFrame.size, cornerRadius: closeFrame.height * 0.5, isDark: params.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: params.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition) + transition.setScale(view: closeBackground, scale: 0.001, completion: { [weak closeBackground] _ in + closeBackground?.removeFromSuperview() + }) + } } } } @@ -131,46 +275,81 @@ public final class TabBarComponent: Component { return true } } + + public final class Search: Equatable { + public let isActive: Bool + public let activate: () -> Void + public let deactivate: () -> Void + + public init(isActive: Bool, activate: @escaping () -> Void, deactivate: @escaping () -> Void) { + self.isActive = isActive + self.activate = activate + self.deactivate = deactivate + } + + public static func ==(lhs: Search, rhs: Search) -> Bool { + if lhs.isActive != rhs.isActive { + return false + } + return true + } + } public let theme: PresentationTheme + public let strings: PresentationStrings public let items: [Item] + public let search: Search? public let selectedId: AnyHashable? - public let isTablet: Bool + public let outerInsets: UIEdgeInsets public init( theme: PresentationTheme, + strings: PresentationStrings, items: [Item], + search: Search?, selectedId: AnyHashable?, - isTablet: Bool + outerInsets: UIEdgeInsets ) { self.theme = theme + self.strings = strings self.items = items + self.search = search self.selectedId = selectedId - self.isTablet = isTablet + self.outerInsets = outerInsets } public static func ==(lhs: TabBarComponent, rhs: TabBarComponent) -> Bool { if lhs.theme !== rhs.theme { return false } + if lhs.strings !== rhs.strings { + return false + } if lhs.items != rhs.items { return false } + if lhs.search != rhs.search { + return false + } if lhs.selectedId != rhs.selectedId { return false } - if lhs.isTablet != rhs.isTablet { + if lhs.outerInsets != rhs.outerInsets { return false } return true } public final class View: UIView, UITabBarDelegate, UIGestureRecognizerDelegate { + private let backgroundContainer: GlassBackgroundContainerView private let liquidLensView: LiquidLensView private let contextGestureContainerView: ContextControllerSourceView + private var measureItemViews: [AnyHashable: ComponentView] = [:] private var itemViews: [AnyHashable: ComponentView] = [:] private var selectedItemViews: [AnyHashable: ComponentView] = [:] + + private var searchView: NavigationSearchView? private var tabSelectionRecognizer: TabSelectionRecognizer? private var itemWithActiveContextGesture: AnyHashable? @@ -178,11 +357,16 @@ public final class TabBarComponent: Component { private var component: TabBarComponent? private weak var state: EmptyComponentState? - private var selectionGestureState: (startX: CGFloat, currentX: CGFloat)? + private var selectionGestureState: (startX: CGFloat, currentX: CGFloat, itemWidth: CGFloat, itemId: AnyHashable)? private var overrideSelectedItemId: AnyHashable? + + public var currentSearchNode: ASDisplayNode? { + return self.searchView?.searchBarNode + } public override init(frame: CGRect) { - self.liquidLensView = LiquidLensView() + self.backgroundContainer = GlassBackgroundContainerView() + self.liquidLensView = LiquidLensView(kind: .externalContainer) self.contextGestureContainerView = ContextControllerSourceView() self.contextGestureContainerView.isGestureEnabled = true @@ -194,12 +378,13 @@ public final class TabBarComponent: Component { self.traitOverrides.horizontalSizeClass = .compact } - self.addSubview(self.contextGestureContainerView) + self.addSubview(self.backgroundContainer) + self.backgroundContainer.contentView.addSubview(self.contextGestureContainerView) self.contextGestureContainerView.addSubview(self.liquidLensView) let tabSelectionRecognizer = TabSelectionRecognizer(target: self, action: #selector(self.onTabSelectionGesture(_:))) self.tabSelectionRecognizer = tabSelectionRecognizer - self.addGestureRecognizer(tabSelectionRecognizer) + self.contextGestureContainerView.addGestureRecognizer(tabSelectionRecognizer) self.contextGestureContainerView.shouldBegin = { [weak self] point in guard let self, let component = self.component else { @@ -281,29 +466,41 @@ public final class TabBarComponent: Component { } @objc private func onTabSelectionGesture(_ recognizer: TabSelectionRecognizer) { + guard let component = self.component else { + return + } switch recognizer.state { case .began: - if let itemId = self.item(at: recognizer.location(in: self)), let itemView = self.itemViews[itemId]?.view { + if let search = component.search, search.isActive { + } else if let itemId = self.item(at: recognizer.location(in: self)), let itemView = self.itemViews[itemId]?.view { let startX = itemView.frame.minX - 4.0 - self.selectionGestureState = (startX, startX) + self.selectionGestureState = (startX, startX, itemView.bounds.width, itemId) self.state?.updated(transition: .spring(duration: 0.4), isLocal: true) } case .changed: - if var selectionGestureState = self.selectionGestureState { + if let search = component.search, search.isActive { + } else if var selectionGestureState = self.selectionGestureState { selectionGestureState.currentX = selectionGestureState.startX + recognizer.translation(in: self).x + if let itemId = self.item(at: recognizer.location(in: self)) { + selectionGestureState.itemId = itemId + } self.selectionGestureState = selectionGestureState self.state?.updated(transition: .immediate, isLocal: true) } case .ended, .cancelled: - self.selectionGestureState = nil - if let component = self.component, let itemId = self.item(at: recognizer.location(in: self)) { - guard let item = component.items.first(where: { $0.id == itemId }) else { - return + if let search = component.search, search.isActive { + search.deactivate() + } else if let selectionGestureState = self.selectionGestureState { + self.selectionGestureState = nil + if case .ended = recognizer.state, let component = self.component { + guard let item = component.items.first(where: { $0.id == selectionGestureState.itemId }) else { + return + } + self.overrideSelectedItemId = selectionGestureState.itemId + item.action(false) } - self.overrideSelectedItemId = itemId - item.action(false) + self.state?.updated(transition: .spring(duration: 0.4), isLocal: true) } - self.state?.updated(transition: .spring(duration: 0.4), isLocal: true) default: break } @@ -355,29 +552,91 @@ public final class TabBarComponent: Component { } func update(component: TabBarComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + let alphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.25) + let _ = alphaTransition + let innerInset: CGFloat = 4.0 let availableSize = CGSize(width: min(500.0, availableSize.width), height: availableSize.height) let previousComponent = self.component self.component = component self.state = state - - let _ = innerInset - let _ = availableSize - let _ = previousComponent self.overrideUserInterfaceStyle = component.theme.overallDarkAppearance ? .dark : .light - let itemSize = CGSize(width: floor((availableSize.width - innerInset * 2.0) / CGFloat(component.items.count)), height: 56.0) - let contentWidth: CGFloat = innerInset * 2.0 + CGFloat(component.items.count) * itemSize.width - let size = CGSize(width: min(availableSize.width, contentWidth), height: itemSize.height + innerInset * 2.0) + let barHeight: CGFloat = 56.0 + innerInset * 2.0 + var availableItemsWidth: CGFloat = availableSize.width - innerInset * 2.0 + if component.search != nil { + availableItemsWidth -= barHeight + 8.0 + } + + var unboundItemWidths: [CGFloat] = [] + var validIds: [AnyHashable] = [] - var selectionFrame: CGRect? + var unboundItemWidthSum: CGFloat = 0.0 for index in 0 ..< component.items.count { let item = component.items[index] validIds.append(item.id) + let measureItemView: ComponentView + if let current = self.measureItemViews[item.id] { + measureItemView = current + } else { + measureItemView = ComponentView() + self.measureItemViews[item.id] = measureItemView + } + + let itemSize = measureItemView.update( + transition: .immediate, + component: AnyComponent(ItemComponent( + item: item, + theme: component.theme, + isCompact: false, + isSelected: false, + isUnconstrained: true + )), + environment: {}, + containerSize: CGSize(width: 200.0, height: 56.0) + ) + + unboundItemWidths.append(itemSize.width) + unboundItemWidthSum += itemSize.width + } + + let itemWidths: [CGFloat] + let totalItemsWidth: CGFloat + + let equalWidth = floorToScreenPixels(availableItemsWidth / CGFloat(component.items.count)) + if unboundItemWidths.allSatisfy({ $0 <= equalWidth }) { + // All items fit in equal width — use equal widths for optical alignment + itemWidths = Array(repeating: equalWidth, count: component.items.count) + totalItemsWidth = equalWidth * CGFloat(component.items.count) + } else { + // Some items need more space — use weighted fit + let itemWeightNorm: CGFloat = availableItemsWidth / unboundItemWidthSum + var widths: [CGFloat] = [] + var total: CGFloat = 0.0 + for index in 0 ..< component.items.count { + let itemWidth = floorToScreenPixels(unboundItemWidths[index] * itemWeightNorm) + widths.append(itemWidth) + total += itemWidth + } + itemWidths = widths + totalItemsWidth = total + } + + let itemHeight: CGFloat = 56.0 + let contentWidth: CGFloat = innerInset * 2.0 + totalItemsWidth + let tabsSize = CGSize(width: min(availableSize.width, contentWidth), height: itemHeight + innerInset * 2.0) + + var selectionFrame: CGRect? + var nextItemX: CGFloat = innerInset + for index in 0 ..< component.items.count { + let item = component.items[index] + + let itemSize = CGSize(width: itemWidths[index], height: itemHeight) + let itemView: ComponentView var itemTransition = transition @@ -409,7 +668,9 @@ public final class TabBarComponent: Component { component: AnyComponent(ItemComponent( item: item, theme: component.theme, - isSelected: false + isCompact: component.search?.isActive == true, + isSelected: false, + isUnconstrained: false )), environment: {}, containerSize: itemSize @@ -419,14 +680,23 @@ public final class TabBarComponent: Component { component: AnyComponent(ItemComponent( item: item, theme: component.theme, - isSelected: true + isCompact: component.search?.isActive == true, + isSelected: true, + isUnconstrained: false )), environment: {}, containerSize: itemSize ) - let itemFrame = CGRect(origin: CGPoint(x: innerInset + CGFloat(index) * itemSize.width, y: floor((size.height - itemSize.height) * 0.5)), size: itemSize) + var itemFrame = CGRect(origin: CGPoint(x: nextItemX, y: floor((tabsSize.height - itemSize.height) * 0.5)), size: itemSize) + nextItemX += itemSize.width + if isItemSelected { + selectionFrame = itemFrame + } + if let itemComponentView = itemView.view as? ItemComponent.View, let selectedItemComponentView = selectedItemView.view as? ItemComponent.View { + let itemAlphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.25) + if itemComponentView.superview == nil { itemComponentView.isUserInteractionEnabled = false selectedItemComponentView.isUserInteractionEnabled = false @@ -434,6 +704,27 @@ public final class TabBarComponent: Component { self.liquidLensView.contentView.addSubview(itemComponentView) self.liquidLensView.selectedContentView.addSubview(selectedItemComponentView) } + + if let search = component.search, search.isActive { + if isItemSelected { + itemFrame.origin.x = floor((48.0 - itemSize.width) * 0.5) + itemTransition.setAlpha(view: itemComponentView, alpha: 1.0) + itemAlphaTransition.setBlur(layer: itemComponentView.layer, radius: 0.0) + itemTransition.setAlpha(view: selectedItemComponentView, alpha: 1.0) + itemAlphaTransition.setBlur(layer: selectedItemComponentView.layer, radius: 0.0) + } else { + itemTransition.setAlpha(view: itemComponentView, alpha: 0.0) + itemAlphaTransition.setBlur(layer: itemComponentView.layer, radius: 10.0) + itemTransition.setAlpha(view: selectedItemComponentView, alpha: 0.0) + itemAlphaTransition.setBlur(layer: selectedItemComponentView.layer, radius: 10.0) + } + } else { + itemTransition.setAlpha(view: itemComponentView, alpha: 1.0) + itemAlphaTransition.setBlur(layer: itemComponentView.layer, radius: 0.0) + itemTransition.setAlpha(view: selectedItemComponentView, alpha: 1.0) + itemAlphaTransition.setBlur(layer: selectedItemComponentView.layer, radius: 0.0) + } + itemTransition.setFrame(view: itemComponentView, frame: itemFrame) itemTransition.setPosition(view: selectedItemComponentView, position: itemFrame.center) itemTransition.setBounds(view: selectedItemComponentView, bounds: CGRect(origin: CGPoint(), size: itemFrame.size)) @@ -444,9 +735,6 @@ public final class TabBarComponent: Component { selectedItemComponentView.playSelectionAnimation() } } - if isItemSelected { - selectionFrame = itemFrame - } } var removeIds: [AnyHashable] = [] @@ -460,22 +748,95 @@ public final class TabBarComponent: Component { for id in removeIds { self.itemViews.removeValue(forKey: id) self.selectedItemViews.removeValue(forKey: id) + self.measureItemViews.removeValue(forKey: id) } - - transition.setFrame(view: self.contextGestureContainerView, frame: CGRect(origin: CGPoint(), size: size)) - - transition.setFrame(view: self.liquidLensView, frame: CGRect(origin: CGPoint(), size: size)) - let lensSelection: (x: CGFloat, width: CGFloat) - if let selectionGestureState = self.selectionGestureState { - lensSelection = (selectionGestureState.currentX, itemSize.width + innerInset * 2.0) - } else if let selectionFrame { - lensSelection = (selectionFrame.minX - innerInset, itemSize.width + innerInset * 2.0) - } else { - lensSelection = (0.0, itemSize.width) + var tabsFrame = CGRect(origin: CGPoint(), size: tabsSize) + if let search = component.search, search.isActive { + tabsFrame.size = CGSize(width: 48.0, height: 48.0) + tabsFrame.origin.y = tabsSize.height - 48.0 + tabsFrame.origin.x = -component.outerInsets.left - tabsFrame.width } - self.liquidLensView.update(size: size, selectionX: lensSelection.x, selectionWidth: lensSelection.width, isDark: component.theme.overallDarkAppearance, isLifted: self.selectionGestureState != nil, transition: transition) + transition.setFrame(view: self.contextGestureContainerView, frame: tabsFrame) + transition.setFrame(view: self.liquidLensView, frame: CGRect(origin: CGPoint(), size: tabsSize)) + + var lensSelection: (x: CGFloat, width: CGFloat) + if let selectionGestureState = self.selectionGestureState { + lensSelection = (selectionGestureState.currentX, selectionGestureState.itemWidth + innerInset * 2.0) + } else if let selectionFrame { + lensSelection = (selectionFrame.minX - innerInset, selectionFrame.width + innerInset * 2.0) + } else { + lensSelection = (0.0, 56.0) + } + + var lensSize: CGSize = tabsSize + var isLensCollapsed = false + if let search = component.search, search.isActive { + isLensCollapsed = true + lensSize = CGSize(width: 48.0, height: 48.0) + lensSelection = (0.0, 48.0) + } + + lensSelection.x = max(0.0, min(lensSelection.x, lensSize.width - lensSelection.width)) + + self.liquidLensView.update(size: lensSize, selectionOrigin: CGPoint(x: lensSelection.x, y: 0.0), selectionSize: CGSize(width: lensSelection.width, height: lensSize.height), inset: 4.0, isDark: component.theme.overallDarkAppearance, isLifted: self.selectionGestureState != nil, isCollapsed: isLensCollapsed, transition: transition) + + var size = tabsSize + + if let search = component.search { + let searchSize: CGSize + let searchFrame: CGRect + if search.isActive { + size.width = availableSize.width + searchSize = CGSize(width: availableSize.width, height: 48.0) + searchFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - searchSize.height), size: searchSize) + } else { + searchSize = CGSize(width: barHeight, height: barHeight) + size.width += barHeight + 8.0 + searchFrame = CGRect(origin: CGPoint(x: availableSize.width - searchSize.width, y: 0.0), size: searchSize) + } + + let searchView: NavigationSearchView + var searchViewTransition = transition + if let current = self.searchView { + searchView = current + } else { + searchViewTransition = searchViewTransition.withAnimation(.none) + searchView = NavigationSearchView( + action: { [weak self] in + guard let self, let component = self.component else { + return + } + component.search?.activate() + }, + closeAction: { [weak self] in + guard let self, let component = self.component else { + return + } + component.search?.deactivate() + } + ) + self.searchView = searchView + self.backgroundContainer.contentView.addSubview(searchView) + searchView.frame = CGRect(origin: CGPoint(x: availableSize.width + 50.0, y: 0.0), size: searchSize) + } + searchView.update(size: searchSize, theme: component.theme, strings: component.strings, isActive: search.isActive, transition: searchViewTransition) + transition.setFrame(view: searchView, frame: searchFrame) + } else { + if let searchView = self.searchView { + self.searchView = nil + transition.setFrame(view: searchView, frame: CGRect(origin: CGPoint(x: availableSize.width + 50.0, y: 0.0), size: searchView.bounds.size), completion: { [weak searchView] completed in + guard let searchView, completed else { + return + } + searchView.removeFromSuperview() + }) + } + } + + transition.setFrame(view: self.backgroundContainer, frame: CGRect(origin: CGPoint(), size: size)) + self.backgroundContainer.update(size: size, isDark: component.theme.overallDarkAppearance, transition: transition) return size } @@ -493,12 +854,16 @@ public final class TabBarComponent: Component { private final class ItemComponent: Component { let item: TabBarComponent.Item let theme: PresentationTheme + let isCompact: Bool let isSelected: Bool + let isUnconstrained: Bool - init(item: TabBarComponent.Item, theme: PresentationTheme, isSelected: Bool) { + init(item: TabBarComponent.Item, theme: PresentationTheme, isCompact: Bool, isSelected: Bool, isUnconstrained: Bool) { self.item = item self.theme = theme + self.isCompact = isCompact self.isSelected = isSelected + self.isUnconstrained = isUnconstrained } static func ==(lhs: ItemComponent, rhs: ItemComponent) -> Bool { @@ -508,9 +873,15 @@ private final class ItemComponent: Component { if lhs.theme !== rhs.theme { return false } + if lhs.isCompact != rhs.isCompact { + return false + } if lhs.isSelected != rhs.isSelected { return false } + if lhs.isUnconstrained != rhs.isUnconstrained { + return false + } return true } @@ -562,6 +933,8 @@ private final class ItemComponent: Component { } func update(component: ItemComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + let alphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.25) + let previousComponent = self.component if previousComponent?.item.item !== component.item.item { @@ -619,7 +992,7 @@ private final class ItemComponent: Component { content: LottieComponent.AppBundleContent( name: animationName ), - color: component.isSelected ? component.theme.rootController.tabBar.selectedTextColor : component.theme.rootController.tabBar.textColor, + color: (component.isSelected && !component.isCompact) ? component.theme.rootController.tabBar.selectedTextColor : component.theme.rootController.tabBar.textColor, placeholderColor: nil, startingPosition: .end, size: CGSize(width: 48.0, height: 48.0), @@ -692,6 +1065,7 @@ private final class ItemComponent: Component { self.contextContainerView.contentView.addSubview(titleView) } titleView.frame = titleFrame + alphaTransition.setAlpha(view: titleView, alpha: component.isCompact ? 0.0 : 1.0) } if let badgeText = component.item.item.badgeValue, !badgeText.isEmpty { @@ -711,7 +1085,7 @@ private final class ItemComponent: Component { font: Font.regular(13.0), background: component.theme.rootController.tabBar.badgeBackgroundColor, foreground: component.theme.rootController.tabBar.badgeTextColor, - insets: UIEdgeInsets(top: 0.0, left: 6.0, bottom: 1.0, right: 6.0) + insets: UIEdgeInsets(top: 0.0, left: 5.0, bottom: 1.0, right: 5.0) )), environment: {}, containerSize: CGSize(width: 100.0, height: 100.0) @@ -723,6 +1097,7 @@ private final class ItemComponent: Component { self.contextContainerView.contentView.addSubview(badgeView) } badgeTransition.setFrame(view: badgeView, frame: badgeFrame) + alphaTransition.setAlpha(view: badgeView, alpha: component.isCompact ? 0.0 : 1.0) } } else if let badge = self.badge { self.badge = nil @@ -733,7 +1108,11 @@ private final class ItemComponent: Component { transition.setFrame(view: self.contextContainerView.contentView, frame: CGRect(origin: CGPoint(), size: availableSize)) self.contextContainerView.contentRect = CGRect(origin: CGPoint(), size: availableSize) - return availableSize + if component.isUnconstrained { + return CGSize(width: titleSize.width + 10.0 * 2.0, height: availableSize.height) + } else { + return availableSize + } } } diff --git a/submodules/TelegramUI/Components/TabSelectionRecognizer/BUILD b/submodules/TelegramUI/Components/TabSelectionRecognizer/BUILD new file mode 100644 index 00000000..b3d29529 --- /dev/null +++ b/submodules/TelegramUI/Components/TabSelectionRecognizer/BUILD @@ -0,0 +1,17 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "TabSelectionRecognizer", + module_name = "TabSelectionRecognizer", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/TabSelectionRecognizer/Sources/TabSelectionRecognizer.swift b/submodules/TelegramUI/Components/TabSelectionRecognizer/Sources/TabSelectionRecognizer.swift new file mode 100644 index 00000000..e67f626e --- /dev/null +++ b/submodules/TelegramUI/Components/TabSelectionRecognizer/Sources/TabSelectionRecognizer.swift @@ -0,0 +1,58 @@ +import Foundation +import UIKit + +public final class TabSelectionRecognizer: UIGestureRecognizer { + private var initialLocation: CGPoint? + private var currentLocation: CGPoint? + + public override init(target: Any?, action: Selector?) { + super.init(target: target, action: action) + + self.delaysTouchesBegan = false + self.delaysTouchesEnded = false + } + + public override func reset() { + super.reset() + + self.initialLocation = nil + } + + public override func touchesBegan(_ touches: Set, with event: UIEvent) { + super.touchesBegan(touches, with: event) + + if self.initialLocation == nil { + self.initialLocation = touches.first?.location(in: self.view) + } + self.currentLocation = self.initialLocation + + self.state = .began + } + + public override func touchesEnded(_ touches: Set, with event: UIEvent) { + super.touchesEnded(touches, with: event) + + self.state = .ended + } + + public override func touchesCancelled(_ touches: Set, with event: UIEvent) { + super.touchesCancelled(touches, with: event) + + self.state = .cancelled + } + + public override func touchesMoved(_ touches: Set, with event: UIEvent) { + super.touchesMoved(touches, with: event) + + self.currentLocation = touches.first?.location(in: self.view) + + self.state = .changed + } + + public func translation(in: UIView?) -> CGPoint { + if let initialLocation = self.initialLocation, let currentLocation = self.currentLocation { + return CGPoint(x: currentLocation.x - initialLocation.x, y: currentLocation.y - initialLocation.y) + } + return CGPoint() + } +} diff --git a/submodules/TelegramUI/Components/TabSelectorComponent/Sources/TabSelectorComponent.swift b/submodules/TelegramUI/Components/TabSelectorComponent/Sources/TabSelectorComponent.swift index 7d93cb30..242c4f2f 100644 --- a/submodules/TelegramUI/Components/TabSelectorComponent/Sources/TabSelectorComponent.swift +++ b/submodules/TelegramUI/Components/TabSelectorComponent/Sources/TabSelectorComponent.swift @@ -65,8 +65,9 @@ public final class TabSelectorComponent: Component { public var lineSelection: Bool public var verticalInset: CGFloat public var allowScroll: Bool + public var height: CGFloat? - public init(font: UIFont, spacing: CGFloat = 2.0, innerSpacing: CGFloat? = nil, fillWidth: Bool = false, lineSelection: Bool = false, verticalInset: CGFloat = 0.0, allowScroll: Bool = true) { + public init(font: UIFont, spacing: CGFloat = 2.0, innerSpacing: CGFloat? = nil, fillWidth: Bool = false, lineSelection: Bool = false, verticalInset: CGFloat = 0.0, allowScroll: Bool = true, height: CGFloat? = nil) { self.font = font self.spacing = spacing self.innerSpacing = innerSpacing @@ -74,6 +75,7 @@ public final class TabSelectorComponent: Component { self.lineSelection = lineSelection self.verticalInset = verticalInset self.allowScroll = allowScroll + self.height = height } } @@ -531,14 +533,17 @@ public final class TabSelectorComponent: Component { self.reorderRecognizer?.isEnabled = component.reorderItem != nil let baseHeight: CGFloat - switch component.style { - case .glass: - baseHeight = 32.0 - case .legacy: - baseHeight = 28.0 + if let customLayout = component.customLayout, let height = customLayout.height { + baseHeight = height + } else { + switch component.style { + case .glass: + baseHeight = 32.0 + case .legacy: + baseHeight = 28.0 + } } - var verticalInset: CGFloat = 0.0 if let customLayout = component.customLayout { verticalInset = customLayout.verticalInset * 2.0 @@ -632,7 +637,7 @@ public final class TabSelectorComponent: Component { if case .component = item.content { useSelectionFraction = true } - if let _ = component.colors.normal { + if let normal = component.colors.normal, normal != component.colors.foreground { useSelectionFraction = true } diff --git a/submodules/TelegramUI/Components/TextFieldComponent/BUILD b/submodules/TelegramUI/Components/TextFieldComponent/BUILD index 4d16bc5b..5a8eba3a 100644 --- a/submodules/TelegramUI/Components/TextFieldComponent/BUILD +++ b/submodules/TelegramUI/Components/TextFieldComponent/BUILD @@ -18,7 +18,6 @@ swift_library( "//submodules/AccountContext", "//submodules/InvisibleInkDustNode", "//submodules/TelegramUI/Components/EmojiTextAttachmentView", - "//submodules/ChatTextLinkEditUI", "//submodules/Pasteboard", "//submodules/ImageTransparency", "//submodules/TelegramUI/Components/Chat/ChatInputTextNode", diff --git a/submodules/TelegramUI/Components/TextFieldComponent/Sources/TextFieldComponent.swift b/submodules/TelegramUI/Components/TextFieldComponent/Sources/TextFieldComponent.swift index 3b76429a..ac25591b 100644 --- a/submodules/TelegramUI/Components/TextFieldComponent/Sources/TextFieldComponent.swift +++ b/submodules/TelegramUI/Components/TextFieldComponent/Sources/TextFieldComponent.swift @@ -10,7 +10,6 @@ import EmojiTextAttachmentView import AccountContext import TextFormat import Pasteboard -import ChatTextLinkEditUI import MobileCoreServices import ImageTransparency import ChatInputTextNode @@ -161,6 +160,9 @@ public final class TextFieldComponent: Component { public let externalHandlingForMultilinePaste: Bool public let formatMenuAvailability: FormatMenuAvailability public let returnKeyType: UIReturnKeyType + public let keyboardType: UIKeyboardType + public let autocapitalizationType: UITextAutocapitalizationType + public let autocorrectionType: UITextAutocorrectionType public let lockedFormatAction: () -> Void public let present: (ViewController) -> Void public let paste: (PasteData) -> Void @@ -188,6 +190,9 @@ public final class TextFieldComponent: Component { externalHandlingForMultilinePaste: Bool = false, formatMenuAvailability: FormatMenuAvailability, returnKeyType: UIReturnKeyType = .default, + keyboardType: UIKeyboardType = .default, + autocapitalizationType: UITextAutocapitalizationType = .sentences, + autocorrectionType: UITextAutocorrectionType = .default, lockedFormatAction: @escaping () -> Void, present: @escaping (ViewController) -> Void, paste: @escaping (PasteData) -> Void, @@ -214,11 +219,14 @@ public final class TextFieldComponent: Component { self.externalHandlingForMultilinePaste = externalHandlingForMultilinePaste self.formatMenuAvailability = formatMenuAvailability self.returnKeyType = returnKeyType + self.keyboardType = keyboardType self.lockedFormatAction = lockedFormatAction self.present = present self.paste = paste self.returnKeyAction = returnKeyAction self.backspaceKeyAction = backspaceKeyAction + self.autocapitalizationType = autocapitalizationType + self.autocorrectionType = autocorrectionType } public static func ==(lhs: TextFieldComponent, rhs: TextFieldComponent) -> Bool { @@ -282,6 +290,15 @@ public final class TextFieldComponent: Component { if lhs.returnKeyType != rhs.returnKeyType { return false } + if lhs.keyboardType != rhs.keyboardType { + return false + } + if lhs.autocapitalizationType != rhs.autocapitalizationType { + return false + } + if lhs.autocorrectionType != rhs.autocorrectionType { + return false + } return true } @@ -409,7 +426,7 @@ public final class TextFieldComponent: Component { let inputState = f(self.inputState) let currentAttributedText = self.textView.attributedText - let updatedAttributedText = textAttributedStringForStateText(context: component.context, stateText: inputState.inputText, fontSize: component.fontSize, textColor: component.textColor, accentTextColor: component.accentColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(component.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in + let updatedAttributedText = textAttributedStringForStateText(context: component.context, stateText: inputState.inputText, fontSize: component.fontSize, textColor: component.textColor, accentTextColor: component.accentColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed, availableEmojis: Set(component.context.animatedEmojiStickersValue.keys), emojiViewProvider: self.emojiViewProvider, makeCollapsedQuoteAttachment: { text, attributes in return ChatInputTextCollapsedQuoteAttachmentImpl(text: text, attributes: attributes) }) if currentAttributedText != updatedAttributedText { @@ -986,7 +1003,7 @@ public final class TextFieldComponent: Component { let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: component.theme) let updatedPresentationData: (initial: PresentationData, signal: Signal) = (presentationData, .single(presentationData)) - let controller = chatTextLinkEditController(sharedContext: component.context.sharedContext, updatedPresentationData: updatedPresentationData, account: component.context.account, text: text.string, link: link, allowEmpty: true, apply: { [weak self] link in + let controller = component.context.sharedContext.makeLinkEditController(context: component.context, updatedPresentationData: updatedPresentationData, text: text.string, link: link, apply: { [weak self] link in if let self { if let link { if !link.isEmpty { @@ -1388,6 +1405,15 @@ public final class TextFieldComponent: Component { if self.textView.returnKeyType != component.returnKeyType { self.textView.returnKeyType = component.returnKeyType } + if self.textView.keyboardType != component.keyboardType { + self.textView.keyboardType = component.keyboardType + } + if self.textView.autocapitalizationType != component.autocapitalizationType { + self.textView.autocapitalizationType = component.autocapitalizationType + } + if self.textView.autocorrectionType != component.autocorrectionType { + self.textView.autocorrectionType = component.autocorrectionType + } if let initialText = component.externalState.initialText { component.externalState.initialText = nil diff --git a/submodules/TelegramUI/Components/TextNodeWithEntities/Sources/TextNodeWithEntities.swift b/submodules/TelegramUI/Components/TextNodeWithEntities/Sources/TextNodeWithEntities.swift index 9622b268..cb5f59f8 100644 --- a/submodules/TelegramUI/Components/TextNodeWithEntities/Sources/TextNodeWithEntities.swift +++ b/submodules/TelegramUI/Components/TextNodeWithEntities/Sources/TextNodeWithEntities.swift @@ -106,7 +106,12 @@ public final class TextNodeWithEntities { public let textNode: TextNode private var inlineStickerItemLayers: [InlineStickerItemLayer.Key: InlineStickerItemLayer] = [:] - private var enableLooping: Bool = true + public var enableLooping: Bool = true + public var energySavingEnableLooping: Bool = true + + private var effectiveEnableLooping: Bool { + return self.enableLooping && self.energySavingEnableLooping + } public var resetEmojiToFirstFrameAutomatically: Bool = false @@ -124,7 +129,7 @@ public final class TextNodeWithEntities { } else { isItemVisible = false } - let isVisibleForAnimations = self.enableLooping && isItemVisible && itemLayer.enableAnimation + let isVisibleForAnimations = self.effectiveEnableLooping && isItemVisible && itemLayer.enableAnimation if itemLayer.isVisibleForAnimations != isVisibleForAnimations { itemLayer.isVisibleForAnimations = isVisibleForAnimations if !isVisibleForAnimations && self.resetEmojiToFirstFrameAutomatically { @@ -257,7 +262,7 @@ public final class TextNodeWithEntities { } private func updateInlineStickers(context: AccountContext, cache: AnimationCache, renderer: MultiAnimationRenderer, textLayout: TextNodeLayout?, placeholderColor: UIColor, attemptSynchronousLoad: Bool, emojiOffset: CGPoint, fontSizeNorm: CGFloat) { - self.enableLooping = context.sharedContext.energyUsageSettings.loopEmoji + self.energySavingEnableLooping = context.sharedContext.energyUsageSettings.loopEmoji var nextIndexById: [Int64: Int] = [:] var validIds: [InlineStickerItemLayer.Key] = [] @@ -292,7 +297,7 @@ public final class TextNodeWithEntities { self.textNode.layer.addSublayer(itemLayer) } itemLayer.enableAnimation = stickerItem.enableAnimation - let isVisibleForAnimations = self.enableLooping && self.isItemVisible(itemRect: itemFrame) && itemLayer.enableAnimation + let isVisibleForAnimations = self.effectiveEnableLooping && self.isItemVisible(itemRect: itemFrame) && itemLayer.enableAnimation if itemLayer.isVisibleForAnimations != isVisibleForAnimations { if !isVisibleForAnimations && self.resetEmojiToFirstFrameAutomatically { itemLayer.reloadAnimation() @@ -410,7 +415,12 @@ public class ImmediateTextNodeWithEntities: TextNode { public var spoilerColor: UIColor = .black public var balancedTextLayout: Bool = false - private var enableLooping: Bool = true + public var enableLooping: Bool = true + public var energySavingEnableLooping: Bool = true + + private var effectiveEnableLooping: Bool { + return self.enableLooping && self.energySavingEnableLooping + } public var arguments: TextNodeWithEntities.Arguments? @@ -423,7 +433,7 @@ public class ImmediateTextNodeWithEntities: TextNode { didSet { if !self.inlineStickerItemLayers.isEmpty && oldValue != self.visibility { for (_, itemLayer) in self.inlineStickerItemLayers { - let isVisibleForAnimations = self.enableLooping && self.visibility && itemLayer.enableAnimation + let isVisibleForAnimations = self.effectiveEnableLooping && self.visibility && itemLayer.enableAnimation if itemLayer.isVisibleForAnimations != isVisibleForAnimations { itemLayer.isVisibleForAnimations = isVisibleForAnimations if !isVisibleForAnimations && self.resetEmojiToFirstFrameAutomatically { @@ -575,7 +585,7 @@ public class ImmediateTextNodeWithEntities: TextNode { } private func updateInlineStickers(context: AccountContext, cache: AnimationCache, renderer: MultiAnimationRenderer, textLayout: TextNodeLayout?, placeholderColor: UIColor, fontSizeNorm: CGFloat) { - self.enableLooping = context.sharedContext.energyUsageSettings.loopEmoji + self.energySavingEnableLooping = context.sharedContext.energyUsageSettings.loopEmoji var nextIndexById: [Int64: Int] = [:] var validIds: [InlineStickerItemLayer.Key] = [] @@ -613,7 +623,7 @@ public class ImmediateTextNodeWithEntities: TextNode { } itemLayer.enableAnimation = stickerItem.enableAnimation - let isVisibleForAnimations = self.enableLooping && self.visibility && itemLayer.enableAnimation + let isVisibleForAnimations = self.effectiveEnableLooping && self.visibility && itemLayer.enableAnimation if itemLayer.isVisibleForAnimations != isVisibleForAnimations { itemLayer.isVisibleForAnimations = isVisibleForAnimations if !isVisibleForAnimations && self.resetEmojiToFirstFrameAutomatically { diff --git a/submodules/TelegramUI/Components/TokenListTextField/Sources/EditableTokenListNode.swift b/submodules/TelegramUI/Components/TokenListTextField/Sources/EditableTokenListNode.swift index 1b0f2749..03e2207b 100644 --- a/submodules/TelegramUI/Components/TokenListTextField/Sources/EditableTokenListNode.swift +++ b/submodules/TelegramUI/Components/TokenListTextField/Sources/EditableTokenListNode.swift @@ -339,8 +339,6 @@ final class EditableTokenListNode: ASDisplayNode, UITextFieldDelegate { self.scrollNode.addSubnode(self.placeholderNode) self.scrollNode.addSubnode(self.textFieldScrollNode) self.textFieldScrollNode.addSubnode(self.textFieldNode) - //self.scrollNode.addSubnode(self.caretIndicatorNode) - self.clipsToBounds = true self.textFieldNode.textField.delegate = self self.textFieldNode.textField.addTarget(self, action: #selector(self.textFieldChanged(_:)), for: .editingChanged) @@ -383,7 +381,6 @@ final class EditableTokenListNode: ASDisplayNode, UITextFieldDelegate { let sideInset: CGFloat = 12.0 + leftInset let verticalInset: CGFloat = 6.0 - var animationDelay = 0.0 var currentOffset = CGPoint(x: sideInset, y: verticalInset) for token in tokens { diff --git a/submodules/TelegramUI/Components/TranslateHeaderPanelComponent/BUILD b/submodules/TelegramUI/Components/TranslateHeaderPanelComponent/BUILD new file mode 100644 index 00000000..852662d5 --- /dev/null +++ b/submodules/TelegramUI/Components/TranslateHeaderPanelComponent/BUILD @@ -0,0 +1,34 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "TranslateHeaderPanelComponent", + module_name = "TranslateHeaderPanelComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Display", + "//submodules/AsyncDisplayKit", + "//submodules/TelegramPresentationData", + "//submodules/AccountContext", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/Postbox", + "//submodules/TelegramCore", + "//submodules/LocalizedPeerData", + "//submodules/TelegramStringFormatting", + "//submodules/TextFormat", + "//submodules/Markdown", + "//submodules/MoreButtonNode", + "//submodules/ContextUI", + "//submodules/TranslateUI", + "//submodules/TelegramUIPreferences", + "//submodules/TelegramNotices", + "//submodules/PremiumUI", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/TranslateHeaderPanelComponent/Sources/ChatTranslationPanelNode.swift b/submodules/TelegramUI/Components/TranslateHeaderPanelComponent/Sources/ChatTranslationPanelNode.swift new file mode 100644 index 00000000..42b05c81 --- /dev/null +++ b/submodules/TelegramUI/Components/TranslateHeaderPanelComponent/Sources/ChatTranslationPanelNode.swift @@ -0,0 +1,1112 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import SwiftSignalKit +import Postbox +import TelegramCore +import TelegramPresentationData +import LocalizedPeerData +import TelegramStringFormatting +import TextFormat +import Markdown +import AccountContext +import MoreButtonNode +import ContextUI +import TranslateUI +import TelegramUIPreferences +import TelegramNotices +import PremiumUI +import ComponentFlow +import ComponentDisplayAdapters +import LocalMediaResources +import AppBundle + +final class ChatTranslationPanelNode: ASDisplayNode { + private let context: AccountContext + private let close: () -> Void + private let toggle: () -> Void + private let controller: () -> ViewController? + private let changeLanguage: (String) -> Void + private let addDoNotTranslateLanguage: (String) -> Void + + private let button: HighlightableButtonNode + private let buttonIconNode: ASImageNode + private let buttonTextNode: ImmediateTextNode + private let moreButton: MoreButtonNode + private let closeButton: HighlightableButtonNode + + private var theme: PresentationTheme? + + private var currentInfo: TranslateHeaderPanelComponent.Info? + + init(context: AccountContext, close: @escaping () -> Void, toggle: @escaping () -> Void, changeLanguage: @escaping (String) -> Void, addDoNotTranslateLanguage: @escaping (String) -> Void, controller: @escaping () -> ViewController?) { + self.context = context + self.close = close + self.toggle = toggle + self.changeLanguage = changeLanguage + self.addDoNotTranslateLanguage = addDoNotTranslateLanguage + self.controller = controller + + self.button = HighlightableButtonNode() + self.buttonIconNode = ASImageNode() + self.buttonIconNode.displaysAsynchronously = false + + self.buttonTextNode = ImmediateTextNode() + self.buttonTextNode.displaysAsynchronously = false + + let theme: PresentationTheme = context.sharedContext.currentPresentationData.with { $0 }.theme + self.moreButton = MoreButtonNode(theme: theme) + self.moreButton.updateColor(theme.chat.inputPanel.panelControlColor, transition: .immediate) + self.moreButton.iconNode.enqueueState(.more, animated: false) + self.moreButton.hitTestSlop = UIEdgeInsets(top: -8.0, left: -8.0, bottom: -8.0, right: -8.0) + + self.closeButton = HighlightableButtonNode() + self.closeButton.hitTestSlop = UIEdgeInsets(top: -8.0, left: -8.0, bottom: -8.0, right: -8.0) + self.closeButton.displaysAsynchronously = false + + super.init() + + self.clipsToBounds = true + + self.addSubnode(self.button) + self.addSubnode(self.moreButton) + + self.button.addSubnode(self.buttonIconNode) + self.button.addSubnode(self.buttonTextNode) + + self.button.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: [.touchUpInside]) + self.moreButton.action = { [weak self] _, gesture in + if let strongSelf = self { + strongSelf.morePressed(node: strongSelf.moreButton.contextSourceNode, gesture: gesture) + } + } + + self.closeButton.addTarget(self, action: #selector(self.closePressed), forControlEvents: [.touchUpInside]) + self.addSubnode(self.closeButton) + } + + func animateOut() { + self.layer.animateBounds(from: self.bounds, to: self.bounds.offsetBy(dx: 0.0, dy: self.bounds.size.height), duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) + } + + func updateLayout( + width: CGFloat, + info: TranslateHeaderPanelComponent.Info, + theme: PresentationTheme, + strings: PresentationStrings, + transition: ContainedViewLayoutTransition + ) -> CGFloat { + let leftInset: CGFloat = 0.0 + let rightInset: CGFloat = 0.0 + + let previousInfo = self.currentInfo + self.currentInfo = info + + var themeUpdated = false + if theme !== self.theme { + themeUpdated = true + self.theme = theme + } + + if themeUpdated { + self.buttonIconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Title Panels/Translate"), color: theme.chat.inputPanel.panelControlColor) + self.moreButton.theme = theme + self.moreButton.updateColor(theme.chat.inputPanel.panelControlColor, transition: .immediate) + self.closeButton.setImage(generateImage(CGSize(width: 12.0, height: 12.0), contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setStrokeColor(theme.chat.inputPanel.panelControlColor.cgColor) + context.setLineWidth(1.33) + context.setLineCap(.round) + context.move(to: CGPoint(x: 1.0, y: 1.0)) + context.addLine(to: CGPoint(x: size.width - 1.0, y: size.height - 1.0)) + context.strokePath() + context.move(to: CGPoint(x: size.width - 1.0, y: 1.0)) + context.addLine(to: CGPoint(x: 1.0, y: size.height - 1.0)) + context.strokePath() + }), for: []) + } + + var textUpdated = false + if themeUpdated || previousInfo?.isActive != info.isActive { + var languageCode = strings.baseLanguageCode + let rawSuffix = "-raw" + if languageCode.hasSuffix(rawSuffix) { + languageCode = String(languageCode.dropLast(rawSuffix.count)) + } + + let toLang = info.toLang ?? languageCode + let key = "Translation.Language.\(toLang)" + let translateTitle: String + if let string = strings.primaryComponent.dict[key] { + translateTitle = strings.Conversation_Translation_TranslateTo(string).string + } else { + let languageLocale = Locale(identifier: languageCode) + let toLanguage = languageLocale.localizedString(forLanguageCode: toLang) ?? "" + translateTitle = strings.Conversation_Translation_TranslateToOther(toLanguage).string + } + + let buttonText = info.isActive ? strings.Conversation_Translation_ShowOriginal : translateTitle + if self.buttonTextNode.attributedText?.string != buttonText { + textUpdated = true + } + self.buttonTextNode.attributedText = NSAttributedString(string: buttonText, font: Font.regular(17.0), textColor: theme.chat.inputPanel.panelControlColor) + } + + let panelHeight: CGFloat = 40.0 + + let contentRightInset: CGFloat = 11.0 + rightInset + + var copyTextView: UIView? + if textUpdated, transition.isAnimated { + if let copyView = self.buttonTextNode.layer.snapshotContentTreeAsView(unhide: false) { + copyTextView = copyView + self.buttonTextNode.view.superview?.insertSubview(copyView, belowSubview: self.buttonTextNode.view) + transition.updateAlpha(layer: copyView.layer, alpha: 0.0, completion: { [weak copyView] _ in + copyView?.removeFromSuperview() + }) + ComponentTransition(transition).setBlur(layer: copyView.layer, radius: 8.0) + + ComponentTransition(transition).animateBlur(layer: self.buttonTextNode.layer, fromRadius: 8.0, toRadius: 0.0) + self.buttonTextNode.alpha = 0.0 + transition.updateAlpha(layer: self.buttonTextNode.layer, alpha: 1.0) + } + } + + let moreButtonSize = self.moreButton.measure(CGSize(width: 100.0, height: panelHeight)) + transition.updateFrame(node: self.moreButton, frame: CGRect(origin: CGPoint(x: width - contentRightInset - moreButtonSize.width, y: floorToScreenPixels((panelHeight - moreButtonSize.height) / 2.0) - 1.0), size: moreButtonSize)) + + let closeButtonSize = self.closeButton.measure(CGSize(width: 100.0, height: 100.0)) + self.closeButton.frame = CGRect(origin: CGPoint(x: width - contentRightInset - closeButtonSize.width, y: floorToScreenPixels((panelHeight - closeButtonSize.height) / 2.0)), size: closeButtonSize) + + if info.isPremium { + self.moreButton.isHidden = false + self.closeButton.isHidden = true + } else { + self.moreButton.isHidden = true + self.closeButton.isHidden = false + } + + let buttonPadding: CGFloat = 10.0 + let buttonSpacing: CGFloat = 10.0 + let buttonTextSize = self.buttonTextNode.updateLayout(CGSize(width: width - contentRightInset - moreButtonSize.width, height: panelHeight)) + if let icon = self.buttonIconNode.image { + let buttonSize = CGSize(width: buttonTextSize.width + icon.size.width + buttonSpacing + buttonPadding * 2.0, height: panelHeight) + transition.updateFrame(node: self.button, frame: CGRect(origin: CGPoint(x: leftInset + floorToScreenPixels((width - leftInset - rightInset - buttonSize.width) / 2.0), y: 0.0), size: buttonSize)) + + transition.updateFrame(node: self.buttonIconNode, frame: CGRect(origin: CGPoint(x: buttonPadding, y: floorToScreenPixels((buttonSize.height - icon.size.height) / 2.0)), size: icon.size)) + + let buttonTextFrame = CGRect(origin: CGPoint(x: buttonPadding + icon.size.width + buttonSpacing, y: floorToScreenPixels((buttonSize.height - buttonTextSize.height) / 2.0)), size: buttonTextSize) + transition.updatePosition(node: self.buttonTextNode, position: buttonTextFrame.center) + if let copyTextView { + transition.updatePosition(layer: copyTextView.layer, position: buttonTextFrame.center) + } + self.buttonTextNode.bounds = CGRect(origin: CGPoint(), size: buttonTextFrame.size) + } + + return panelHeight + } + + @objc private func closePressed() { + guard let info = self.currentInfo else { + return + } + let isPremium = info.isPremium + + var translationAvailable = isPremium + if case let .channel(channel) = info.peer, channel.flags.contains(.autoTranslateEnabled) { + translationAvailable = true + } + + if translationAvailable { + self.close() + } else if !isPremium { + let _ = ApplicationSpecificNotice.incrementTranslationSuggestion(accountManager: self.context.sharedContext.accountManager, count: -100, timestamp: Int32(Date().timeIntervalSince1970) + 60 * 60 * 24 * 7).startStandalone() + } + } + + @objc private func buttonPressed() { + guard let info = self.currentInfo else { + return + } + + let isPremium = info.isPremium + + var translationAvailable = isPremium + if case let .channel(channel) = info.peer, channel.flags.contains(.autoTranslateEnabled) { + translationAvailable = true + } + + if translationAvailable { + self.toggle() + } else if !info.isActive { + if !isPremium { + let context = self.context + var replaceImpl: ((ViewController) -> Void)? + let controller = PremiumDemoScreen(context: context, subject: .translation, action: { + let controller = PremiumIntroScreen(context: context, source: .translation) + replaceImpl?(controller) + }) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + self.controller()?.push(controller) + } + } + } + + @objc private func morePressed(node: ContextReferenceContentNode, gesture: ContextGesture?) { + guard let info = self.currentInfo else { + return + } + + let context = self.context + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + + var languageCode = presentationData.strings.baseLanguageCode + let rawSuffix = "-raw" + if languageCode.hasSuffix(rawSuffix) { + languageCode = String(languageCode.dropLast(rawSuffix.count)) + } + + let doNotTranslateTitle: String + let fromLang = info.fromLang + let key = "Translation.Language.\(fromLang)" + if let string = presentationData.strings.primaryComponent.dict[key] { + doNotTranslateTitle = presentationData.strings.Conversation_Translation_DoNotTranslate(string).string + } else { + let languageLocale = Locale(identifier: languageCode) + let fromLanguage = languageLocale.localizedString(forLanguageCode: fromLang) ?? "" + doNotTranslateTitle = presentationData.strings.Conversation_Translation_DoNotTranslateOther(fromLanguage).string + } + + let items: Signal = context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.translationSettings]) + |> take(1) + |> map { sharedData -> ContextController.Items in + let settings: TranslationSettings + if let current = sharedData.entries[ApplicationSpecificSharedDataKeys.translationSettings]?.get(TranslationSettings.self) { + settings = current + } else { + settings = TranslationSettings.defaultSettings + } + + var items: [ContextMenuItem] = [] + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_Translation_ChooseLanguage, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Translate"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] c, _ in + guard let self else { + return + } + + var addedLanguages = Set() + + var topLanguages: [String] = [] + let langCode = normalizeTranslationLanguage(languageCode) + + var selectedLanguages: Set + if let ignoredLanguages = settings.ignoredLanguages { + selectedLanguages = Set(ignoredLanguages) + } else { + selectedLanguages = Set([langCode]) + for language in systemLanguageCodes() { + selectedLanguages.insert(language) + } + } + for code in supportedTranslationLanguages { + if selectedLanguages.contains(code) { + topLanguages.append(code) + } + } + + topLanguages.append("") + + var languages: [(String, String)] = [] + let languageLocale = Locale(identifier: langCode) + + for code in topLanguages { + if !addedLanguages.contains(code) { + let displayTitle = languageLocale.localizedString(forLanguageCode: code) ?? "" + let value = (code, displayTitle) + if code == languageCode { + languages.insert(value, at: 0) + } else { + languages.append(value) + } + addedLanguages.insert(code) + } + } + + for code in supportedTranslationLanguages { + if !addedLanguages.contains(code) { + let displayTitle = languageLocale.localizedString(forLanguageCode: code) ?? "" + let value = (code, displayTitle) + if code == languageCode { + languages.insert(value, at: 0) + } else { + languages.append(value) + } + addedLanguages.insert(code) + } + } + + c?.pushItems(items: .single(ContextController.Items( + content: .custom( + TranslationLanguagesContextMenuContent( + context: self.context, + languages: languages, back: { [weak c] in + c?.popItems() + }, selectLanguage: { [weak self, weak c] language in + c?.dismiss(completion: { + guard let self else { + return + } + self.changeLanguage(language) + }) + } + ) + ) + ))) + }))) + + items.append(.separator) + + items.append(.action(ContextMenuActionItem(text: doNotTranslateTitle, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Restrict"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] c, _ in + c?.dismiss(completion: nil) + + guard let self, let info = self.currentInfo else { + return + } + self.addDoNotTranslateLanguage(info.fromLang) + }))) + + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_Translation_Hide, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] c, _ in + c?.dismiss(completion: nil) + + self?.close() + }))) + + items.append(.separator) + + let cocoonPath = getAppBundle().url(forResource: "Cocoon", withExtension: "tgs")?.path ?? "" + let cocoonFile = TelegramMediaFile( + fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: -123456789), + partialReference: nil, + resource: BundleResource(name: "Cocoon", path: cocoonPath), + previewRepresentations: [], + videoThumbnails: [], + immediateThumbnailData: nil, + mimeType: "application/x-tgsticker", + size: nil, + attributes: [ + .FileName(fileName: "sticker.tgs"), + .CustomEmoji(isPremium: false, isSingleColor: true, alt: "", packReference: .animatedEmojiAnimations) + ], + alternativeRepresentations: [] + ) + + let (cocoonText, entities) = parseCocoonMenuTextEntities(presentationData.strings.Conversation_Translation_CocoonInfo, emojiFileId: cocoonFile.fileId.id) + items.append(.action(ContextMenuActionItem(text: cocoonText, entities: entities, entityFiles: [cocoonFile.fileId.id: cocoonFile], enableEntityAnimations: true, textLayout: .multiline, textFont: .small, icon: { _ in return nil }, action: { [weak self] c, _ in + c?.dismiss(completion: nil) + + if let controller = self?.controller() { + let infoController = context.sharedContext.makeCocoonInfoScreen(context: context) + controller.push(infoController) + } + }))) + + return ContextController.Items(content: .list(items)) + } + + if let controller = self.controller() { + let contextController = ContextController(context: context, presentationData: presentationData, source: .reference(TranslationContextReferenceContentSource(controller: controller, sourceNode: node)), items: items, gesture: gesture) + controller.presentInGlobalOverlay(contextController) + } + } +} + +private final class TranslationContextReferenceContentSource: ContextReferenceContentSource { + private let controller: ViewController + private let sourceNode: ContextReferenceContentNode + + var keepInPlace: Bool { + return true + } + + init(controller: ViewController, sourceNode: ContextReferenceContentNode) { + self.controller = controller + self.sourceNode = sourceNode + } + + func transitionInfo() -> ContextControllerReferenceViewInfo? { + return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds) + } +} + +private let separatorHeight: CGFloat = 7.0 + +private final class TranslationLanguagesContextMenuContent: ContextControllerItemsContent { + private final class BackButtonNode: HighlightTrackingButtonNode { + let highlightBackgroundNode: ASDisplayNode + let titleLabelNode: ImmediateTextNode + let separatorNode: ASDisplayNode + let iconNode: ASImageNode + + var action: (() -> Void)? + + private var theme: PresentationTheme? + + init() { + self.highlightBackgroundNode = ASDisplayNode() + self.highlightBackgroundNode.isAccessibilityElement = false + self.highlightBackgroundNode.alpha = 0.0 + + self.titleLabelNode = ImmediateTextNode() + self.titleLabelNode.isAccessibilityElement = false + self.titleLabelNode.maximumNumberOfLines = 1 + self.titleLabelNode.isUserInteractionEnabled = false + + self.iconNode = ASImageNode() + self.iconNode.isAccessibilityElement = false + + self.separatorNode = ASDisplayNode() + self.separatorNode.isAccessibilityElement = false + + super.init() + + self.addSubnode(self.separatorNode) + self.addSubnode(self.highlightBackgroundNode) + self.addSubnode(self.titleLabelNode) + self.addSubnode(self.iconNode) + + self.isAccessibilityElement = true + + self.highligthedChanged = { [weak self] highlighted in + guard let strongSelf = self else { + return + } + if highlighted { + strongSelf.highlightBackgroundNode.alpha = 1.0 + } else { + let previousAlpha = strongSelf.highlightBackgroundNode.alpha + strongSelf.highlightBackgroundNode.alpha = 0.0 + strongSelf.highlightBackgroundNode.layer.animateAlpha(from: previousAlpha, to: 0.0, duration: 0.2) + } + } + + self.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside) + } + + @objc private func pressed() { + self.action?() + } + + func update(size: CGSize, presentationData: PresentationData, isLast: Bool) { + let standardIconWidth: CGFloat = 32.0 + let sideInset: CGFloat = 16.0 + let iconSideInset: CGFloat = 12.0 + + if self.theme !== presentationData.theme { + self.theme = presentationData.theme + self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: presentationData.theme.contextMenu.primaryColor) + + self.accessibilityLabel = presentationData.strings.Common_Back + } + + self.highlightBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor + self.separatorNode.backgroundColor = presentationData.theme.contextMenu.itemSeparatorColor + + self.highlightBackgroundNode.frame = CGRect(origin: CGPoint(), size: size) + + self.titleLabelNode.attributedText = NSAttributedString(string: presentationData.strings.Common_Back, font: Font.regular(17.0), textColor: presentationData.theme.contextMenu.primaryColor) + let titleSize = self.titleLabelNode.updateLayout(CGSize(width: size.width - sideInset - standardIconWidth, height: 100.0)) + self.titleLabelNode.frame = CGRect(origin: CGPoint(x: sideInset + 36.0, y: floor((size.height - titleSize.height) / 2.0)), size: titleSize) + + if let iconImage = self.iconNode.image { + let iconFrame = CGRect(origin: CGPoint(x: iconSideInset, y: floor((size.height - iconImage.size.height) / 2.0)), size: iconImage.size) + self.iconNode.frame = iconFrame + } + + self.separatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: size.height - UIScreenPixel), size: CGSize(width: size.width, height: UIScreenPixel)) + self.separatorNode.isHidden = isLast + } + } + + private final class LanguagesListNode: ASDisplayNode, ASScrollViewDelegate { + private final class ItemNode: HighlightTrackingButtonNode { + let context: AccountContext + let highlightBackgroundNode: ASDisplayNode + let titleLabelNode: ImmediateTextNode + let separatorNode: ASDisplayNode + + let action: () -> Void + + private var language: String? + + init(context: AccountContext, action: @escaping () -> Void) { + self.action = action + self.context = context + + self.highlightBackgroundNode = ASDisplayNode() + self.highlightBackgroundNode.isAccessibilityElement = false + self.highlightBackgroundNode.alpha = 0.0 + + self.titleLabelNode = ImmediateTextNode() + self.titleLabelNode.isAccessibilityElement = false + self.titleLabelNode.maximumNumberOfLines = 1 + self.titleLabelNode.isUserInteractionEnabled = false + + self.separatorNode = ASDisplayNode() + self.separatorNode.isAccessibilityElement = false + + super.init() + + self.isAccessibilityElement = true + + self.addSubnode(self.separatorNode) + self.addSubnode(self.highlightBackgroundNode) + self.addSubnode(self.titleLabelNode) + + self.highligthedChanged = { [weak self] highlighted in + guard let strongSelf = self, let language = strongSelf.language, !language.isEmpty else { + return + } + if highlighted { + strongSelf.highlightBackgroundNode.alpha = 1.0 + } else { + let previousAlpha = strongSelf.highlightBackgroundNode.alpha + strongSelf.highlightBackgroundNode.alpha = 0.0 + strongSelf.highlightBackgroundNode.layer.animateAlpha(from: previousAlpha, to: 0.0, duration: 0.2) + } + } + + self.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside) + } + + @objc private func pressed() { + guard let language = self.language, !language.isEmpty else { + return + } + self.action() + } + + private var displayTitle: String? + func update(size: CGSize, presentationData: PresentationData, language: String, displayTitle: String, isLast: Bool, syncronousLoad: Bool) { + let sideInset: CGFloat = 16.0 + + if self.language != language { + self.language = language + self.displayTitle = displayTitle + + self.accessibilityLabel = "\(displayTitle)" + } + + self.highlightBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor + + self.highlightBackgroundNode.frame = CGRect(origin: CGPoint(), size: size) + + self.titleLabelNode.attributedText = NSAttributedString(string: self.displayTitle ?? "", font: Font.regular(17.0), textColor: presentationData.theme.contextMenu.primaryColor) + let maxTextWidth: CGFloat = size.width - sideInset + + let titleSize = self.titleLabelNode.updateLayout(CGSize(width: maxTextWidth, height: 100.0)) + let titleFrame = CGRect(origin: CGPoint(x: sideInset, y: floor((size.height - titleSize.height) / 2.0)), size: titleSize) + self.titleLabelNode.frame = titleFrame + + if language == "" { + self.separatorNode.backgroundColor = presentationData.theme.contextMenu.sectionSeparatorColor + self.separatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: separatorHeight)) + self.separatorNode.isHidden = false + } else { + self.separatorNode.backgroundColor = presentationData.theme.contextMenu.itemSeparatorColor + self.separatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: size.height), size: CGSize(width: size.width, height: UIScreenPixel)) + self.separatorNode.isHidden = isLast + } + } + } + + private let context: AccountContext + private let languages: [(String, String)] + private let requestUpdate: (LanguagesListNode, ContainedViewLayoutTransition) -> Void + private let requestUpdateApparentHeight: (LanguagesListNode, ContainedViewLayoutTransition) -> Void + private let selectLanguage: (String) -> Void + + private let scrollNode: ASScrollNode + private var ignoreScrolling: Bool = false + private var animateIn: Bool = false + private var bottomScrollInset: CGFloat = 0.0 + + private var presentationData: PresentationData? + private var currentSize: CGSize? + private var apparentHeight: CGFloat = 0.0 + + private var itemNodes: [Int: ItemNode] = [:] + + init( + context: AccountContext, + languages: [(String, String)], + requestUpdate: @escaping (LanguagesListNode, ContainedViewLayoutTransition) -> Void, + requestUpdateApparentHeight: @escaping (LanguagesListNode, ContainedViewLayoutTransition) -> Void, + selectLanguage: @escaping (String) -> Void + ) { + self.context = context + self.languages = languages + self.requestUpdate = requestUpdate + self.requestUpdateApparentHeight = requestUpdateApparentHeight + self.selectLanguage = selectLanguage + + self.scrollNode = ASScrollNode() + self.scrollNode.canCancelAllTouchesInViews = true + self.scrollNode.view.delaysContentTouches = false + self.scrollNode.view.showsVerticalScrollIndicator = false + if #available(iOS 11.0, *) { + self.scrollNode.view.contentInsetAdjustmentBehavior = .never + } + self.scrollNode.clipsToBounds = false + + super.init() + + self.addSubnode(self.scrollNode) + self.scrollNode.view.delegate = self.wrappedScrollViewDelegate + + self.clipsToBounds = true + } + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + if self.ignoreScrolling { + return + } + self.updateVisibleItems(animated: false, syncronousLoad: false) + + if let size = self.currentSize { + var apparentHeight = -self.scrollNode.view.contentOffset.y + self.scrollNode.view.contentSize.height + apparentHeight = max(apparentHeight, 44.0) + apparentHeight = min(apparentHeight, size.height) + if self.apparentHeight != apparentHeight { + self.apparentHeight = apparentHeight + + self.requestUpdateApparentHeight(self, .immediate) + } + } + } + + private func updateVisibleItems(animated: Bool, syncronousLoad: Bool) { + guard let size = self.currentSize else { + return + } + guard let presentationData = self.presentationData else { + return + } + let itemHeight: CGFloat = 44.0 + let visibleBounds = self.scrollNode.bounds.insetBy(dx: 0.0, dy: -180.0) + + var validIds = Set() + + let minVisibleIndex = max(0, Int(floor(visibleBounds.minY / itemHeight))) + let maxVisibleIndex = Int(ceil(visibleBounds.maxY / itemHeight)) + + var separatorIndex = 0 + for i in 0 ..< self.languages.count { + if self.languages[i].0.isEmpty { + separatorIndex = i + break + } + } + + if minVisibleIndex <= maxVisibleIndex { + for index in minVisibleIndex ... maxVisibleIndex { + if index < self.languages.count { + let height = self.languages[index].0.isEmpty ? separatorHeight : itemHeight + var itemFrame = CGRect(origin: CGPoint(x: 0.0, y: CGFloat(index) * itemHeight), size: CGSize(width: size.width, height: height)) + if index > separatorIndex { + itemFrame.origin.y += separatorHeight - itemHeight + } + + let (languageCode, displayTitle) = self.languages[index] + validIds.insert(index) + + let itemNode: ItemNode + if let current = self.itemNodes[index] { + itemNode = current + } else { + let selectLanguage = self.selectLanguage + itemNode = ItemNode(context: self.context, action: { + selectLanguage(languageCode) + }) + self.itemNodes[index] = itemNode + self.scrollNode.addSubnode(itemNode) + } + + itemNode.update(size: itemFrame.size, presentationData: presentationData, language: languageCode, displayTitle: displayTitle, isLast: index == self.languages.count - 1 || index == separatorIndex - 1, syncronousLoad: syncronousLoad) + itemNode.frame = itemFrame + } + } + } + + var removeIds: [Int] = [] + for (id, itemNode) in self.itemNodes { + if !validIds.contains(id) { + removeIds.append(id) + itemNode.removeFromSupernode() + } + } + for id in removeIds { + self.itemNodes.removeValue(forKey: id) + } + } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + var extendedScrollNodeFrame = self.scrollNode.frame + extendedScrollNodeFrame.size.height += self.bottomScrollInset + + if extendedScrollNodeFrame.contains(point) { + return self.scrollNode.view.hitTest(self.view.convert(point, to: self.scrollNode.view), with: event) + } + + return super.hitTest(point, with: event) + } + + func update(presentationData: PresentationData, constrainedSize: CGSize, bottomInset: CGFloat, transition: ContainedViewLayoutTransition) -> (height: CGFloat, apparentHeight: CGFloat) { + let itemHeight: CGFloat = 44.0 + + self.presentationData = presentationData + + var separatorIndex = 0 + for i in 0 ..< self.languages.count { + if self.languages[i].0.isEmpty { + separatorIndex = i + break + } + } + + var contentHeight: CGFloat + if separatorIndex != 0 { + contentHeight = CGFloat(self.languages.count - 1) * itemHeight + separatorHeight + } else { + contentHeight = CGFloat(self.languages.count) * itemHeight + } + let size = CGSize(width: constrainedSize.width, height: contentHeight) + + let containerSize = CGSize(width: size.width, height: min(constrainedSize.height, size.height)) + self.currentSize = containerSize + + self.ignoreScrolling = true + + if self.scrollNode.frame != CGRect(origin: CGPoint(), size: containerSize) { + self.scrollNode.frame = CGRect(origin: CGPoint(), size: containerSize) + } + if self.scrollNode.view.contentInset.bottom != bottomInset { + self.scrollNode.view.contentInset.bottom = bottomInset + } + self.bottomScrollInset = bottomInset + let scrollContentSize = CGSize(width: size.width, height: size.height) + if self.scrollNode.view.contentSize != scrollContentSize { + self.scrollNode.view.contentSize = scrollContentSize + } + self.ignoreScrolling = false + + self.updateVisibleItems(animated: transition.isAnimated, syncronousLoad: !transition.isAnimated) + + self.animateIn = false + + var apparentHeight = -self.scrollNode.view.contentOffset.y + self.scrollNode.view.contentSize.height + apparentHeight = max(apparentHeight, 44.0) + apparentHeight = min(apparentHeight, containerSize.height) + self.apparentHeight = apparentHeight + + return (containerSize.height, apparentHeight) + } + } + + final class ItemsNode: ASDisplayNode, ContextControllerItemsNode { + private let context: AccountContext + private let languages: [(String, String)] + private let requestUpdate: (ContainedViewLayoutTransition) -> Void + private let requestUpdateApparentHeight: (ContainedViewLayoutTransition) -> Void + + private var presentationData: PresentationData + + private var backButtonNode: BackButtonNode? + private var separatorNode: ASDisplayNode? + + private let currentTabIndex: Int = 0 + private var visibleTabNodes: [Int: LanguagesListNode] = [:] + + private let selectLanguage: (String) -> Void + + private(set) var apparentHeight: CGFloat = 0.0 + + init( + context: AccountContext, + languages: [(String, String)], + requestUpdate: @escaping (ContainedViewLayoutTransition) -> Void, + requestUpdateApparentHeight: @escaping (ContainedViewLayoutTransition) -> Void, + back: (() -> Void)?, + selectLanguage: @escaping (String) -> Void + ) { + self.context = context + self.languages = languages + self.selectLanguage = selectLanguage + self.presentationData = context.sharedContext.currentPresentationData.with({ $0 }) + + self.requestUpdate = requestUpdate + self.requestUpdateApparentHeight = requestUpdateApparentHeight + + if let back = back { + self.backButtonNode = BackButtonNode() + self.backButtonNode?.action = { + back() + } + } + + super.init() + + if self.backButtonNode != nil { + self.separatorNode = ASDisplayNode() + } + + if let backButtonNode = self.backButtonNode { + self.addSubnode(backButtonNode) + } + if let separatorNode = self.separatorNode { + self.addSubnode(separatorNode) + } + } + + func update(presentationData: PresentationData, constrainedWidth: CGFloat, maxHeight: CGFloat, bottomInset: CGFloat, transition: ContainedViewLayoutTransition) -> (cleanSize: CGSize, apparentHeight: CGFloat) { + let constrainedSize = CGSize(width: min(220.0, constrainedWidth), height: min(604.0, maxHeight)) + + var topContentHeight: CGFloat = 0.0 + if let backButtonNode = self.backButtonNode { + let backButtonFrame = CGRect(origin: CGPoint(x: 0.0, y: topContentHeight), size: CGSize(width: constrainedSize.width, height: 44.0)) + backButtonNode.update(size: backButtonFrame.size, presentationData: self.presentationData, isLast: true) + transition.updateFrame(node: backButtonNode, frame: backButtonFrame) + topContentHeight += backButtonFrame.height + } + if let separatorNode = self.separatorNode { + let separatorFrame = CGRect(origin: CGPoint(x: 0.0, y: topContentHeight), size: CGSize(width: constrainedSize.width, height: separatorHeight)) + separatorNode.backgroundColor = self.presentationData.theme.contextMenu.sectionSeparatorColor + transition.updateFrame(node: separatorNode, frame: separatorFrame) + topContentHeight += separatorFrame.height + } + + var tabLayouts: [Int: (height: CGFloat, apparentHeight: CGFloat)] = [:] + + var visibleIndices: [Int] = [] + visibleIndices.append(self.currentTabIndex) + + let previousVisibleTabFrames: [(Int, CGRect)] = self.visibleTabNodes.map { key, value -> (Int, CGRect) in + return (key, value.frame) + } + + for index in visibleIndices { + var tabTransition = transition + let tabNode: LanguagesListNode + var initialReferenceFrame: CGRect? + if let current = self.visibleTabNodes[index] { + tabNode = current + } else { + for (previousIndex, previousFrame) in previousVisibleTabFrames { + if index > previousIndex { + initialReferenceFrame = previousFrame.offsetBy(dx: constrainedSize.width, dy: 0.0) + } else { + initialReferenceFrame = previousFrame.offsetBy(dx: -constrainedSize.width, dy: 0.0) + } + break + } + + tabNode = LanguagesListNode( + context: self.context, + languages: self.languages, + requestUpdate: { [weak self] tab, transition in + guard let strongSelf = self else { + return + } + if strongSelf.visibleTabNodes.contains(where: { $0.value === tab }) { + strongSelf.requestUpdate(transition) + } + }, + requestUpdateApparentHeight: { [weak self] tab, transition in + guard let strongSelf = self else { + return + } + if strongSelf.visibleTabNodes.contains(where: { $0.value === tab }) { + strongSelf.requestUpdateApparentHeight(transition) + } + }, + selectLanguage: self.selectLanguage + ) + self.addSubnode(tabNode) + self.visibleTabNodes[index] = tabNode + tabTransition = .immediate + } + + let tabLayout = tabNode.update(presentationData: presentationData, constrainedSize: CGSize(width: constrainedSize.width, height: constrainedSize.height - topContentHeight), bottomInset: bottomInset, transition: tabTransition) + tabLayouts[index] = tabLayout + let currentFractionalTabIndex = CGFloat(self.currentTabIndex) + let xOffset: CGFloat = (CGFloat(index) - currentFractionalTabIndex) * constrainedSize.width + let tabFrame = CGRect(origin: CGPoint(x: xOffset, y: topContentHeight), size: CGSize(width: constrainedSize.width, height: tabLayout.height)) + tabTransition.updateFrame(node: tabNode, frame: tabFrame) + if let initialReferenceFrame = initialReferenceFrame { + transition.animatePositionAdditive(node: tabNode, offset: CGPoint(x: initialReferenceFrame.minX - tabFrame.minX, y: 0.0)) + } + } + + var contentSize = CGSize(width: constrainedSize.width, height: topContentHeight) + var apparentHeight = topContentHeight + + if let tabLayout = tabLayouts[self.currentTabIndex] { + contentSize.height += tabLayout.height + apparentHeight += tabLayout.apparentHeight + } + + return (contentSize, apparentHeight) + } + } + + let context: AccountContext + let languages: [(String, String)] + let back: (() -> Void)? + let selectLanguage: (String) -> Void + + public init( + context: AccountContext, + languages: [(String, String)], + back: (() -> Void)?, + selectLanguage: @escaping (String) -> Void + ) { + self.context = context + self.languages = languages + self.back = back + self.selectLanguage = selectLanguage + } + + func node( + requestUpdate: @escaping (ContainedViewLayoutTransition) -> Void, + requestUpdateApparentHeight: @escaping (ContainedViewLayoutTransition) -> Void + ) -> ContextControllerItemsNode { + return ItemsNode( + context: self.context, + languages: self.languages, + requestUpdate: requestUpdate, + requestUpdateApparentHeight: requestUpdateApparentHeight, + back: self.back, + selectLanguage: self.selectLanguage + ) + } +} + +private func parseCocoonMenuTextEntities(_ input: String, emojiFileId: Int64) -> (String, [MessageTextEntity]) { + var output = "" + + var entities: [MessageTextEntity] = [] + + var i = input.startIndex + var outputCount = 0 + + func utf16Len(_ s: String) -> Int { + s.utf16.count + } + + func peek(_ offset: Int) -> Character? { + var idx = i + for _ in 0.. start { + entities.append(MessageTextEntity(range: start.. start { + entities.append(MessageTextEntity(range: start.. Void + public let toggle: () -> Void + public let changeLanguage: (String) -> Void + public let addDoNotTranslateLanguage: (String) -> Void + public let controller: () -> ViewController? + + public init( + context: AccountContext, + theme: PresentationTheme, + strings: PresentationStrings, + info: Info, + close: @escaping () -> Void, + toggle: @escaping () -> Void, + changeLanguage: @escaping (String) -> Void, + addDoNotTranslateLanguage: @escaping (String) -> Void, + controller: @escaping () -> ViewController? + ) { + self.context = context + self.theme = theme + self.strings = strings + self.info = info + self.close = close + self.toggle = toggle + self.changeLanguage = changeLanguage + self.addDoNotTranslateLanguage = addDoNotTranslateLanguage + self.controller = controller + } + + public static func ==(lhs: TranslateHeaderPanelComponent, rhs: TranslateHeaderPanelComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.theme !== rhs.theme { + return false + } + if lhs.strings !== rhs.strings { + return false + } + if lhs.info != rhs.info { + return false + } + return true + } + + public final class View: UIView { + private var panel: ChatTranslationPanelNode? + + private var component: TranslateHeaderPanelComponent? + private weak var state: EmptyComponentState? + + public override init(frame: CGRect) { + super.init(frame: frame) + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + } + + func update(component: TranslateHeaderPanelComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + self.state = state + + let panel: ChatTranslationPanelNode + if let current = self.panel { + panel = current + } else { + panel = ChatTranslationPanelNode( + context: component.context, + close: component.close, + toggle: component.toggle, + changeLanguage: component.changeLanguage, + addDoNotTranslateLanguage: component.addDoNotTranslateLanguage, + controller: component.controller + ) + self.panel = panel + self.addSubview(panel.view) + } + + let size = CGSize(width: availableSize.width, height: 40.0) + let panelFrame = CGRect(origin: CGPoint(), size: size) + transition.setFrame(view: panel.view, frame: panelFrame) + let _ = panel.updateLayout( + width: panelFrame.width, + info: component.info, + theme: component.theme, + strings: component.strings, + transition: transition.containedViewLayoutTransition + ) + + return size + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Media/EntityInputClearIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Input/Media/EntityInputClearIcon.imageset/Contents.json index d4b20938..ea33d1b7 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat/Input/Media/EntityInputClearIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Media/EntityInputClearIcon.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "backspace_24.svg", + "filename" : "backspace_30.pdf", "idiom" : "universal" } ], diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Media/EntityInputClearIcon.imageset/backspace_30.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Input/Media/EntityInputClearIcon.imageset/backspace_30.pdf new file mode 100644 index 0000000000000000000000000000000000000000..387f2711d1f1d92e9aeee0713ce32317531b83a5 GIT binary patch literal 4847 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 literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Media/EntityInputSettingsIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Input/Media/EntityInputSettingsIcon.imageset/Contents.json index 92f837ea..f0b8b677 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat/Input/Media/EntityInputSettingsIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Media/EntityInputSettingsIcon.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "keyboard_2444.svg", + "filename" : "settings_30 (4).pdf", "idiom" : "universal" } ], diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Media/EntityInputSettingsIcon.imageset/settings_30 (4).pdf b/submodules/TelegramUI/Images.xcassets/Chat/Input/Media/EntityInputSettingsIcon.imageset/settings_30 (4).pdf new file mode 100644 index 0000000000000000000000000000000000000000..38a6fcde68a892ac7c3e3ab7b27cfb64a53154ee GIT binary patch literal 7917 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 Jt&E!N{{dVRxJUp1 literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Media/PanelFeaturedIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Input/Media/PanelFeaturedIcon.imageset/Contents.json index 5a3751c0..94aac7db 100644 --- a/submodules/TelegramUI/Images.xcassets/Chat/Input/Media/PanelFeaturedIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Media/PanelFeaturedIcon.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "Group 1.svg", + "filename" : "trending_44.pdf", "idiom" : "universal" } ], diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Media/PanelFeaturedIcon.imageset/trending_44.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Input/Media/PanelFeaturedIcon.imageset/trending_44.pdf new file mode 100644 index 0000000000000000000000000000000000000000..0ce4046658fad5245f88b21f1117e61f6803629e GIT binary patch literal 8700 zcmb7}XE>Z)*Y^oRgdjvO6B0qR(R=T`cgA2adKtYFC2F)7J&5Q;jW&qhg@|6G_vq2$ zCFQ#A>%Q;jeU4{;IM02qz4qGWTpxbNe?3)^kYWNdb7MaBziF6204u=W#0nDt5D);c zNI`925T~2B4HyQIfSB5wL2gEF8jFU90|daLqikXYF@^oMDM0KjV3q*3n-FThP0~*G zE)G9K{hILe``6gdIKP6a-^6nQ+c`Ucogj9m9{({e3xGMf{9gI@G$EniE2z7ez<%yU z%HGNLw@nS=Z13V^3UR(&`7ci~F?)A_K7f^(^=JFL!@@=cCF#|9 zrJvQ)jc9?btBX1{+m#*d z;l$5nUm2mLBs$4;=6KCk^H7-ei0Zk=yZut}1R8y&@XF(0}0%PtGC;bI0q($SZ z2jrSISNWy`t6N)^MKz;yxfBIelV%Oxd1c^g8~*}O(Mn>7&&c9uL6iGfl570R6IPOD zRNDkg@SLmSrcdEXm8-4@-VvL@!0jGQYM&)=d(c%D zAQ(&ep3BM=niQznh@!#=6B`gUc4I$C^Dz%;-lJC}nD_A>62XZqzitOskRR~q6;vvG zs@2Wj2E5&#wmJ+R0>+%Xz z-_#6KE)X6klFWP!r^5D&Gl)p4Q8UmhHdWk3CLq+of@P&nJ5Zu78 z{WMsMKVz|x%o-!oJ+U$V!S>v=x*&T|v(_FRQB+AQkLyB}!10-Y8s!vMlaqxYt`M87 zUi}71j={Ecr4e6)oS+LWSYvWd ztpkAyE}Nm{G#z-5mFnM^uk3yLa011VE&AEGErMI-4uO5RO+JZB7G)IhbUeeb`tx!k zT|*8&zByVK{&q*Cse5A1cw%;dP+dqOj^`%PGs#f)%b7ulmCdR*?Hdn|ZlbnF&t~7P z6iTcR19r@XU)krvA3Y1{V-?carDue7%b0IgJvEMRIk8m9e}$>*4KAN%Dr7l|o$t)Q zZylomKAoEn4A}*6(%H3_$*|U8&CD=mI||AAapQALh!zLFbb{`sN)*rRka&7dXm#Yp z?p3^@EL^cB9??X+-N_G~gN}VRG*^y!se|v?-L!G~!mHVSvc25cv|i5ap#JFiBc&{gkOA}0LeuShL z-i^=$S&lWjJ3VMsHdYF!FDx&A89HC+o3*VL#MRNuQQ0iRFx-=4Wk!9ujVPCQ1UU+= zQ-SiG;7>pc$tWCNViq68*y>D_Qli(ym>x2-aX%@W$R!sK7~Me@E!anR_(VYra97Y^ zd-X|k3vT|RNU@i~y;g6B*1WFogE+BdF208YiXKWcUVg)l zJgm6|?_kAsr%kMqmrR4`9g{(KS(12s#qrh;PeP*rD)(*bf-y`7I#$r916`Ep!sTeU z;AtgMxW%+ik?@V#v~N|S3N`QB638PO>)by$rwDo}i$8hq%V#ceLMnw#_e80s&*5WG zC!b6HNISAS44bH=a&RF1_1a^_ftRcb{o|7D1nmHBc)a~Wp|M>}q;UqHe49wtLHrz2QzaT#>-aP~~L=E2!!{>)ml5c4}=-_Jm>7LYj9{>Y-l@ z#2p+dS0%G)9&NF`QG=V#LcBUnoVtt11UuB})DjApnbFz`M#aho50=S+eDP)%x&w_7 zLgm~Lug{i|;Or@m2YmxY!wOjC&gRe<)okRi7}ne?4{!x(+x0O@+-Pvj-daT2eMme7 zypbRxoVoNo3#UonLRkYd!oW_{HSGg^$wA>q_ZJ5YREh=GJVFgND3cAuRM-v>CQ|+2 zfCJ*@h@4K1Btlwj21$-PCQ#@DDnh!!jZ%8L>7l{p(U8@d3I*$q&FIG%!jDYl^+Ph! z0g)ZsNLl*J3rb+G0fQy6>SZfq37_&3>}=m=qjsB#SiJYijY@j8w0?P(OF9jtBX8zx z7n!JgcKydke5$qZld@L%n&$O42Nb?e2=X0nwK6Ui3~Sl(J!bG(F(|MgLjEo+*2t-8 zJ-yF!HEyik+0NRUTQyT>vfAB3powPhNc4d&eYZV?;jpw;Qc`#KM^3^eeFS6nO$u?n zGH99Alb_I(V5#$&bbMO(+Dd2&4-GHe#=e6-c3WgkY(lAWGAIWi3eAy8`m!b8U}79& z%LFcldMYmK(1?!reXW0pklSw(y(nfpmz2;eH#fI{UfOp)BCOL`B2c8cxaNF-3cN_i zPTLOC=dP^|s=cR)+uRWdtkXrmN|BvBZe=Q1f;Mz)aDA0}+$H~j^d2NW9ldR3M(Hg# z5U=)3nu>rYm9e$eG|1#flp6D&++1)T6fJFl03N?kONsIkvss9-P|KJ`c7x!;p zB>?+Bu_>+kHB<$ElCaC(?Iel`8CkcrDMax#Qbh>Un?Qe??1ynk;Cr|aTMY431Mnmz zPz^-xN|HOkbj)z^RA=s@p=lTdVqy03@Pv~wl%Y_;HWj}VX_w747dH6$Y^F`GWn5%k zBfYXikzjSo#puX31}S?=m=RE%qTcHX;&rSgfiBFW`LwwO9ooAQS2RS-BzIw7Jb-Dm zlbsMg)J0sYTp$SCFR_>_(ui7Tbe5MeGDYBoWTgFKwG>DKyg&E}eYzS6C~=TlHJ|ud zTpBO|WwcTmW$^(0@s608@c|B8t>@*kP?HzWA&$e|KcFNLXEUfT8t`Z>StpoNJLP9W6; z3aB8R_+PytCr9Rd^MJ#j{S8`GP{aaeEwWt@GAq_wW2{D0^#uxe000AB6~OTJE>!&P zD-tsl;A8D>GQB%!;;|}N1UUB|->X8Ny8B3^LxooZ?Te_e_}ZI@Pn`KU^*B)?_@6+Z zo)M$PgGr0;k+r^N#Xs>!Sx|9*M2?f!wzXj3&TVj4yDe|Q{5!Qb3Z;MUH+gqbG?ga_ zm~!YHB0yC}4FCh?qtJ(0Tv-RIx%pNaD23Qxg6f0fvT0SRhIKd|DN3_{8nhHX!sH-I zkDhzMERmF>{3dmHcAbA6?}*_1c}U=J3mlxrYNLQkOBo;B)0SXj1yqFh!IArkS1AZ1 zG(VDpDc<`|$kb%RlpwD`8uNIDbU@%EJR>#&N@S!*oL)k0ERG zX^HO=Pz)i0>Uu(P5ZB3Py1j~O0D zg)z6Qm{2=DFOQrMX;ko*@fN3!g~>+0U3r2GyUGpY@uZdaXvg-C*NIdKjmxfL!};-9{rbj z2}Ll4jkpoIN0MK1D)J3Bxva2{*<2z+a!H0KswDJ~cb@67*vje3PHOQg@aorzoi$cy zpyit6w`$31(W~+1u;m{>16J?Epk1Oh5OOfCgT5KLWED0rfH*FTr97J9f{~3c z$-|r@hFPgO)A<)NG5KAi5ucwAZ;hmmjE{i|n?-LBIRN>iKlGKcL(7}41w>XXxOVYD z8)2IxjV4XaFkg;mPG~2ADfbd=$!e*ARtNu`Y+TRd`001=3(}#1(BKUw_KE&E1!LFl z;vsU=T}x(;GA=5US0t2?3Q~fm?P47!FD0WTBRZbT`}EQEAzll<29<0V$CX$Whm<51 zUzIo)PZwWT0IL_LG$v*YC3Kf7Ef2HDB+G0EY|d&zZTBWscV18Jyx*$0FxfL^sjz>a z>F~)d%rPHEY%&5D?H60sl#a~FTQcu*?TS%4Q8LM6SMuXyPm51e=8IwB((4BqRJxB! zjt4KPtx&tL!en%p^Uqf#UAVSC3x1aOnf};XckK8&ihXnPrE%r%Rqkp1bFz*nDI) zcEpqG<>glRRrIdvW9iM5>2Eqh)q-VR>-55e}c&lejMK^3LFU>3Z7}*64tp^IceM8t(`?ko=AqC z8lPgEV(q$2du8jCyBqGWH|1WW$ge*=duo4LHtAO~T65Hm{1v$e89EtPF&1==eoB66 zkf9=^vZy<(Q=nQ`ba3@}F6&3zZYWu_B_%!2!AAbVI)Y)oaMh}9bHsGQG`TC|%Zo2Z zm))1;rCNxZHz$3Li;f#F7w~6Y9=NzX<5VL2(smqo{!)E7&+dBpSlt>WC#z63w|s$v zhl%&<>l!>0eF!yegRf6u`dh?DmPEzTcbDe*9=3-X@?sHHoE6o3Y1eh7Tx&J&Kuo@Z zbETh+9F5MrRng07KA4`g*5Ch}4o#0IT4ITIE!iM#B5>TWJ3ZK)>qAZ|Gup3nD(K2t zYt>9D+8IwPO>Y=%oO5dXA$6$OkgC``c{Mg_JwNT;x2Uyc+cjMK8eMQ@me${<0@tssM3x z88+TMkoQj^)O$;T7(;hhqSogES`EbDqA;baR@~6nC0|eX(m}-BEevPJ*ne&%k_&<39e|m~30$7w?Ax<`6kH1#`mzaMh zvxwW<*gL5^fK7kxNE`^bJ@oG#-+28wB=bjkqi$-Qib=g;rE3ty#ygs+#jo?ZJ~Ty6P* zgs+&Ie_SZ?w&` zuDyBR3x?zMwPGKH6|Ww@wQ?D#xz}U4YL!p;6C0Sl#21_sbIGtpT-Tq7Qgmjs=I(P* zc$Qy1PMz(vth=|iCRd%9l$ZTHFYj+{)9*{u=k>9-IY);IOPZl{(lvSn7Cld?Q>8&3 z#rqF;lGXb-waRjqc?&1bRl8CK#>a=XYzq)_{A$q39@jG^P@B~xl6tdN-*fDXw{3Y| z?S#bPgW4JIzl3+sP0)7&r7xP!#hx1trc58N%2XnpLI!5Ooo=Upj&;V?IbBBozcPz@H(C;vOC%_3++|R5_XFiP;HxHTt&`-XICqWpp&lb*Gw=*% zT(4;cT2Y@{mQGgOfjEBAl~q%FB%f$@~UfCE*%n>VdyMu-s`eQI=uLV+fx965ht$v6jQ}1^U3$vBFuzQt!4bQw~0pb z4qJ?)1nt$6vGe-+x!wo-(TW~Ty{y$OJW5&Bt1i{yFA@PIj)hm!is<*OB=%=IV&p;? zETVA5X&(BDQ<-=MC2fw(>SY3N!iSz?fxX*Gc zhAY!-6yKrL9yzYPxwNr1j9bxR8>87oH5_m_LLa@P4C;8s+b>XyOf430e)dEK?poIHLZ^4);FvA?-u)62DT*rCC%Zlq zd-4+@ngibiwg$surLIx0N$&l60b27r!s^jJW<^vD?r~yl5=Fon`yMfU=;B9WT$Rq2 zPH&^D)sLv?L{}}#=Z4-?meorngAL^&En(U{{2ML1h^s;i^W*VNik0kgP2#QxX|MBy zGNpUPr#rQGi_u%9X=UZP1x;Z)UG=8$mo0giliW}B5;x~zl~sF~wOg&hD(o0Ct!ipb za_>Tu220hH(`AV@Yd&Z=vWQMR{~$Y{Qz^bq3t-vApGTmch@?d>^|;xz9~7QYO3INAR5hYHLn+-?z;X>KE#1E|=pxJAa< z22&Tbc+cIuLNX|8H20y)4UGArR%fIkFijRH|7Mn3%JCL+CFqa1>CuW<)8}$zoPd`; zOm1ZML6mn*NFmsIU-a&hq(<(Le4Sbssyd?!JmNknu_P{VRo1c>KGiI+chQ5&hUb&t2)eQ~)ksA}Uti z_C_F65+Vr?sNq=| zb=DZCBzw+lfGK)d)dm{ij#93q^9tEuBbMrLn{t^@$EF-DboZ?f1cJU#X1y;n0xyV#kOX< zQAjp)bEa)q1TpA_gVDI>o4h+>2af{|SqbY|dGYkWuCDZpN}dOQGiRpL$~bHkFNMV@ zFh8avIWq#%tiBiVh@9_GH7%6CAAdKIniRRbiX#-~KEWDnoFHMx!otZ)+}Td|UA2iz z7w(J9@bX+H@#g=ZxH;bxHu;{0#+cnBy~;uHhthCoxhv;eQN(nszC_$Ca#`+$RS7M# z_<)Od^M}#Yzq6KV*}_5G3UNA+o|%+zQopCHFS&!ub8=#rav_DDcPcB^iSVEuDaq-%`yWWOb(Ynl)H$NIo5y)@+qd1g94ct+W*5dDe5KV8 zy(GNfxol2xWK9bhzyINFblwhO-gFpQFgXja<&y+OS4ylTXa3l^b_wzivZJ6!--f8G zjVy}fS}AAvMeN>;s~L)X<1sCx>`Ek_Mjg0Dp}F$No^D#qsgJ<6P-n=`R&y=eYOP4e zoUTgYm5+~YfxYcTNBEIq0aAEyetUQtn=)C~O>jJ&0uKjJ3m-x$gJm-(l-X=i5- za|Y<&mUM5??pGyR1#I!BmaPaegM$CB#o^mXf7h9Bk@Qc^Tio6b2C=(Y@fP9!7*%dd z+b{r-^|$R`NbjfZ*C}qdD-Sih*(~7p47bqxYtQ2LF1NG);{?vXknayP0{>aQ*MdUa zAWkYy5Oc_FVf|M~KS=685GVP?!zmXKnp2WMz@HGq?WzGYjE>${f8en5ATf-|IfKRtXwx~^KUjF z$G@x#a{@zcAWoRKX(20dlN5C9Km%58Ha0ePUiyE2{7e)UHGBJ;EV)gBpLxfk4)uiG bUfj)}aK0%7!G2yP4?8CZ=F_K=%2NLi^Oac!+E7 zjDeudn<1RG3!mk|VenZHPLFk%&16|@(*Y{*;0)NP4t%=+wH}Mfbzn_uOjw+Qd?$!N zMkter8AJq>_{bI(&^x4)9{Df0{% z;Ce33DGY9nE|>d0hi~~nc}Q{op5k%(_hJR%*lcW0+@jJ}>#k1O1g$7V`dSh4a=N+&*HcVvaGPVnKq+xMFZrb`D#2)R;fb5H6z1y zyexkFP~{Vc_`!42poCdk0~$B6x9G{>)FYR_c+Tq>G=F+w|Uh$|C_< zb1^ILJKvL;3CWz&Qf!pSJ-(SPG358-^3tV}LOt!sO~>HWMxh0zTskJsIcV#MKHOr+N= z3p-I+cG2D$HjS!^lB{0*Rz)#**Zn07mA{WQ8oI1Jyd`(;QQ4MLpB@Y`wq-x1E;W-o zc#?Nt<~5ZHW92>PdSW~!Uz}4e^SKxHrCF$4B(I6VJ$Tn{+_$z*W#rfxZ)~=<{{bzx zmD2Ntfwu!!>c}OAxCP@4qE@V*A94!&fNsCieM9z{My(JLA5PL4HMCk88@i{uEwimV8`wRY=$t$s#~U74nRpYES>&%E&pJ<-jKVaV8f zA>Ao+aavT8b(+wkFXnaLe6vb=<-XIY7q9b8xXajoBMh62p#}LB_GooUb|MA6o4k?j&$Bb=rB`_O?v( z>gGka>}tcimkm02DHX?RrX(kJrmEImtM6`zt|QmmCdMYWqfhHcv5&J)bZZbBt6!QNI9!oYvy|EEge7Gm%k9rAEjwdku!Z?R z=b`;+{j>VP4>uV5RIRHjj@S}mm-#UxG}AfbWM+8Ak4#=hOU8H(e7mFBva!`xZ%@}v zr-7vB`dO}zUB2Z8xqfak{}j>u>F=JL@AjV$;BvTsC%Dymo^ns)FSf6b(yrEdyKCE- zb{;HzBU})e|y08b4zzn->fe!J(LH}9e7QGk7~X%#zTI#Np|_2_v`mdH8zPn6>&@B zMC2&0?ap8aA%IoN((s4QtK{Lnn4dSNd(~!DWmg46>{jI(b$HGS&R&6LK?J7$u`%wBa=;(<>LB^`;s)#}BDa&&TdbtK2Nu^27$yaxKWwqY* zP;bU=#Cf@e*QI~l^}_0Umf3!vVc&udPwDg_8`amN|V>(uN>3yVdRiBT=f%?8HOYM)yp$dubE!GfF(M*zjj8AMR|%S{F>@OnwUn+C_C6$aZS&Y^uik`J zs^mY;s2DrHFhYRrgZD-G@G`rbZ4V`64eS~2^cwW)9las9Q8|~hxaoa%w)28pYKM2e za4deDA6uR~!l7k{=aJbxT$&Z_A&qr|rh+!lqdxIA@$0xwK?7%1H`Q&D z*>u!Xqgmkg;)VDApI@h4KVE;l=0t00&sOVk`cT=&-n_PA{UQCJ;RD06!?Sw@Ek~2A zvxT;O?@Lm?#~8m~{cSaOIIGDw^I7g-h2(3=N=bH8XwLJ1(HX-_$86&1n)Hr6b=KF+ z^RM^+Sn)FPXIXEM@>!>4YSjMjwD$MK>tCh6buRl*&unBym&Z2*HVlqcjAh@jD$cz; zROQ~`-hH}#L7QNXK(LlEQdIk!K@jpyG$-sN7k(>S)}<&CHN-S>a< zHlLBLdWShwS}^PBCHAGTMV+{_hcmmElqk4&bB6nSUsg#*X6^6G_qbM`{H}CYQ4 z;%;0jd{Xq%qwJ@_fN5c@>D{Iu&z~K7)#6>%Vb$YW-s0*L{C(qS+bHg3Ujgq8_Iq!L zKTFCdV8is!a{BT{pZj`4UhOCuAJu*v5Z$3656cfMSKGYaC!;TK^mJvI!L{PSu~RQ9 z9;dF@5wtBo8RuIw@?*fdm-x@k`ghGWJ&#-;wXF4C!?|ny&Hf{+xhlG{Xsg0_nm23g zL*&-B@gmwnm^`$1&}%5Gl;X@id-t|a3t5HqW5o8K!sy{|%b#h+jZ};^4ycVi9f;1T z$*W=6nUvh}ksBFxLs*B0Hp$!HA9Aewl&SLRlM*ufP{E?DXH|_9pR%5u4UdUiKd(-= zo@%3cNjow+d^~o?=`FHaRfi=kmwN}Vz{L1gAI+g@w?)F@q@38++`5kL#vP3mo zf_{`B-N%Ei+}8alq~VW$bN8NScglrqa<68-Z0Cg^vyM#5hISb)lI`|YemY(h5rgq6fEC&dkRV^S}G>piZB(_3lS6!|P*JGy}gUJ$&vbr#YMzH{6 zC_0Hw`H=tcnH*h_NyDjm6$hMkcVf^lCJ*C|fw6ee1L3Cx4Y4HFQLy9*BMO#?C((!m zi0S&vQ*0&{cuWok`Q-!C$asVbxdMzrL0~);qF_;sh=&j?4SlGnK!IT@0#uV5La@k$ z9}^?SU@Q$!fbn?91;j%AD0m3QlE?@ZU0Y1R8=0 zlYUGLN*}N%1LCn%3YCn&&@Tw=1BS^IR5$4<&WMR2V(}zVG!hmePi6^YiDV)fMnEb= z5=@FXfGC>#+>LtwBghzcx;2n>o(M13Y8&}1+PY*(BjRn&JP zHmFnmDaKqdR4f7X1eFM)P4+BR)QuzBZh-9$Na|$gueR| z7hoEdEIuYEVpMc3=5V-t9<5PL@S71|?1Dm146+-(!(@v%?7Kr~L zO+~U`@wfsHCW{B5hndA>d|fVw&*Fd;(bH-Qq%%AK27y;h1pRiVq0p~8yfNDWY!yN? zKo84_+q*7T0DR&9+dmH=zeNHs>2Hy&*ep+$2i=3^$O22Fn?%XbWAofx7)SKDOiMQ3 zg$40F1S~Yh#6m_KN3O_>?!k2sFu{Ke))}x5x-!%z^dckl12^Hkjwf(+Wq2IAxAke1_O#k z8qO3(py0uI_HT^vA9e`%f%%sm_5b66W(Ga>ryUWz+yBN$cry5b`_qmD|GqBYgTZ!T zd0@~g80mo;uLCEy4W2?Eg2PAcUw?@zkF(%%`4C!FQ5WHMvb|U+BY{ LnANNGcNqK+?wi`= literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/ExpandIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Message/ExpandIcon.imageset/Contents.json new file mode 100644 index 00000000..13001193 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Message/ExpandIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "original.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/ExpandIcon.imageset/original.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Message/ExpandIcon.imageset/original.pdf new file mode 100644 index 0000000000000000000000000000000000000000..9f7203cc1aba47e54348718b897a59ea6eabc00d GIT binary patch literal 4641 zcmZu#c|4Tc8z+?Bk`^gRy`@bt``RLVWN9o(MH*vdG-jA3lO;r{D=lOfAz31qO7_ZB zxRj-owTKoeWeG|7z3&*Kd;85FGspLNp6|1s^Et5NSX^#-P!ycIiPP=z}z3z%uBU35gBpG`2HcEHR_Aj__PT44yy~ zBNiYQT4`$oTpou`bq53qccK`SB`wO8Y_E|pEotff73HVRqondrYh67Vz)V>jE=8TY zaPfVIMV5h!3=L%Mb!Ho`^x)Y!FI;5VK3h)C#{T4d;2#p{FV)StGOKwX&6{rQ%YAh( ztHj^$V?x{e#2?9%QWse=1}ILN{G*h^j(fZfoR!o@W#?80kv$7l7@e=>{DfXXLu{|* z`6Z$4v6Tux7VvBys*Wfx-d{FJ`BA1Mn3#{qPgqvbZr9T-pQIC`th%CBuTV{DJNef( zWxvW&rB#Yj6?@nDADOV%LL1x0Ynm(qYa={?q=-NdLdAHV3BxIP3*EE9_ZLGY^n1H( zaLJ+#M|O`Ht637F)%H8>*VfRB5}GfmCaF;8ux8DvUeg3JwO>yr$g5OIxo2m)PYM$! zk5)f*P8_~43rw1)Go*FX;4*o&ehq86j)BFw+0O%%>t!AXNHYUuJZAUbR~-w|SqQ9s z;C5ebE+}_KN4Z(5;N*6S)JVY3t1DMZz`*+#BOcc=dQ ztD*Jy48>x_Fr6j!j`bUt%SBLEWY1Nth(awH36SZe2$rr?Oc(Zb+6##Gv-b(pJ6*=s z`pT>dNc(CkSRqGQcNy3{=aCNDQqu<93@i;d_v`ouzvysVCXbLJ03QyT!xw^p) zx72(Ww*IN>&S3zz?8^C9!3cxs)IC?@8#+F0`><$OX?)A+lMVM{s0k>hne2vD5vQt! zmz~_u<}o!f(zVOqsVRr&1$hV#O{BVT2EAJU;rG>(g)7%Gg zZ%8CGLeXoX7vLrR@`9?+?|#JBR)K1%qBekh_`cJ0VB>(=*og`5#C+Yr!#W;oWfu#A z?*y;aQ%DZ;2t^sitl7Lc>z*Giph_++Erw5-0VBJEArMwTFpQY zp=NItzUEs^S@^~()heH=+$hJ5j+^~$e??7Po2C1J5}0=1viTY%*~5ZrOg(TZ%Qa_t zMofxbhQMkd?oHuhi)u>s!L#X?Z}7}|!y7j(HEc@F&#?bUa8vk(;h#I5rqa-S+u(ro z=Zy!D?z{J!wAhl($Ont`zL)0P$fY@DRM?u>YFLp|F&Tr5z<0AQ$W6$VDC0CgIDS3E zPtGdY?1*=~W69U~7O}$N$w4g>_uK=at?K;S#hK%rZ<^nPAETQ#E^AcYitItuItpC6 zWEAX#HtKe?vd8GK_!ZCM4#btmG2-UnkqisBOxL2L>)j$<`L5=!)^{B4$hEF-U3S~C zKC*Y!u(P*HS-f^yYI1kFdP8PoZ&PdozR@8$KD905hw+7s%IBfawlwrL#y7rv;h1$# z_uiq&KE7 zB)ob}MBDYhGp{gCGEVhsVVi4TnH@e>m0h=z*6)hIn}(qj2Js`c~Vefd9}z8ps8v;R)=sP{VKnZaA`)EJ{%tM_j2u5+pB-7b~K zDlb@!SU9C)E&R7)6Cx7!Y`uUa9H@1)za@BX`0`Y@)!ViFw35e#tN4+{y@j6bSsjVR zeMLj~>yVg&2F69xru8E-WPnS;J{94B{YzISq|Z`oj(cUffVK0&n4&XI<1XGb$f zV+ZdHIecmB4H=mCwXKi%@P#wCh5t#zf6ipsuMX*+fQtcx0qLe@(PyG=Yn_T7M|RvD z?!pAoE9hE*;6=4Un_X(lvz}gIDBl=y9$pq14r<)ORg? zyk*^%;))(&ituB_s}P@4%R)@mHPs15?PqK*C>azOb){6KxTRj-=bhVrkE7X&*oyS_ zh-}FEwD+a$ja-XEekZ~vRy>Wng^r2&8WVv|A_pe8J>_X`E5IE4tLDwB&hnn3q1}Qd zzROOaCyytX+IzMON=+xq$Ii;$4Q>oJkv63u1Xn&p3)T(zSsiX1xYpA1wdb*t?{2ey z$jRX-ljbW~k#SdAqg|q+qBod(?S7r4y&hQDmB^s&jdt8Z)ACb!MUORJUd z@$#kbp=uQ-5Jcnc;|G$mrow`O*3P{5GEyyF7uzz=!g* zA91E1)_-5m9?fm>&v{-jTqXTRx>}mi5}yAeXnfA-$_e{KiZ-Qde}i47WzmhnpKD$v z{}T3xsGfISr9m3(&FK74w)u6|J2&CSMp`p1wlc9PxM_HzY9jBZZCSz9ks8k~&)&10 zOFH-q`23A{3*{!^xA5_^)(z>b$)0c4M`cozvn-iYdI%R{Vr{! zqIll3D~u}<%es;0kLC2PC|C0AWsMH@zp9ar&OJ0x#L28oeP6LR`o`y%2{*5lJS~02 z5&kk7GB1fYzt{5f#q*=D+k9)fZ2R0R+uZ#^e{3D^7)QPuDCWLJ{OAu0q|5jPZJGT= z!BElU%RqnF>pkU@HMVc|%N{5kKU*DPlvy@Bapq;!lk_!vLUt9U zBK_;eeh%67WB<9;_`bES@3H&iwvE0USoiF{JAIgU=CV^tH)LmCs$hGMhp3hU^OX63j2 z6voCq2=rqk+Z3H1jJPy>&QbgPS%onFXz{Y0=haP=pV6P5kBm#$yr@CHkz}uZMK?M& zax#9;*&XsaHOHiER{Mso0pk2?ee%h=ok?>3`@EJHIUhP&dDqSjFICHsqWEBB2e@ck zk4-;I$f%RwJ$)BBo?+t6?$<4r@4obN-tk#^;9lcp^1c45&nC-|qlcbf{ zhsC?+7%xl$uirZ5RBl{#iT}_fahuwu?8u@Dl|~m`W%bb@%wh#yL*bj~v7#2%mrga7=y#Xl3|1C0O62{FsV2VNn$iWlCGoxaE|}&2NQ4w(D>;H(1hs#pw>eE zhy*ka1J0not^S`>!b1ao(?m@-lL>Q@$xzeDAR4Lz0Zk&HKnTM@ZqRrbLqS!;LMuE1 zJ=Gux!%lSw;>03wQ>{c}5UA;1LK@;>Ax~!qIfBgKnZko41Q~_FWAGq(YVNRNgg}50 zaWy0h8X!_9pfF-}NNb9KC<+-Y(8X*{v&=a zg#5+??VsP6Y#DSfI)}ocyU-!maFB2@3>aJwCiS=hn`Xn{G3g+W!>7X>Q;tknE^Lt+ zg~N8{)1dzvY%*dTai?lb$wf-=7jnvY6Pv?0!eCLE|B_4C%&4p*;3hgtEEScUD=C49 zM$`&@Dv!!!ANhAO0w{0*+CwN{suA!vNWeoA2f8RoFlcXy#2{-L!w{hn{{w?|$sZU2 zI$hw;Z?>dA`LOU#{*NBCmH+U8L*f7MfkR`Uo=p4Tai|O?oddvaFfo9dya_r9>`_Dv x7DK{n{O2!G>ycJ$HV=f`DjFlCHN%?@R|Kjsmq+FBL^Z-;u{dD;dc!?N{{!ow+VlVb literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Instant View/Back.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Instant View/Back.imageset/Contents.json index bfad7e17..eac666dc 100644 --- a/submodules/TelegramUI/Images.xcassets/Instant View/Back.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Instant View/Back.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "ic_left.pdf", + "filename" : "browser_back_30.pdf", "idiom" : "universal" } ], diff --git a/submodules/TelegramUI/Images.xcassets/Instant View/Back.imageset/browser_back_30.pdf b/submodules/TelegramUI/Images.xcassets/Instant View/Back.imageset/browser_back_30.pdf new file mode 100644 index 0000000000000000000000000000000000000000..16eb92b04b573bce563569a3e30610e53daeb270 GIT binary patch literal 4189 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 literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Instant View/Bookmark.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Instant View/Bookmark.imageset/Contents.json index 09b651c2..6193eeb8 100644 --- a/submodules/TelegramUI/Images.xcassets/Instant View/Bookmark.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Instant View/Bookmark.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "Bookmark.pdf", + "filename" : "browser_bookmark_30.pdf", "idiom" : "universal" } ], diff --git a/submodules/TelegramUI/Images.xcassets/Instant View/Bookmark.imageset/browser_bookmark_30.pdf b/submodules/TelegramUI/Images.xcassets/Instant View/Bookmark.imageset/browser_bookmark_30.pdf new file mode 100644 index 0000000000000000000000000000000000000000..3640d0baba96494f4d2a128ca4e05bc642e1c08e GIT binary patch literal 5104 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 literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Instant View/Browser.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Instant View/Browser.imageset/Contents.json index c45b00a1..2a075937 100644 --- a/submodules/TelegramUI/Images.xcassets/Instant View/Browser.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Instant View/Browser.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "Browser.pdf", + "filename" : "browser_safari_30.pdf", "idiom" : "universal" } ], diff --git a/submodules/TelegramUI/Images.xcassets/Instant View/Browser.imageset/browser_safari_30.pdf b/submodules/TelegramUI/Images.xcassets/Instant View/Browser.imageset/browser_safari_30.pdf new file mode 100644 index 0000000000000000000000000000000000000000..6ed6502e7c78f47f30b8ff5f5edd459107a649ea GIT binary patch literal 4956 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 literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Instant View/OpenDocument.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Instant View/OpenDocument.imageset/Contents.json index c60877c4..223317f3 100644 --- a/submodules/TelegramUI/Images.xcassets/Instant View/OpenDocument.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Instant View/OpenDocument.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "docviewer_24.pdf", + "filename" : "browser_doc_30.pdf", "idiom" : "universal" } ], diff --git a/submodules/TelegramUI/Images.xcassets/Instant View/OpenDocument.imageset/browser_doc_30.pdf b/submodules/TelegramUI/Images.xcassets/Instant View/OpenDocument.imageset/browser_doc_30.pdf new file mode 100644 index 0000000000000000000000000000000000000000..d0d4f47124dacf9dabfa6381105331b75e7d4c8f GIT binary patch literal 5672 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 literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Instant View/Search.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Instant View/Search.imageset/Contents.json new file mode 100644 index 00000000..17c8b580 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Instant View/Search.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "browser_search_30.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Instant View/Search.imageset/browser_search_30.pdf b/submodules/TelegramUI/Images.xcassets/Instant View/Search.imageset/browser_search_30.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4a5636aa33a56c7b47d3a18161984290d732564a GIT binary patch literal 4516 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 Qj)(+2f?2c1V5i~#0dOV6YXATM literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Instant View/Share.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Instant View/Share.imageset/Contents.json new file mode 100644 index 00000000..e9078769 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Instant View/Share.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "browser_share_30.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Instant View/Share.imageset/browser_share_30.pdf b/submodules/TelegramUI/Images.xcassets/Instant View/Share.imageset/browser_share_30.pdf new file mode 100644 index 0000000000000000000000000000000000000000..23c7c6d41c4f5b7115e697640491212159dfd6c2 GIT binary patch literal 5208 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

U|=e3H>(BEZ5DFjDtRUg(dmp17HcyNCi@;KKIJ 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 literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Dice.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Premium/Dice.imageset/Contents.json new file mode 100644 index 00000000..d45ae279 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/Dice.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "dice.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Dice.imageset/dice.pdf b/submodules/TelegramUI/Images.xcassets/Premium/Dice.imageset/dice.pdf new file mode 100644 index 0000000000000000000000000000000000000000..43f73ca9aee17c5d584b100fc6cb6e322244ccd6 GIT binary patch literal 4749 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 literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Sources/AppDelegate.swift b/submodules/TelegramUI/Sources/AppDelegate.swift index eab0c417..99b50385 100644 --- a/submodules/TelegramUI/Sources/AppDelegate.swift +++ b/submodules/TelegramUI/Sources/AppDelegate.swift @@ -1,5 +1,5 @@ import UIKit -import SwiftSignalKit +@preconcurrency import SwiftSignalKit import Display import TelegramCore import UserNotifications @@ -41,7 +41,8 @@ import MediaEditor import TelegramUIDeclareEncodables import ContextMenuScreen import MetalEngine -import RecaptchaEnterprise +import RecaptchaEnterpriseSDK +import NavigationBarImpl #if canImport(AppCenter) import AppCenter @@ -330,6 +331,10 @@ private func extractAccountManagerState(records: AccountRecordsView Void)? var declineImpl: (() -> Void)? - let controller = TermsOfServiceController(presentationData: presentationData, text: termsOfServiceUpdate.text, entities: termsOfServiceUpdate.entities, ageConfirmation: termsOfServiceUpdate.ageConfirmation, signingUp: false, accept: { proccedBot in + let controller = TermsOfServiceController(context: strongSelf.context, presentationData: presentationData, text: termsOfServiceUpdate.text, entities: termsOfServiceUpdate.entities, ageConfirmation: termsOfServiceUpdate.ageConfirmation, signingUp: false, accept: { proccedBot in acceptImpl?(proccedBot) }, decline: { declineImpl?() diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift index b90870ac..abb4967a 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift @@ -124,6 +124,9 @@ import AdsInfoScreen import PostSuggestionsSettingsScreen import ChatSendStarsScreen import ChatSendAsContextMenu +import GlobalControlPanelsContext +import ComponentFlow +import ComponentDisplayAdapters extension ChatControllerImpl { func reloadChatLocation(chatLocation: ChatLocation, chatLocationContextHolder: Atomic, historyNode: ChatHistoryListNodeImpl, apply: @escaping ((ContainedViewLayoutTransition?) -> Void) -> Void) { @@ -228,17 +231,6 @@ extension ChatControllerImpl { } self.navigationBar?.userInfo = contentData.state.navigationUserInfo - if self.chatTitleView?.titleContent != contentData.state.chatTitleContent { - var animateTitleContents = false - if !synchronous, case let .messageOptions(_, _, info) = self.subject, case .reply = info { - animateTitleContents = true - } - if animateTitleContents && self.chatTitleView?.titleContent != nil { - self.chatTitleView?.animateLayoutTransition() - } - self.chatTitleView?.titleContent = contentData.state.chatTitleContent - } - if let infoAvatar = contentData.state.infoAvatar { switch infoAvatar { case let .peer(peer, imageOverride, contextActionIsEnabled, accessibilityLabel): @@ -381,6 +373,13 @@ extension ChatControllerImpl { if previousState.pinnedMessage != contentData.state.pinnedMessage { animated = true } + if previousState.translationState?.isEnabled != contentData.state.translationState?.isEnabled { + animated = true + } + if previousState.chatTitleContent != contentData.state.chatTitleContent { + animated = true + } + var transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.4, curve: .spring) : .immediate if let forceAnimationTransition { transition = forceAnimationTransition @@ -389,6 +388,22 @@ extension ChatControllerImpl { transition = .immediate } + if let chatTitleContent = contentData.state.chatTitleContent { + var titleTransition = ComponentTransition(transition) + if case .messageOptions = self.subject { + titleTransition = titleTransition.withAnimation(.none) + } + self.chatTitleView?.update( + context: self.context, + theme: self.presentationData.theme, + strings: self.presentationData.strings, + dateTimeFormat: self.presentationData.dateTimeFormat, + nameDisplayOrder: self.presentationData.nameDisplayOrder, + content: chatTitleContent, + transition: titleTransition + ) + } + self.updateChatPresentationInterfaceState(transition: transition, interactive: false, { presentationInterfaceState in var presentationInterfaceState = presentationInterfaceState presentationInterfaceState = presentationInterfaceState.updatedPeer({ _ in @@ -677,7 +692,7 @@ extension ChatControllerImpl { self.displayNode = ChatControllerNode(context: self.context, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder, subject: self.subject, controllerInteraction: self.controllerInteraction!, chatPresentationInterfaceState: self.presentationInterfaceState, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, navigationBar: self.navigationBar, statusBar: self.statusBar, backgroundNode: self.chatBackgroundNode, controller: self) - if let currentItem = self.tempVoicePlaylistCurrentItem { + if let currentItem = self.globalControlPanelsContext?.tempVoicePlaylistCurrentItem { self.chatDisplayNode.historyNode.voicePlaylistItemChanged(nil, currentItem) } @@ -988,7 +1003,15 @@ extension ChatControllerImpl { } if let errorText = errorText { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + let alertController = textAlertController( + context: strongSelf.context, + title: nil, + text: errorText, + actions: [ + TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {}) + ] + ) + strongSelf.present(alertController, in: .window(.root)) return } } @@ -1092,10 +1115,15 @@ extension ChatControllerImpl { strongSelf.chatDisplayNode.historyNode.scrollToEndOfHistory() case let .businessLinkSetup(link): if messages.count > 1 { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.BusinessLink_AlertTextLimitText, actions: [ - TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {}) - ]), in: .window(.root)) - + let alertController = textAlertController( + context: strongSelf.context, + title: nil, + text: strongSelf.presentationData.strings.BusinessLink_AlertTextLimitText, + actions: [ + TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {}) + ] + ) + strongSelf.present(alertController, in: .window(.root)) return } @@ -1871,8 +1899,9 @@ extension ChatControllerImpl { titleString = self.presentationData.strings.Chat_DeletePaidMessageTon_Title textString = self.presentationData.strings.Chat_DeletePaidMessageTon_Text } - self.present(standardTextAlertController( - theme: AlertControllerTheme(presentationData: self.presentationData), + + let alertController = textAlertController( + context: self.context, title: titleString, text: textString, actions: [ @@ -1884,9 +1913,9 @@ extension ChatControllerImpl { }), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_Cancel, action: {}) ], - actionLayout: .vertical, - parseMarkdown: true - ), in: .window(.root)) + actionLayout: .vertical + ) + self.present(alertController, in: .window(.root)) } if let contextController { contextController.dismiss(completion: commit) @@ -3638,7 +3667,7 @@ extension ChatControllerImpl { } } - let controller = chatTextLinkEditController(sharedContext: strongSelf.context.sharedContext, updatedPresentationData: strongSelf.updatedPresentationData, account: strongSelf.context.account, text: text?.string ?? "", link: link, allowEmpty: true, apply: { [weak self] link in + let controller = chatTextLinkEditController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, text: text?.string ?? "", link: link, apply: { [weak self] link in if let strongSelf = self, let inputMode = inputMode, let selectionRange = selectionRange { if let link { if !link.isEmpty { @@ -4590,6 +4619,44 @@ extension ChatControllerImpl { }) } + var mediaPlayback = false + var liveLocationMode: GlobalControlPanelsContext.LiveLocationMode? + if case .standard = self.mode { + //mediaAccessoryPanelVisibility = .specific(size: .compact) + mediaPlayback = true + liveLocationMode = self.chatLocation.peerId.flatMap(GlobalControlPanelsContext.LiveLocationMode.peer) + } + + var groupCallPanelSource: EnginePeer.Id? + switch self.chatLocation { + case let .peer(peerId): + switch self.subject { + case .message, .none: + groupCallPanelSource = peerId + default: + break + } + case .replyThread, .customChatContents: + break + } + + let globalControlPanelsContext = GlobalControlPanelsContext( + context: self.context, + mediaPlayback: mediaPlayback, + liveLocationMode: liveLocationMode, + groupCalls: groupCallPanelSource, + chatListNotices: false + ) + self.globalControlPanelsContext = globalControlPanelsContext + self.globalControlPanelsContextStateDisposable = (globalControlPanelsContext.state + |> deliverOnMainQueue).startStrict(next: { [weak self] state in + guard let self else { + return + } + self.globalControlPanelsContextState = state + self.requestLayout(transition: .animated(duration: 0.4, curve: .spring)) + }) + self.displayNodeDidLoad() } @@ -4745,7 +4812,18 @@ extension ChatControllerImpl { return true } }) - strongSelf.chatTitleView?.inputActivities = (peerId, displayActivities) + strongSelf.chatTitleView?.updateActivities( + activities: ChatTitleComponent.Activities( + peerId: peerId, + items: displayActivities.map { item -> ChatTitleComponent.Activities.Item in + return ChatTitleComponent.Activities.Item( + peer: EnginePeer(item.0), + activity: item.1 + ) + } + ), + transition: .spring(duration: 0.4) + ) strongSelf.peerInputActivitiesPromise.set(.single(activities)) diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerMediaRecording.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerMediaRecording.swift index e7be8359..807b2f12 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerMediaRecording.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerMediaRecording.swift @@ -361,49 +361,54 @@ extension ChatControllerImpl { strongSelf.recorderDataDisposable.set(nil) } else { let randomId = Int64.random(in: Int64.min ... Int64.max) - - let resource = LocalFileMediaResource(fileId: randomId) - strongSelf.context.account.postbox.mediaBox.storeResourceData(resource.id, data: data.compressedData) - + let originalData = data.compressedData + let duration = data.duration let waveformBuffer: Data? = data.waveform - let correlationId = Int64.random(in: 0 ..< Int64.max) - var usedCorrelationId = false + // GHOSTGRAM: Check if Voice Morpher needs processing + let needsProcessing = VoiceMorpherManager.shared.isEnabled && VoiceMorpherManager.shared.selectedPreset != .disabled - var shouldAnimateMessageTransition = strongSelf.chatDisplayNode.shouldAnimateMessageTransition - if strongSelf.chatLocation.threadId == nil, let channel = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isMonoForum, let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = strongSelf.presentationInterfaceState.renderedPeer?.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.hasPermission(.manageDirect) { - shouldAnimateMessageTransition = false - } - - if shouldAnimateMessageTransition, let textInputPanelNode = strongSelf.chatDisplayNode.textInputPanelNode, let micButton = textInputPanelNode.micButton { - usedCorrelationId = true - strongSelf.chatDisplayNode.messageTransitionNode.add(correlationId: correlationId, source: .audioMicInput(ChatMessageTransitionNodeImpl.Source.AudioMicInput(micButton: micButton)), initiated: { - guard let strongSelf = self else { - return + if needsProcessing { + // Show processing indicator + let statusController = OverlayStatusController(theme: strongSelf.presentationData.theme, type: .loading(cancelled: nil)) + statusController.statusBar.statusBarStyle = strongSelf.presentationData.theme.rootController.statusBarStyle.style + strongSelf.present(statusController, in: .window(.root)) + + // Process async + VoiceMorpherEngine.shared.processOggData(originalData) { [weak self, weak statusController] result in + DispatchQueue.main.async { + statusController?.dismiss() + + guard let strongSelf = self else { return } + + let processedData: Data + switch result { + case .success(let data): + processedData = data + case .failure: + processedData = originalData // fallback to original on error + } + + strongSelf.finishSendingVoiceMessage( + randomId: randomId, + processedData: processedData, + duration: duration, + waveformBuffer: waveformBuffer, + viewOnce: viewOnce + ) } - strongSelf.audioRecorder.set(.single(nil)) - }) - } else { - strongSelf.audioRecorder.set(.single(nil)) - } - - strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({ - if let strongSelf = self { - strongSelf.chatDisplayNode.collapseInput() - - strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { - $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil).withUpdatedPostSuggestionState(nil) } - }) } - }, usedCorrelationId ? correlationId : nil) - - var attributes: [MessageAttribute] = [] - if viewOnce { - attributes.append(AutoremoveTimeoutMessageAttribute(timeout: viewOnceTimeout, countdownBeginTime: nil)) + } else { + // No processing needed, send directly + strongSelf.finishSendingVoiceMessage( + randomId: randomId, + processedData: originalData, + duration: duration, + waveformBuffer: waveformBuffer, + viewOnce: viewOnce + ) } - strongSelf.sendMessages([.message(text: "", attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: Int64(data.compressedData.count), attributes: [.Audio(isVoice: true, duration: Int(data.duration), title: nil, performer: nil, waveform: waveformBuffer)], alternativeRepresentations: [])), threadId: strongSelf.chatLocation.threadId, replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: [])]) - strongSelf.recorderFeedback?.tap() strongSelf.recorderFeedback = nil strongSelf.recorderDataDisposable.set(nil) @@ -774,4 +779,50 @@ extension ChatControllerImpl { self.videoRecorderValue?.sendVideoRecording(silentPosting: silentPosting, scheduleTime: scheduleTime, messageEffect: messageEffect) } } + + // MARK: - GHOSTGRAM: Voice Morpher Helper + + private func finishSendingVoiceMessage( + randomId: Int64, + processedData: Data, + duration: Double, + waveformBuffer: Data?, + viewOnce: Bool + ) { + let resource = LocalFileMediaResource(fileId: randomId) + self.context.account.postbox.mediaBox.storeResourceData(resource.id, data: processedData) + + let correlationId = Int64.random(in: 0 ..< Int64.max) + var usedCorrelationId = false + + var shouldAnimateMessageTransition = self.chatDisplayNode.shouldAnimateMessageTransition + if self.chatLocation.threadId == nil, let channel = self.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isMonoForum, let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = self.presentationInterfaceState.renderedPeer?.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.hasPermission(.manageDirect) { + shouldAnimateMessageTransition = false + } + + if shouldAnimateMessageTransition, let textInputPanelNode = self.chatDisplayNode.textInputPanelNode, let micButton = textInputPanelNode.micButton { + usedCorrelationId = true + self.chatDisplayNode.messageTransitionNode.add(correlationId: correlationId, source: .audioMicInput(ChatMessageTransitionNodeImpl.Source.AudioMicInput(micButton: micButton)), initiated: { [weak self] in + self?.audioRecorder.set(.single(nil)) + }) + } else { + self.audioRecorder.set(.single(nil)) + } + + self.chatDisplayNode.setupSendActionOnViewUpdate({ [weak self] in + if let strongSelf = self { + strongSelf.chatDisplayNode.collapseInput() + strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { + $0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil).withUpdatedPostSuggestionState(nil) } + }) + } + }, usedCorrelationId ? correlationId : nil) + + var attributes: [MessageAttribute] = [] + if viewOnce { + attributes.append(AutoremoveTimeoutMessageAttribute(timeout: viewOnceTimeout, countdownBeginTime: nil)) + } + + self.sendMessages([.message(text: "", attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: Int64(processedData.count), attributes: [.Audio(isVoice: true, duration: Int(duration), title: nil, performer: nil, waveform: waveformBuffer)], alternativeRepresentations: [])), threadId: self.chatLocation.threadId, replyToMessageId: self.presentationInterfaceState.interfaceState.replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: [])]) + } } diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerNavigationButtonAction.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerNavigationButtonAction.swift index c6306556..7ea7d6cb 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerNavigationButtonAction.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerNavigationButtonAction.swift @@ -280,13 +280,19 @@ extension ChatControllerImpl { return } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationText, actions: [ - TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { - }), - TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationAction, action: { - beginClear(.scheduledMessages) - }) - ], parseMarkdown: true), in: .window(.root)) + let alertController = textAlertController( + context: strongSelf.context, + title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle, + text: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationText, + actions: [ + TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { + }), + TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationAction, action: { + beginClear(.scheduledMessages) + }) + ] + ) + strongSelf.present(alertController, in: .window(.root)) })) } else { if let _ = canClearForMyself ?? canClearForEveryone { @@ -310,13 +316,19 @@ extension ChatControllerImpl { return } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationTitle, text: confirmationText, actions: [ - TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { - }), - TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationAction, action: { - beginClear(.forEveryone) - }) - ], parseMarkdown: true), in: .window(.root)) + let alertController = textAlertController( + context: strongSelf.context, + title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationTitle, + text: confirmationText, + actions: [ + TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { + }), + TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteForEveryoneConfirmationAction, action: { + beginClear(.forEveryone) + }) + ] + ) + strongSelf.present(alertController, in: .window(.root)) })) } if let canClearForMyself = canClearForMyself { @@ -330,13 +342,19 @@ extension ChatControllerImpl { items.append(ActionSheetButtonItem(title: text, color: .destructive, action: { [weak self, weak actionSheet] in actionSheet?.dismissAnimated() if mainPeer.id == context.account.peerId, let strongSelf = self { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle, text: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationText, actions: [ - TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { - }), - TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationAction, action: { - beginClear(.forLocalPeer) - }) - ], parseMarkdown: true), in: .window(.root)) + let alertController = textAlertController( + context: strongSelf.context, + title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationTitle, + text: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationText, + actions: [ + TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { + }), + TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.ChatList_DeleteSavedMessagesConfirmationAction, action: { + beginClear(.forLocalPeer) + }) + ] + ) + strongSelf.present(alertController, in: .window(.root)) } else { beginClear(.forLocalPeer) } diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift index ff318176..d4e1caea 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift @@ -17,6 +17,7 @@ import PremiumUI import TooltipUI import TopMessageReactions import TelegramNotices +import PresentationDataUtils extension ChatControllerImpl { func openMessageContextMenu(message: Message, selectAll: Bool, node: ASDisplayNode, frame: CGRect, anyRecognizer: UIGestureRecognizer?, location: CGPoint?) -> Void { @@ -433,9 +434,15 @@ extension ChatControllerImpl { if case let .known(reactionSettings) = reactionSettings, let starsAllowed = reactionSettings.starsAllowed, !starsAllowed { if let peer = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.Chat_ToastStarsReactionsDisabled(peer.debugDisplayTitle).string, actions: [ - TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_OK, action: {}) - ]), in: .window(.root)) + let alertController = textAlertController( + context: strongSelf.context, + title: nil, + text: strongSelf.presentationData.strings.Chat_ToastStarsReactionsDisabled(peer.debugDisplayTitle).string, + actions: [ + TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_OK, action: {}) + ] + ) + strongSelf.present(alertController, in: .window(.root)) } return } diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageFactCheck.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageFactCheck.swift index 481e550d..ca5ac6c4 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageFactCheck.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageFactCheck.swift @@ -16,7 +16,12 @@ extension ChatControllerImpl { break } } - let controller = factCheckAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, value: currentText, entities: currentEntities, apply: { [weak self] text, entities in + let controller = factCheckAlertController( + context: self.context, + updatedPresentationData: self.updatedPresentationData, + value: currentText, + entities: currentEntities, + apply: { [weak self] text, entities in guard let self else { return } diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenPeer.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenPeer.swift index a97982eb..1c93cb42 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenPeer.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenPeer.swift @@ -357,7 +357,9 @@ extension ChatControllerImpl { }))) } - items.append(.separator) + if !items.isEmpty { + items.append(.separator) + } items.append(.action(ContextMenuActionItem(text: strings.Conversation_Search, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Search"), color: theme.contextMenu.primaryColor) }, action: { [weak self] action in diff --git a/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift b/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift index dafe1781..ea9c5938 100644 --- a/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift +++ b/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift @@ -14,6 +14,7 @@ import PresentationDataUtils import TelegramCallsUI import AttachmentUI import WebUI +import LegacyChatHeaderPanelComponent func updateChatPresentationInterfaceStateImpl( selfController: ChatControllerImpl, diff --git a/submodules/TelegramUI/Sources/ChatBusinessLinkTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatBusinessLinkTitlePanelNode.swift index 4fe8585e..fd0e74da 100644 --- a/submodules/TelegramUI/Sources/ChatBusinessLinkTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatBusinessLinkTitlePanelNode.swift @@ -14,6 +14,7 @@ import TelegramCore import SwiftSignalKit import UndoUI import ShareController +import LegacyChatHeaderPanelComponent private final class ChatBusinessLinkTitlePanelComponent: Component { let context: AccountContext diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index f1c29167..6ba6d498 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -144,6 +144,9 @@ import FaceScanScreen import ChatThemeScreen import ChatTextInputPanelNode import ChatInputAccessoryPanel +import GlobalControlPanelsContext +import ChatSearchNavigationContentNode +import ChatAgeRestrictionAlertController public final class ChatControllerOverlayPresentationData { public let expandData: (ASDisplayNode?, () -> Void) @@ -293,7 +296,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let chatThemePromise = Promise() let chatWallpaperPromise = Promise() - var chatTitleView: ChatTitleView? + var chatTitleView: ChatNavigationBarTitleView? var leftNavigationButton: ChatNavigationButton? var rightNavigationButton: ChatNavigationButton? var secondaryRightNavigationButton: ChatNavigationButton? @@ -524,9 +527,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let joinChannelDisposable = MetaDisposable() var shouldDisplayDownButton = false - - var hasEmbeddedTitleContent = false - var isEmbeddedTitleContentHidden = false var chatLocationContextHolder: Atomic @@ -626,6 +626,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var lastPostedScheduledMessagesToastTimestamp: Double = 0.0 var postedScheduledMessagesEventsDisposable: Disposable? + var globalControlPanelsContext: GlobalControlPanelsContext? + var globalControlPanelsContextState: GlobalControlPanelsContext.State? + var globalControlPanelsContextStateDisposable: Disposable? + public init( context: AccountContext, chatLocation: ChatLocation, @@ -675,26 +679,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.chatBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true, useSharedAnimationPhase: useSharedAnimationPhase) self.wallpaperReady.set(self.chatBackgroundNode.isReady) - var locationBroadcastPanelSource: LocationBroadcastPanelSource - var groupCallPanelSource: GroupCallPanelSource - - switch chatLocation { - case let .peer(peerId): - locationBroadcastPanelSource = .peer(peerId) - switch subject { - case .message, .none: - groupCallPanelSource = .peer(peerId) - default: - groupCallPanelSource = .none - } - case .replyThread: - locationBroadcastPanelSource = .none - groupCallPanelSource = .none - case .customChatContents: - locationBroadcastPanelSource = .none - groupCallPanelSource = .none - } - var presentationData = context.sharedContext.currentPresentationData.with { $0 } if let forcedTheme = self.forcedTheme { presentationData = presentationData.withUpdated(theme: forcedTheme) @@ -707,7 +691,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.stickerSettings = ChatInterfaceStickerSettings() - self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: context.account.peerId, mode: mode, chatLocation: chatLocation, subject: subject, peerNearbyData: peerNearbyData, greetingData: context.prefetchManager?.preloadedGreetingSticker, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil, businessIntro: nil) + self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: context.account.peerId, mode: mode, chatLocation: chatLocation, subject: subject, peerNearbyData: peerNearbyData, greetingData: context.prefetchManager?.preloadedGreetingSticker, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil, businessIntro: nil) if case let .customChatContents(customChatContents) = subject { switch customChatContents.kind { @@ -726,25 +710,20 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.presentationInterfaceStatePromise = ValuePromise(self.presentationInterfaceState) - var mediaAccessoryPanelVisibility = MediaAccessoryPanelVisibility.none - if case .standard = mode { - mediaAccessoryPanelVisibility = .specific(size: .compact) - } else { - locationBroadcastPanelSource = .none - groupCallPanelSource = .none - } let navigationBarPresentationData: NavigationBarPresentationData? switch mode { case .inline, .standard(.embedded): navigationBarPresentationData = nil default: - navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData, hideBackground: self.context.sharedContext.immediateExperimentalUISettings.playerEmbedding ? true : false, hideBadge: false) + navigationBarPresentationData = NavigationBarPresentationData(presentationData: self.presentationData, hideBackground: false, hideBadge: false, style: .glass) } - self.moreBarButton = MoreHeaderButton(color: self.presentationData.theme.rootController.navigationBar.buttonColor) + self.moreBarButton = MoreHeaderButton(color: self.presentationData.theme.chat.inputPanel.panelControlColor) self.moreBarButton.isUserInteractionEnabled = true - super.init(context: context, navigationBarPresentationData: navigationBarPresentationData, mediaAccessoryPanelVisibility: mediaAccessoryPanelVisibility, locationBroadcastPanelSource: locationBroadcastPanelSource, groupCallPanelSource: groupCallPanelSource) + super.init(context: context, navigationBarPresentationData: navigationBarPresentationData) + + self._hasGlassStyle = true self.automaticallyControlPresentationContextLayout = false self.blocksBackgroundWhenInOverlay = true @@ -754,6 +733,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.ready.set(.never()) + self.chatBackgroundNode.isDarkUpdated = { [weak self] in + guard let self else { + return + } + self.updateStatusBarPresentation(animated: false) + } + self.scrollToTop = { [weak self] in guard let strongSelf = self, strongSelf.isNodeLoaded else { return @@ -800,12 +786,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G textString = strongSelf.presentationData.strings.QuickReply_ChatRemoveAwayMessage_Text } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: titleString, text: textString, actions: [ - TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), - TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.QuickReply_ChatRemoveGeneric_DeleteAction, action: { [weak strongSelf] in - strongSelf?.dismiss() - }) - ]), in: .window(.root)) + let alertController = textAlertController( + context: strongSelf.context, + title: titleString, + text: textString, + actions: [ + TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), + TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.QuickReply_ChatRemoveGeneric_DeleteAction, action: { [weak strongSelf] in + strongSelf?.dismiss() + }) + ] + ) + strongSelf.present(alertController, in: .window(.root)) return false } @@ -817,12 +809,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let message = inputText.string if message != link.message || entities != link.entities { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.Business_Links_AlertUnsavedText, actions: [ - TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), - TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.Business_Links_AlertUnsavedAction, action: { [weak strongSelf] in - strongSelf?.dismiss() - }) - ]), in: .window(.root)) + let alertController = textAlertController( + context: strongSelf.context, + title: nil, + text: strongSelf.presentationData.strings.Business_Links_AlertUnsavedText, + actions: [ + TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), + TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.Business_Links_AlertUnsavedAction, action: { [weak strongSelf] in + strongSelf?.dismiss() + }) + ] + ) + strongSelf.present(alertController, in: .window(.root)) return false } @@ -1833,9 +1831,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if case let .known(reactionSettings) = reactionSettings, let starsAllowed = reactionSettings.starsAllowed, !starsAllowed { if let peer = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.Chat_ToastStarsReactionsDisabled(peer.debugDisplayTitle).string, actions: [ - TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_OK, action: {}) - ]), in: .window(.root)) + let alertController = textAlertController( + context: strongSelf.context, + title: nil, + text: strongSelf.presentationData.strings.Chat_ToastStarsReactionsDisabled(peer.debugDisplayTitle).string, + actions: [ + TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_OK, action: {}) + ] + ) + strongSelf.present(alertController, in: .window(.root)) } return } @@ -2448,6 +2452,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } let controller = self.context.sharedContext.makeGiftOfferScreen( context: self.context, + updatedPresentationData: self.updatedPresentationData, gift: gift, peer: peer, amount: amount, @@ -2477,7 +2482,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if message.effectivelyIncoming(strongSelf.context.account.peerId) { switch buttonType { case 0: - let promptController = promptController(sharedContext: strongSelf.context.sharedContext, updatedPresentationData: strongSelf.updatedPresentationData, text: strongSelf.presentationData.strings.Chat_PostSuggestion_Reject_Title, titleFont: .bold, value: "", placeholder: strongSelf.presentationData.strings.Chat_PostSuggestion_Reject_Placeholder, characterLimit: 4096, apply: { value in + let promptController = promptController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, text: strongSelf.presentationData.strings.Chat_PostSuggestion_Reject_Title, titleFont: .bold, value: "", placeholder: strongSelf.presentationData.strings.Chat_PostSuggestion_Reject_Placeholder, characterLimit: 4096, apply: { value in guard let self else { return } @@ -2501,13 +2506,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let configuration = StarsSubscriptionConfiguration.with(appConfiguration: strongSelf.context.currentAppConfiguration.with { $0 }) funds = (amount, amount.currency == .stars ? Int(configuration.channelMessageSuggestionStarsCommissionPermille) : Int(configuration.channelMessageSuggestionTonCommissionPermille)) } - - #if DEBUG - if "".isEmpty { - funds = nil - } - #endif - + var isAdmin = false if let channel = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isMonoForum, let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = strongSelf.presentationInterfaceState.renderedPeer?.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.hasPermission(.manageDirect) { isAdmin = true @@ -4323,7 +4322,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var message: Message? if let historyMessage = self.chatDisplayNode.historyNode.messageInCurrentHistoryView(messageId) { message = historyMessage - } else if let panelMessage = self.chatDisplayNode.adPanelNode?.message, panelMessage.id == messageId { + } else if let panelMessage = self.chatDisplayNode.adPanelMessage, panelMessage.id == messageId { message = panelMessage } @@ -4585,7 +4584,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G adOpaqueId = adAttribute.opaqueId } } - if adOpaqueId == nil, let panelMessage = self.chatDisplayNode.adPanelNode?.message, let adAttribute = panelMessage.adAttribute { + if adOpaqueId == nil, let panelMessage = self.chatDisplayNode.adPanelMessage, let adAttribute = panelMessage.adAttribute { adOpaqueId = adAttribute.opaqueId } let _ = self.context.engine.accountData.updateAdMessagesEnabled(enabled: false).start() @@ -5177,14 +5176,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return true } - self.chatTitleView = ChatTitleView(context: self.context, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, animationCache: controllerInteraction.presentationContext.animationCache, animationRenderer: controllerInteraction.presentationContext.animationRenderer) - - if case .messageOptions = self.subject { - self.chatTitleView?.disableAnimations = true - } + self.chatTitleView = ChatNavigationBarTitleView(frame: CGRect()) self.navigationItem.titleView = self.chatTitleView - self.chatTitleView?.longPressed = { [weak self] in + self.chatTitleView?.longTapAction = { [weak self] in if let strongSelf = self, let peerView = strongSelf.contentData?.state.peerView, let peer = peerView.peers[peerView.peerId], peer.restrictionText(platform: "ios", contentSettings: strongSelf.context.currentContentSettings.with { $0 }) == nil && !strongSelf.presentationInterfaceState.isNotAccessible { if case .standard(.previewing) = strongSelf.mode { } else { @@ -5570,7 +5565,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.moreBarButton.addTarget(self, action: #selector(self.moreButtonPressed), forControlEvents: .touchUpInside) self.navigationItem.titleView = self.chatTitleView - self.chatTitleView?.pressed = { [weak self] in + self.chatTitleView?.tapAction = { [weak self] in self?.navigationButtonAction(.openChatInfo(expandAvatar: false, section: nil)) } @@ -6136,7 +6131,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.networkStateDisposable = (context.account.networkState |> deliverOnMainQueue).startStrict(next: { [weak self] state in if let strongSelf = self, case .standard(.default) = strongSelf.presentationInterfaceState.mode { - strongSelf.chatTitleView?.networkState = state + strongSelf.chatTitleView?.updateNetworkState(networkState: state, transition: .spring(duration: 0.4)) } }) @@ -6241,6 +6236,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.newTopicEventsDisposable?.dispose() self.updateMessageTodoDisposables?.dispose() self.preloadNextChatPeerIdDisposable.dispose() + self.globalControlPanelsContextStateDisposable?.dispose() } public func updatePresentationMode(_ mode: ChatControllerPresentationMode) { @@ -6324,7 +6320,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G case .embedded: self.statusBar.statusBarStyle = .Ignore default: - self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style + if let isDark = self.chatDisplayNode.backgroundNode.isDark { + if isDark { + self.statusBar.statusBarStyle = .White + } else { + self.statusBar.statusBarStyle = .Black + } + } else { + self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style + } self.deferScreenEdgeGestures = [] } case .overlay: @@ -6360,18 +6364,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let presentationTheme: PresentationTheme if let forcedNavigationBarTheme = self.forcedNavigationBarTheme { presentationTheme = forcedNavigationBarTheme - navigationBarTheme = NavigationBarTheme(rootControllerTheme: forcedNavigationBarTheme, hideBackground: false, hideBadge: true) - } else if self.hasEmbeddedTitleContent { - presentationTheme = self.presentationData.theme - navigationBarTheme = NavigationBarTheme(rootControllerTheme: defaultDarkPresentationTheme, hideBackground: self.context.sharedContext.immediateExperimentalUISettings.playerEmbedding ? true : false, hideBadge: true) + navigationBarTheme = NavigationBarTheme(rootControllerTheme: forcedNavigationBarTheme, hideBackground: false, hideBadge: true, edgeEffectColor: .clear, style: .glass) } else { presentationTheme = self.presentationData.theme - navigationBarTheme = NavigationBarTheme(rootControllerTheme: self.presentationData.theme, hideBackground: self.context.sharedContext.immediateExperimentalUISettings.playerEmbedding ? true : false, hideBadge: false) + navigationBarTheme = NavigationBarTheme(rootControllerTheme: self.presentationData.theme, hideBackground: false, hideBadge: false, edgeEffectColor: .clear, style: .glass) } - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(presentationStrings: self.presentationData.strings))) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)), transition: .immediate) - self.chatTitleView?.updateThemeAndStrings(theme: presentationTheme, strings: self.presentationData.strings, hasEmbeddedTitleContent: self.hasEmbeddedTitleContent) + self.moreBarButton.updateColor(color: presentationTheme.chat.inputPanel.panelControlColor) } enum PinnedReferenceMessage { @@ -6790,9 +6791,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } - if !chatNavigationStack.isEmpty { - self.chatDisplayNode.navigationBar?.backButtonNode.isGestureEnabled = true - self.chatDisplayNode.navigationBar?.backButtonNode.activated = { [weak self] gesture, _ in + if !chatNavigationStack.isEmpty, let backButtonNode = self.chatDisplayNode.navigationBar?.backButtonNode as? ContextControllerSourceNode { + backButtonNode.isGestureEnabled = true + backButtonNode.activated = { [weak self] gesture, _ in guard let strongSelf = self, let backButtonNode = strongSelf.chatDisplayNode.navigationBar?.backButtonNode, let navigationController = strongSelf.effectiveNavigationController else { gesture.cancel() return @@ -6849,7 +6850,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if case .standard(.default) = self.presentationInterfaceState.mode, self.raiseToListen == nil { self.raiseToListen = RaiseToListenManager(shouldActivate: { [weak self] in - if let strongSelf = self, strongSelf.isNodeLoaded && strongSelf.canReadHistoryValue, strongSelf.presentationInterfaceState.interfaceState.editMessage == nil, strongSelf.playlistStateAndType == nil { + if let strongSelf = self, strongSelf.isNodeLoaded && strongSelf.canReadHistoryValue, strongSelf.presentationInterfaceState.interfaceState.editMessage == nil, strongSelf.globalControlPanelsContext?.playlistStateAndType == nil { if !strongSelf.context.sharedContext.currentMediaInputSettings.with({ $0.enableRaiseToSpeak }) { return false } @@ -6890,7 +6891,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self?.deactivateRaiseGesture() }) self.raiseToListen?.enabled = self.canReadHistoryValue - self.tempVoicePlaylistEnded = { [weak self] in + self.globalControlPanelsContext?.setTempVoicePlaylistEnded({ [weak self] in guard let strongSelf = self else { return } @@ -6907,14 +6908,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.returnInputViewFocus = false strongSelf.chatDisplayNode.ensureInputViewFocused() } - } - self.tempVoicePlaylistItemChanged = { [weak self] previousItem, currentItem in + }) + self.globalControlPanelsContext?.setTempVoicePlaylistItemChanged({ [weak self] previousItem, currentItem in guard let strongSelf = self else { return } strongSelf.chatDisplayNode.historyNode.voicePlaylistItemChanged(previousItem, currentItem) - } + }) } if let arguments = self.presentationArguments as? ChatControllerOverlayPresentationData { @@ -7189,24 +7190,27 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G controller.navigationPresentation = .modal controller.setState(.custom(icon: .animation("BroadcastGroup"), title: presentationData.strings.BroadcastGroups_IntroTitle, subtitle: nil, text: presentationData.strings.BroadcastGroups_IntroText, buttonTitle: presentationData.strings.BroadcastGroups_Convert, secondaryButtonTitle: presentationData.strings.BroadcastGroups_Cancel, footerText: nil), animated: false) controller.proceed = { [weak controller] result in - let attributedTitle = NSAttributedString(string: presentationData.strings.BroadcastGroups_ConfirmationAlert_Title, font: Font.semibold(presentationData.listsFontSize.baseDisplaySize), textColor: presentationData.theme.actionSheet.primaryTextColor, paragraphAlignment: .center) - let body = MarkdownAttributeSet(font: Font.regular(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: presentationData.theme.actionSheet.primaryTextColor) - let bold = MarkdownAttributeSet(font: Font.semibold(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: presentationData.theme.actionSheet.primaryTextColor) - let attributedText = parseMarkdownIntoAttributedString(presentationData.strings.BroadcastGroups_ConfirmationAlert_Text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .center) - - let alertController = richTextAlertController(context: context, title: attributedTitle, text: attributedText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - let _ = context.engine.notices.dismissPeerSpecificServerProvidedSuggestion(peerId: peerId, suggestion: .convertToGigagroup).startStandalone() - }), TextAlertAction(type: .defaultAction, title: presentationData.strings.BroadcastGroups_ConfirmationAlert_Convert, action: { [weak controller] in - controller?.dismiss() - - let _ = context.engine.notices.dismissPeerSpecificServerProvidedSuggestion(peerId: peerId, suggestion: .convertToGigagroup).startStandalone() - - let _ = (convertGroupToGigagroup(account: context.account, peerId: peerId) - |> deliverOnMainQueue).startStandalone(completed: { - let participantsLimit = context.currentLimitsConfiguration.with { $0 }.maxSupergroupMemberCount - strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .gigagroupConversion(text: presentationData.strings.BroadcastGroups_Success(presentationStringsFormattedNumber(participantsLimit, presentationData.dateTimeFormat.decimalSeparator)).string), elevatedLayout: false, action: { _ in return false }), in: .current) - }) - })]) + let alertController = textAlertController( + context: context, + title: presentationData.strings.BroadcastGroups_ConfirmationAlert_Title, + text: presentationData.strings.BroadcastGroups_ConfirmationAlert_Text, + actions: [ + TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { + let _ = context.engine.notices.dismissPeerSpecificServerProvidedSuggestion(peerId: peerId, suggestion: .convertToGigagroup).startStandalone() + }), + TextAlertAction(type: .defaultAction, title: presentationData.strings.BroadcastGroups_ConfirmationAlert_Convert, action: { [weak controller] in + controller?.dismiss() + + let _ = context.engine.notices.dismissPeerSpecificServerProvidedSuggestion(peerId: peerId, suggestion: .convertToGigagroup).startStandalone() + + let _ = (convertGroupToGigagroup(account: context.account, peerId: peerId) + |> deliverOnMainQueue).startStandalone(completed: { + let participantsLimit = context.currentLimitsConfiguration.with { $0 }.maxSupergroupMemberCount + strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .gigagroupConversion(text: presentationData.strings.BroadcastGroups_Success(presentationStringsFormattedNumber(participantsLimit, presentationData.dateTimeFormat.decimalSeparator)).string), elevatedLayout: false, action: { _ in return false }), in: .current) + }) + }) + ] + ) controller?.present(alertController, in: .window(.root)) } strongSelf.push(controller) @@ -7235,7 +7239,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.storedAnimateFromSnapshotState = nil if let titleViewSnapshotState = snapshotState.titleViewSnapshotState { - self.chatTitleView?.animateFromSnapshot(titleViewSnapshotState) + self.chatTitleView?.animateFromSnapshot(titleViewSnapshotState, direction: .up) } if let avatarSnapshotState = snapshotState.avatarSnapshotState { (self.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.animateFromSnapshot(avatarSnapshotState) @@ -7299,6 +7303,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G override public func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) + if #available(iOS 18.0, *) { + } else { + //TODO:release + } UIView.performWithoutAnimation { self.view.endEditing(true) } @@ -7434,7 +7442,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G super.containerLayoutUpdated(layout, transition: transition) self.validLayout = layout - self.chatTitleView?.layout = layout switch self.presentationInterfaceState.mode { case .standard, .inline: @@ -8081,64 +8088,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.presentEmojiList(references: [stickerPackReference], previewIconFile: previewIconFile) } } - - func displayDiceTooltip(dice: TelegramMediaDice) { - guard let _ = dice.value else { - return - } - self.window?.forEachController({ controller in - if let controller = controller as? UndoOverlayController { - controller.dismissWithCommitAction() - } - }) - self.forEachController({ controller in - if let controller = controller as? UndoOverlayController { - controller.dismissWithCommitAction() - } - return true - }) - let value: String? - let emoji = dice.emoji.strippedEmoji - switch emoji { - case "🎲": - value = self.presentationData.strings.Conversation_Dice_u1F3B2 - case "🎯": - value = self.presentationData.strings.Conversation_Dice_u1F3AF - case "🏀": - value = self.presentationData.strings.Conversation_Dice_u1F3C0 - case "⚽": - value = self.presentationData.strings.Conversation_Dice_u26BD - case "🎰": - value = self.presentationData.strings.Conversation_Dice_u1F3B0 - case "🎳": - value = self.presentationData.strings.Conversation_Dice_u1F3B3 - default: - let emojiHex = emoji.unicodeScalars.map({ String(format:"%02x", $0.value) }).joined().uppercased() - let key = "Conversation.Dice.u\(emojiHex)" - if let string = self.presentationData.strings.primaryComponent.dict[key] { - value = string - } else if let string = self.presentationData.strings.secondaryComponent?.dict[key] { - value = string - } else { - value = nil - } - } - if let value = value { - self.present(UndoOverlayController(presentationData: self.presentationData, content: .dice(dice: dice, context: self.context, text: value, action: canSendMessagesToChat(self.presentationInterfaceState) ? self.presentationData.strings.Conversation_SendDice : nil), elevatedLayout: false, action: { [weak self] action in - if let self, canSendMessagesToChat(self.presentationInterfaceState), action == .undo { - self.presentPaidMessageAlertIfNeeded(completion: { [weak self] postpone in - guard let self else { - return - } - self.sendMessages([.message(text: "", attributes: [], inlineStickers: [:], mediaReference: AnyMediaReference.standalone(media: TelegramMediaDice(emoji: dice.emoji)), threadId: self.chatLocation.threadId, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])], postpone: postpone) - }) - } - return false - }), in: .current) - } - } - func transformEnqueueMessages(_ messages: [EnqueueMessage], silentPosting: Bool, scheduleTime: Int32? = nil, repeatPeriod: Int32? = nil, postpone: Bool = false) -> [EnqueueMessage] { var defaultThreadId: Int64? var defaultReplyMessageSubject: EngineMessageReplySubject? @@ -8470,9 +8420,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if case let .customChatContents(customChatContents) = strongSelf.presentationInterfaceState.subject, let messageLimit = customChatContents.messageLimit { if let originalHistoryView = strongSelf.chatDisplayNode.historyNode.originalHistoryView, originalHistoryView.entries.count + mappedMessages.count > messageLimit { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.Chat_QuickReplyMediaMessageLimitReachedText(Int32(messageLimit)), actions: [ - TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_OK, action: {}) - ]), in: .window(.root)) + let alertController = textAlertController( + context: strongSelf.context, + title: nil, + text: strongSelf.presentationData.strings.Chat_QuickReplyMediaMessageLimitReachedText(Int32(messageLimit)), + actions: [ + TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_OK, action: {}) + ] + ) + strongSelf.present(alertController, in: .window(.root)) return } } diff --git a/submodules/TelegramUI/Sources/ChatControllerAdminBanUsers.swift b/submodules/TelegramUI/Sources/ChatControllerAdminBanUsers.swift index bbd1aa11..a8834e58 100644 --- a/submodules/TelegramUI/Sources/ChatControllerAdminBanUsers.swift +++ b/submodules/TelegramUI/Sources/ChatControllerAdminBanUsers.swift @@ -404,8 +404,8 @@ extension ChatControllerImpl { titleString = self.presentationData.strings.Chat_DeletePaidMessageTon_Title textString = self.presentationData.strings.Chat_DeletePaidMessageTon_Text } - self.present(standardTextAlertController( - theme: AlertControllerTheme(presentationData: self.presentationData), + self.present(textAlertController( + context: self.context, title: titleString, text: textString, actions: [ @@ -429,6 +429,7 @@ extension ChatControllerImpl { return } + if messageIds.count == 1, let message = messages.values.compactMap({ $0 }).first, let repeatAttribute = message.attributes.first(where: { $0 is ScheduledRepeatAttribute }) as? ScheduledRepeatAttribute { let commit = { [weak self] in guard let self else { @@ -449,8 +450,8 @@ extension ChatControllerImpl { deleteOneAction = self.presentationData.strings.ScheduledMessages_DeleteRepeatingActionSingle deleteAllAction = self.presentationData.strings.ScheduledMessages_DeleteRepeatingActionMultiple } - self.present(standardTextAlertController( - theme: AlertControllerTheme(presentationData: self.presentationData), + self.present(textAlertController( + context: self.context, title: title, text: text, actions: [ diff --git a/submodules/TelegramUI/Sources/ChatControllerContentData.swift b/submodules/TelegramUI/Sources/ChatControllerContentData.swift index 31d439a2..3caadb68 100644 --- a/submodules/TelegramUI/Sources/ChatControllerContentData.swift +++ b/submodules/TelegramUI/Sources/ChatControllerContentData.swift @@ -519,32 +519,44 @@ extension ChatControllerImpl { if case .reply = info { let titleContent: ChatTitleContent if case let .reply(hasQuote) = messageOptionsTitleInfo, hasQuote { - titleContent = .custom(strings.Chat_TitleQuoteSelection, subtitleText, false) + titleContent = .custom(title: [ChatTitleContent.TitleTextItem(id: AnyHashable(0), content: .text(strings.Chat_TitleQuoteSelection))], subtitle: subtitleText, isEnabled: false) } else { - titleContent = .custom(strings.Chat_TitleReply, subtitleText, false) + titleContent = .custom(title: [ChatTitleContent.TitleTextItem(id: AnyHashable(0), content: .text(strings.Chat_TitleReply))], subtitle: subtitleText, isEnabled: false) } strongSelf.state.chatTitleContent = titleContent } else if case .link = info { - strongSelf.state.chatTitleContent = .custom(strings.Chat_TitleLinkOptions, subtitleText, false) + strongSelf.state.chatTitleContent = .custom(title: [ChatTitleContent.TitleTextItem(id: AnyHashable(0), content: .text(strings.Chat_TitleLinkOptions))], subtitle: subtitleText, isEnabled: false) } else if displayedCount == 1 { - strongSelf.state.chatTitleContent = .custom(strings.Conversation_ForwardOptions_ForwardTitleSingle, subtitleText, false) + strongSelf.state.chatTitleContent = .custom(title: [ChatTitleContent.TitleTextItem(id: AnyHashable(0), content: .text(strings.Conversation_ForwardOptions_ForwardTitleSingle))], subtitle: subtitleText, isEnabled: false) } else { - strongSelf.state.chatTitleContent = .custom(strings.Conversation_ForwardOptions_ForwardTitle(Int32(displayedCount ?? 1)), subtitleText, false) + strongSelf.state.chatTitleContent = .custom(title: [ChatTitleContent.TitleTextItem(id: AnyHashable(0), content: .text(strings.Conversation_ForwardOptions_ForwardTitle(Int32(displayedCount ?? 1))))], subtitle: subtitleText, isEnabled: false) } } else if let selectionState = configuration.selectionState { if selectionState.selectedIds.count > 0 { - strongSelf.state.chatTitleContent = .custom(strings.Conversation_SelectedMessages(Int32(selectionState.selectedIds.count)), nil, false) + let rawText = strings.Conversation_SelectedMessagesFormat(Int32(selectionState.selectedIds.count)) + var items: [ChatTitleContent.TitleTextItem] = [] + if let range = rawText.range(of: "{}") { + if range.lowerBound != rawText.startIndex { + items.append(ChatTitleContent.TitleTextItem(id: AnyHashable("selection_0"), content: .text(String(rawText[rawText.startIndex ..< range.lowerBound])))) + } + items.append(ChatTitleContent.TitleTextItem(id: AnyHashable("selection_1"), content: .number(selectionState.selectedIds.count, minDigits: 1))) + if range.upperBound != rawText.endIndex { + items.append(ChatTitleContent.TitleTextItem(id: AnyHashable("selection_2"), content: .text(String(rawText[range.upperBound ..< rawText.endIndex])))) + } + } + + strongSelf.state.chatTitleContent = .custom(title: items, subtitle: nil, isEnabled: false) } else { if let reportReason = configuration.reportReason { - strongSelf.state.chatTitleContent = .custom(reportReason.title, strings.Conversation_SelectMessages, false) + strongSelf.state.chatTitleContent = .custom(title: [ChatTitleContent.TitleTextItem(id: AnyHashable(1), content: .text(reportReason.title))], subtitle: strings.Conversation_SelectMessages, isEnabled: false) } else { - strongSelf.state.chatTitleContent = .custom(strings.Conversation_SelectMessages, nil, false) + strongSelf.state.chatTitleContent = .custom(title: [ChatTitleContent.TitleTextItem(id: AnyHashable(2), content: .text(strings.Conversation_SelectMessages))], subtitle: nil, isEnabled: false) } } } else if let peer = peerViewMainPeer(peerView) { if case .pinnedMessages = configuration.subject { - strongSelf.state.chatTitleContent = .custom(strings.Chat_TitlePinnedMessages(Int32(displayedCount ?? 1)), nil, false) + strongSelf.state.chatTitleContent = .custom(title: [ChatTitleContent.TitleTextItem(id: AnyHashable(0), content: .text(strings.Chat_TitlePinnedMessages(Int32(displayedCount ?? 1))))], subtitle: nil, isEnabled: false) } else if let channel = peer as? TelegramChannel, channel.isMonoForum { if let linkedMonoforumId = channel.linkedMonoforumId, let mainPeer = peerView.peers[linkedMonoforumId] { strongSelf.state.chatTitleContent = .peer(peerView: ChatTitleContent.PeerData( @@ -557,7 +569,7 @@ extension ChatControllerImpl { cachedData: nil ), customTitle: nil, customSubtitle: strings.Chat_Monoforum_Subtitle, onlineMemberCount: (nil, nil), isScheduledMessages: false, isMuted: nil, customMessageCount: nil, isEnabled: true) } else { - strongSelf.state.chatTitleContent = .custom(channel.debugDisplayTitle, nil, true) + strongSelf.state.chatTitleContent = .custom(title: [ChatTitleContent.TitleTextItem(id: AnyHashable(0), content: .text(channel.debugDisplayTitle))], subtitle: nil, isEnabled: true) } } else { strongSelf.state.chatTitleContent = .peer(peerView: ChatTitleContent.PeerData(peerView: peerView), customTitle: nil, customSubtitle: nil, onlineMemberCount: onlineMemberCount, isScheduledMessages: isScheduledMessages, isMuted: nil, customMessageCount: nil, isEnabled: hasPeerInfo) @@ -1549,7 +1561,7 @@ extension ChatControllerImpl { } strongSelf.state.infoAvatar = .emojiStatus(content: avatarContent, contextActionIsEnabled: infoContextActionIsEnabled) } else if chatLocation.threadId == EngineMessage.newTopicThreadId { - strongSelf.state.chatTitleContent = .custom(strongSelf.presentationData.strings.Chat_MessageHeaderBotNewThread, nil, false) + strongSelf.state.chatTitleContent = .custom(title: [ChatTitleContent.TitleTextItem(id: AnyHashable(0), content: .text(strongSelf.presentationData.strings.Chat_MessageHeaderBotNewThread))], subtitle: nil, isEnabled: false) strongSelf.state.infoAvatar = nil } else { strongSelf.state.chatTitleContent = .replyThread(type: replyThreadType, count: count) @@ -1742,11 +1754,11 @@ extension ChatControllerImpl { case let .quickReplyMessageInput(shortcut, shortcutType): switch shortcutType { case .generic: - self.state.chatTitleContent = .custom("\(shortcut)", nil, false) + self.state.chatTitleContent = .custom(title: [ChatTitleContent.TitleTextItem(id: AnyHashable(0), content: .text("\(shortcut)"))], subtitle: nil, isEnabled: false) case .greeting: - self.state.chatTitleContent = .custom(strings.QuickReply_TitleGreetingMessage, nil, false) + self.state.chatTitleContent = .custom(title: [ChatTitleContent.TitleTextItem(id: AnyHashable(0), content: .text(strings.QuickReply_TitleGreetingMessage))], subtitle: nil, isEnabled: false) case .away: - self.state.chatTitleContent = .custom(strings.QuickReply_TitleAwayMessage, nil, false) + self.state.chatTitleContent = .custom(title: [ChatTitleContent.TitleTextItem(id: AnyHashable(0), content: .text(strings.QuickReply_TitleAwayMessage))], subtitle: nil, isEnabled: false) } case let .businessLinkSetup(link): let linkUrl: String @@ -1756,10 +1768,10 @@ extension ChatControllerImpl { linkUrl = link.url } - self.state.chatTitleContent = .custom(link.title ?? strings.Business_Links_EditLinkTitle, linkUrl, false) + self.state.chatTitleContent = .custom(title: [ChatTitleContent.TitleTextItem(id: AnyHashable(0), content: .text(link.title ?? strings.Business_Links_EditLinkTitle))], subtitle: linkUrl, isEnabled: false) } } else { - self.state.chatTitleContent = .custom(" ", nil, false) + self.state.chatTitleContent = .custom(title: [ChatTitleContent.TitleTextItem(id: AnyHashable(0), content: .text(" "))], subtitle: nil, isEnabled: false) } self.peerDisposable = (peerView diff --git a/submodules/TelegramUI/Sources/ChatControllerDisplayDiceTooltip.swift b/submodules/TelegramUI/Sources/ChatControllerDisplayDiceTooltip.swift new file mode 100644 index 00000000..6b59404e --- /dev/null +++ b/submodules/TelegramUI/Sources/ChatControllerDisplayDiceTooltip.swift @@ -0,0 +1,124 @@ +import Foundation +import AccountContext +import Postbox +import TelegramCore +import SwiftSignalKit +import Display +import TelegramPresentationData +import PresentationDataUtils +import UndoUI +import EmojiGameStakeScreen +import ChatPresentationInterfaceState +import TelegramStringFormatting + +extension ChatControllerImpl { + func presentEmojiGameStake() { + let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Configuration.EmojiGame()) + |> deliverOnMainQueue).start(next: { [weak self] gameInfo in + guard let self, case let .available(info) = gameInfo else { + return + } + let controller = EmojiGameStakeScreen( + context: self.context, + gameInfo: info, + completion: { [weak self] stake in + guard let self else { + return + } + self.presentPaidMessageAlertIfNeeded(completion: { [weak self] postpone in + guard let self else { + return + } + self.sendMessages([.message(text: "", attributes: [], inlineStickers: [:], mediaReference: AnyMediaReference.standalone(media: TelegramMediaDice(emoji: "🎲", tonAmount: stake.value > 0 ? stake.value : nil)), threadId: self.chatLocation.threadId, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])], postpone: postpone) + }) + } + ) + self.push(controller) + }) + } + + func displayDiceTooltip(dice: TelegramMediaDice) { + guard let _ = dice.value else { + return + } + self.window?.forEachController({ controller in + if let controller = controller as? UndoOverlayController { + controller.dismissWithCommitAction() + } + }) + self.forEachController({ controller in + if let controller = controller as? UndoOverlayController { + controller.dismissWithCommitAction() + } + return true + }) + + let emoji = dice.emoji.strippedEmoji + + + let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Configuration.EmojiGame()) + |> deliverOnMainQueue).start(next: { [weak self] gameInfo in + guard let self else { + return + } + + let canSendMessages = canSendMessagesToChat(self.presentationInterfaceState) + let value: String? + var changeAction: String? + var tonAmount: Int64? + if canSendMessages, emoji == "🎲", case let .available(info) = gameInfo { + let currentStake = info.previousStake + value = "\(self.presentationData.strings.Conversation_Dice_Stake) $ \(formatTonAmountText(currentStake, dateTimeFormat: self.presentationData.dateTimeFormat))" + changeAction = self.presentationData.strings.Conversation_Dice_Change + tonAmount = info.previousStake + } else { + switch emoji { + case "🎲": + value = self.presentationData.strings.Conversation_Dice_u1F3B2 + case "🎯": + value = self.presentationData.strings.Conversation_Dice_u1F3AF + case "🏀": + value = self.presentationData.strings.Conversation_Dice_u1F3C0 + case "⚽": + value = self.presentationData.strings.Conversation_Dice_u26BD + case "🎰": + value = self.presentationData.strings.Conversation_Dice_u1F3B0 + case "🎳": + value = self.presentationData.strings.Conversation_Dice_u1F3B3 + default: + let emojiHex = emoji.unicodeScalars.map({ String(format:"%02x", $0.value) }).joined().uppercased() + let key = "Conversation.Dice.u\(emojiHex)" + if let string = self.presentationData.strings.primaryComponent.dict[key] { + value = string + } else if let string = self.presentationData.strings.secondaryComponent?.dict[key] { + value = string + } else { + value = nil + } + } + } + if let value = value { + self.present(UndoOverlayController(presentationData: self.presentationData, content: .dice(dice: dice, context: self.context, text: value, action: canSendMessages ? self.presentationData.strings.Conversation_SendDice : nil, changeAction: changeAction), elevatedLayout: false, action: { [weak self] action in + if let self, canSendMessagesToChat(self.presentationInterfaceState) { + switch action { + case .undo: + self.presentPaidMessageAlertIfNeeded(completion: { [weak self] postpone in + guard let self else { + return + } + self.sendMessages([.message(text: "", attributes: [], inlineStickers: [:], mediaReference: AnyMediaReference.standalone(media: TelegramMediaDice(emoji: dice.emoji, tonAmount: tonAmount)), threadId: self.chatLocation.threadId, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])], postpone: postpone) + }) + case .info: + if let _ = changeAction { + self.presentEmojiGameStake() + } + default: + break + } + } + return false + }), in: .current) + } + }) + } +} diff --git a/submodules/TelegramUI/Sources/ChatControllerEditChat.swift b/submodules/TelegramUI/Sources/ChatControllerEditChat.swift index 50f1593c..acec0b49 100644 --- a/submodules/TelegramUI/Sources/ChatControllerEditChat.swift +++ b/submodules/TelegramUI/Sources/ChatControllerEditChat.swift @@ -9,6 +9,7 @@ import TelegramPresentationData import PresentationDataUtils import QuickReplyNameAlertController import BusinessLinkNameAlertController +import ChatTitleView extension ChatControllerImpl { func editChat() { @@ -48,7 +49,16 @@ extension ChatControllerImpl { contentNode.setErrorText(errorText: self.presentationData.strings.QuickReply_ShortcutExistsInlineError) } } else { - self.chatTitleView?.titleContent = .custom("\(value)", nil, false) + self.chatTitleView?.update( + context: self.context, + theme: self.presentationData.theme, + strings: self.presentationData.strings, + dateTimeFormat: self.presentationData.dateTimeFormat, + nameDisplayOrder: self.presentationData.nameDisplayOrder, + content: .custom(title: [ChatTitleContent.TitleTextItem(id: AnyHashable(0), content: .text("\(value)"))], subtitle: nil, isEnabled: false), + transition: .immediate + ) + alertController?.view.endEditing(true) alertController?.dismissAnimated() @@ -66,22 +76,19 @@ extension ChatControllerImpl { var completion: ((String?) -> Void)? let alertController = businessLinkNameAlertController( context: self.context, - text: self.presentationData.strings.Business_Links_LinkNameTitle, - subtext: self.presentationData.strings.Business_Links_LinkNameText, value: currentValue, - characterLimit: 32, apply: { value in completion?(value) } ) completion = { [weak self, weak alertController] value in guard let self else { - alertController?.dismissAnimated() + alertController?.dismiss(completion: nil) return } if let value { if value == currentValue { - alertController?.dismissAnimated() + alertController?.dismiss(completion: nil) return } @@ -93,13 +100,21 @@ extension ChatControllerImpl { } else { linkUrl = link.url } - self.chatTitleView?.titleContent = .custom(value.isEmpty ? self.presentationData.strings.Business_Links_EditLinkTitle : value, linkUrl, false) + self.chatTitleView?.update( + context: self.context, + theme: self.presentationData.theme, + strings: self.presentationData.strings, + dateTimeFormat: self.presentationData.dateTimeFormat, + nameDisplayOrder: self.presentationData.nameDisplayOrder, + content: .custom(title: [ChatTitleContent.TitleTextItem(id: AnyHashable(0), content: .text(value.isEmpty ? self.presentationData.strings.Business_Links_EditLinkTitle : value))], subtitle: linkUrl, isEnabled: false), + transition: .immediate + ) if case let .customChatContents(customChatContents) = self.subject { customChatContents.businessLinkUpdate(message: link.message, entities: link.entities, title: value.isEmpty ? nil : value) } alertController?.view.endEditing(true) - alertController?.dismissAnimated() + alertController?.dismiss(completion: nil) } } self.present(alertController, in: .window(.root)) diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index e7efdff9..a1e76201 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -51,6 +51,16 @@ import ChatThemeScreen import ChatTextInputPanelNode import ChatInputAccessoryPanel import ChatMessageTextBubbleContentNode +import HeaderPanelContainerComponent +import MediaPlaybackHeaderPanelComponent +import LiveLocationHeaderPanelComponent +import TranslateHeaderPanelComponent +import AdPanelHeaderPanelComponent +import MessageFeeHeaderPanelComponent +import LegacyChatHeaderPanelComponent +import ChatSearchNavigationContentNode +import GroupCallHeaderPanelComponent +import PresentationDataUtils final class VideoNavigationControllerDropContentItem: NavigationControllerDropContentItem { let itemNode: OverlayMediaItemNode @@ -119,7 +129,9 @@ class HistoryNodeContainer: ASDisplayNode { var isSecret: Bool { didSet { if self.isSecret != oldValue { - setLayerDisableScreenshots(self.layer, self.isSecret) + // MISC: Bypass screenshot protection if enabled + let shouldDisable = self.isSecret && !MiscSettingsManager.shared.shouldBypassScreenshotProtection + setLayerDisableScreenshots(self.layer, shouldDisable) } } } @@ -134,7 +146,9 @@ class HistoryNodeContainer: ASDisplayNode { super.init() if self.isSecret { - setLayerDisableScreenshots(self.layer, self.isSecret) + // MISC: Bypass screenshot protection if enabled + let shouldDisable = self.isSecret && !MiscSettingsManager.shared.shouldBypassScreenshotProtection + setLayerDisableScreenshots(self.layer, shouldDisable) } } } @@ -218,26 +232,29 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { private let inputPanelClippingNode: SparseNode let inputPanelBackgroundNode: NavigationBackgroundNode - private var navigationBarBackgroundContent: WallpaperBubbleBackgroundNode? - private var intrinsicInputPanelBackgroundNodeSize: CGSize? private var inputPanelBottomBackgroundSeparatorBaseOffset: CGFloat = 0.0 private var plainInputSeparatorAlpha: CGFloat? private var usePlainInputSeparator: Bool - private var chatImportStatusPanel: ChatImportStatusPanel? - - private(set) var adPanelNode: ChatAdPanelNode? - private(set) var feePanelNode: ChatFeePanelNode? + var adPanelMessage: Message? { + guard let headerPanelsComponentView = self.headerPanelsView?.view as? HeaderPanelContainerComponent.View else { + return nil + } + guard let adPanelView = headerPanelsComponentView.panel(forKey: AnyHashable("ad")) as? AdPanelHeaderPanelComponent.View else { + return nil + } + return adPanelView.message?._asMessage() + } private let titleAccessoryPanelContainer: ChatControllerTitlePanelNodeContainer - private var titleAccessoryPanelNode: ChatTitleAccessoryPanelNode? - - private var chatTranslationPanel: ChatTranslationPanelNode? + private var currentTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode? private var floatingTopicsPanelContainer: ChatControllerTitlePanelNodeContainer private var floatingTopicsPanel: (view: ComponentView, component: ChatFloatingTopicsPanel)? + private var headerPanelsView: ComponentView? + private var topBackgroundEdgeEffectNode: WallpaperEdgeEffectNode? private var bottomBackgroundEdgeEffectNode: WallpaperEdgeEffectNode? private(set) var inputPanelNode: ChatInputPanelNode? @@ -1091,7 +1108,18 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { if self.inputPanelContainerNode.expansionFraction > 0.3 { statusBar.updateStatusBarStyle(.White, animated: true) } else { - statusBar.updateStatusBarStyle(self.chatPresentationInterfaceState.theme.rootController.statusBarStyle.style, animated: true) + let statusBarStyle: StatusBarStyle + if let isDark = self.backgroundNode.isDark { + if isDark { + statusBarStyle = .White + } else { + statusBarStyle = .Black + } + } else { + statusBarStyle = self.chatPresentationInterfaceState.theme.rootController.statusBarStyle.style + } + + statusBar.updateStatusBarStyle(statusBarStyle, animated: true) } self.controller?.deferScreenEdgeGestures = [] case .overlay: @@ -1318,7 +1346,275 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { } self.containerLayoutAndNavigationBarHeight = (layout, navigationBarHeight) + var headerPanels: [HeaderPanelContainerComponent.Panel] = [] + if let mediaPlayback = self.controller?.globalControlPanelsContextState?.mediaPlayback { + headerPanels.append(HeaderPanelContainerComponent.Panel( + key: "media", + orderIndex: 0, + component: AnyComponent(MediaPlaybackHeaderPanelComponent( + context: self.context, + theme: self.chatPresentationInterfaceState.theme, + strings: self.chatPresentationInterfaceState.strings, + data: mediaPlayback, + controller: { [weak self] in + return self?.controller + } + ))) + ) + } + if let liveLocation = self.controller?.globalControlPanelsContextState?.liveLocation { + headerPanels.append(HeaderPanelContainerComponent.Panel( + key: "liveLocation", + orderIndex: 1, + component: AnyComponent(LiveLocationHeaderPanelComponent( + context: self.context, + theme: self.chatPresentationInterfaceState.theme, + strings: self.chatPresentationInterfaceState.strings, + data: liveLocation, + controller: { [weak self] in + return self?.controller + } + ))) + ) + } + if let groupCall = self.controller?.globalControlPanelsContextState?.groupCall { + headerPanels.append(HeaderPanelContainerComponent.Panel( + key: "groupCall", + orderIndex: 2, + component: AnyComponent(GroupCallHeaderPanelComponent( + context: self.context, + theme: self.chatPresentationInterfaceState.theme, + strings: self.chatPresentationInterfaceState.strings, + data: groupCall, + onTapAction: { [weak self] in + guard let self, let groupCall = self.controller?.globalControlPanelsContextState?.groupCall else { + return + } + self.controller?.joinGroupCall( + peerId: groupCall.peerId, + invite: nil, + activeCall: EngineGroupCallDescription( + id: groupCall.info.id, + accessHash: groupCall.info.accessHash, + title: groupCall.info.title, + scheduleTimestamp: groupCall.info.scheduleTimestamp, + subscribedToScheduled: groupCall.info.subscribedToScheduled, + isStream: groupCall.info.isStream + ) + ) + }, + onNotifyScheduledTapAction: { [weak self] in + guard let self, let controller = self.controller, let groupCall = self.controller?.globalControlPanelsContextState?.groupCall else { + return + } + if groupCall.info.scheduleTimestamp != nil && !groupCall.info.subscribedToScheduled { + let _ = self.context.engine.calls.toggleScheduledGroupCallSubscription(peerId: groupCall.peerId, reference: .id(id: groupCall.info.id, accessHash: groupCall.info.accessHash), subscribe: true).startStandalone() + + controller.controllerInteraction?.displayUndo( + .universal( + animation: "anim_set_notification", + scale: 0.06, + colors: [ + "Middle.Group 1.Fill 1": UIColor.white, + "Top.Group 1.Fill 1": UIColor.white, + "Bottom.Group 1.Fill 1": UIColor.white, + "EXAMPLE.Group 1.Fill 1": UIColor.white, + "Line.Group 1.Stroke 1": UIColor.white + ], + title: nil, + text: controller.presentationData.strings.Chat_ToastSubscribedToScheduledLiveStream_Text, + customUndoText: nil, + timeout: nil + ) + ) + } + } + ))) + ) + } + + var hasTranslationPanel = false + if let _ = self.chatPresentationInterfaceState.translationState, self.emptyType == nil { + if case .overlay = self.chatPresentationInterfaceState.mode { + } else if self.chatPresentationInterfaceState.renderedPeer?.peer?.restrictionText(platform: "ios", contentSettings: self.context.currentContentSettings.with { $0 }) != nil { + } else if self.chatPresentationInterfaceState.search != nil { + } else { + hasTranslationPanel = true + } + } + + var displayAdPanel = false + if let _ = self.chatPresentationInterfaceState.adMessage { + if let chatHistoryState = self.chatPresentationInterfaceState.chatHistoryState, case .loaded(false, _) = chatHistoryState { + if let user = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser, user.botInfo != nil && !self.chatPresentationInterfaceState.peerIsBlocked && self.chatPresentationInterfaceState.hasAtLeast3Messages { + displayAdPanel = true + } + } + } + if displayAdPanel, let adMessage = self.chatPresentationInterfaceState.adMessage { + headerPanels.append(HeaderPanelContainerComponent.Panel( + key: "ad", + orderIndex: 2, + component: AnyComponent(AdPanelHeaderPanelComponent( + context: self.context, + theme: self.chatPresentationInterfaceState.theme, + strings: self.chatPresentationInterfaceState.strings, + info: AdPanelHeaderPanelComponent.Info( + message: EngineMessage(adMessage) + ), + action: { [weak self] message in + guard let self else { + return + } + self.controllerInteraction.activateAdAction(message.id, nil, false, false) + }, + contextAction: { [weak self] message, sourceNode, gesture in + guard let self else { + return + } + self.controllerInteraction.adContextAction(message._asMessage(), sourceNode, gesture) + }, + close: { [weak self] in + guard let self, let adMessage = self.chatPresentationInterfaceState.adMessage else { + return + } + if self.context.isPremium, let adAttribute = adMessage.adAttribute { + self.controllerInteraction.removeAd(adAttribute.opaqueId) + } else { + self.controllerInteraction.openNoAdsDemo() + } + } + ))) + ) + } + + if let titleAccessoryPanelNode = titlePanelForChatPresentationInterfaceState(self.chatPresentationInterfaceState, context: self.context, currentPanel: self.currentTitleAccessoryPanelNode, controllerInteraction: self.controllerInteraction, interfaceInteraction: self.interfaceInteraction, force: false) { + self.currentTitleAccessoryPanelNode = titleAccessoryPanelNode + let panelKey = "\(type(of: titleAccessoryPanelNode))" + headerPanels.append(HeaderPanelContainerComponent.Panel( + key: panelKey, + orderIndex: 3, + component: AnyComponent(LegacyChatHeaderPanelComponent( + panelNode: titleAccessoryPanelNode, + interfaceState: self.chatPresentationInterfaceState + ))) + ) + } else { + self.currentTitleAccessoryPanelNode = nil + } + + var displayFeePanel: (value: Int64, peer: EnginePeer)? + if let chatHistoryState = self.chatPresentationInterfaceState.chatHistoryState, case .loaded(false, _) = chatHistoryState { + if let user = self.chatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser, user.botInfo == nil { + if !self.chatPresentationInterfaceState.peerIsBlocked, let paidMessageStars = self.chatPresentationInterfaceState.contactStatus?.peerStatusSettings?.paidMessageStars, paidMessageStars.value > 0 { + displayFeePanel = (paidMessageStars.value, .user(user)) + } + } else if let removePaidMessageFeeData = self.chatPresentationInterfaceState.removePaidMessageFeeData { + displayFeePanel = (removePaidMessageFeeData.amount.value, removePaidMessageFeeData.peer) + } + } + if let displayFeePanel { + headerPanels.append(HeaderPanelContainerComponent.Panel( + key: "fee", + orderIndex: 4, + component: AnyComponent(MessageFeeHeaderPanelComponent( + context: self.context, + theme: self.chatPresentationInterfaceState.theme, + strings: self.chatPresentationInterfaceState.strings, + info: MessageFeeHeaderPanelComponent.Info( + value: displayFeePanel.value, + peer: displayFeePanel.peer + ), + removeFee: { [weak self] in + guard let self else { + return + } + self.controllerInteraction.openMessageFeeException() + }, + ))) + ) + } + + if hasTranslationPanel, let translationState = self.chatPresentationInterfaceState.translationState { + headerPanels.append(HeaderPanelContainerComponent.Panel( + key: "translate", + orderIndex: 5, + component: AnyComponent(TranslateHeaderPanelComponent( + context: self.context, + theme: self.chatPresentationInterfaceState.theme, + strings: self.chatPresentationInterfaceState.strings, + info: TranslateHeaderPanelComponent.Info( + isPremium: self.chatPresentationInterfaceState.isPremium, + isActive: translationState.isEnabled, + fromLang: translationState.fromLang, + toLang: translationState.toLang, + peer: (self.chatPresentationInterfaceState.renderedPeer?.chatMainPeer).flatMap(EnginePeer.init) + ), + close: { [weak self] in + guard let self else { + return + } + self.interfaceInteraction?.hideTranslationPanel() + }, + toggle: { [weak self] in + guard let self, let translationState = self.chatPresentationInterfaceState.translationState else { + return + } + self.interfaceInteraction?.toggleTranslation(translationState.isEnabled ? .original : .translated) + }, + changeLanguage: { [weak self] code in + guard let self else { + return + } + self.interfaceInteraction?.changeTranslationLanguage(code) + }, + addDoNotTranslateLanguage: { [weak self] code in + guard let self else { + return + } + self.interfaceInteraction?.addDoNotTranslateLanguage(code) + }, + controller: { [weak self] in + return self?.controller + } + ))) + ) + } + var floatingTopicsPanelInsets = UIEdgeInsets() + + var headerPanelsSize: CGSize? + if !headerPanels.isEmpty { + let headerPanelsView: ComponentView + var headerPanelsTransition = ComponentTransition(transition) + if let current = self.headerPanelsView { + headerPanelsView = current + } else { + headerPanelsTransition = headerPanelsTransition.withAnimation(.none) + headerPanelsView = ComponentView() + self.headerPanelsView = headerPanelsView + } + let headerPanelsSizeValue = headerPanelsView.update( + transition: headerPanelsTransition, + component: AnyComponent(HeaderPanelContainerComponent( + theme: self.chatPresentationInterfaceState.theme, + tabs: nil, + panels: headerPanels + )), + environment: {}, + containerSize: CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right, height: layout.size.height) + ) + headerPanelsSize = headerPanelsSizeValue + floatingTopicsPanelInsets.top += headerPanelsSizeValue.height + } else if let headerPanelsView = self.headerPanelsView { + self.headerPanelsView = nil + if let headerPanelsComponentView = headerPanelsView.view { + transition.updateAlpha(layer: headerPanelsComponentView.layer, alpha: 0.0, completion: { [weak headerPanelsComponentView] _ in + headerPanelsComponentView?.removeFromSuperview() + }) + } + } + var dismissedFloatingTopicsPanel: (view: ComponentView, component: ChatFloatingTopicsPanel)? var immediatelyLayoutFloatingTopicsNodeAndAnimateAppearance = false var didChangeFloatingTopicsPanel = false @@ -1337,215 +1633,15 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { switch floatingTopicsPanelComponent.location { case .side: - floatingTopicsPanelInsets.left = 72.0 + 8.0 + 8.0 + floatingTopicsPanelInsets.left += 72.0 + 10.0 + 10.0 case .top: - floatingTopicsPanelInsets.top = 40.0 + 8.0 + floatingTopicsPanelInsets.top += 40.0 + 10.0 } } else if let floatingTopicsPanel = self.floatingTopicsPanel { self.floatingTopicsPanel = nil dismissedFloatingTopicsPanel = floatingTopicsPanel } - var dismissedTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode? - var immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance = false - var titleAccessoryPanelHeight: CGFloat? - var titleAccessoryPanelBackgroundHeight: CGFloat? - var titleAccessoryPanelHitTestSlop: CGFloat? - - if let titleAccessoryPanelNode = titlePanelForChatPresentationInterfaceState(self.chatPresentationInterfaceState, context: self.context, currentPanel: self.titleAccessoryPanelNode, controllerInteraction: self.controllerInteraction, interfaceInteraction: self.interfaceInteraction, force: false) { - if self.titleAccessoryPanelNode != titleAccessoryPanelNode { - dismissedTitleAccessoryPanelNode = self.titleAccessoryPanelNode - self.titleAccessoryPanelNode = titleAccessoryPanelNode - immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance = true - self.titleAccessoryPanelContainer.addSubnode(titleAccessoryPanelNode) - - titleAccessoryPanelNode.clipsToBounds = true - } - - let layoutResult = titleAccessoryPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState) - titleAccessoryPanelHeight = layoutResult.insetHeight - titleAccessoryPanelBackgroundHeight = layoutResult.backgroundHeight - titleAccessoryPanelHitTestSlop = layoutResult.hitTestSlop - if immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance { - if transition.isAnimated { - titleAccessoryPanelNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) - } - titleAccessoryPanelNode.subnodeTransform = CATransform3DMakeTranslation(0.0, -layoutResult.backgroundHeight, 0.0) - transition.updateSublayerTransformOffset(layer: titleAccessoryPanelNode.layer, offset: CGPoint()) - } - } else if let titleAccessoryPanelNode = self.titleAccessoryPanelNode { - dismissedTitleAccessoryPanelNode = titleAccessoryPanelNode - self.titleAccessoryPanelNode = nil - } - - var dismissedTranslationPanelNode: ChatTranslationPanelNode? - var immediatelyLayoutTranslationPanelNodeAndAnimateAppearance = false - var translationPanelHeight: CGFloat? - - var hasTranslationPanel = false - if let _ = self.chatPresentationInterfaceState.translationState, self.emptyType == nil { - if case .overlay = self.chatPresentationInterfaceState.mode { - } else if self.chatPresentationInterfaceState.renderedPeer?.peer?.restrictionText(platform: "ios", contentSettings: self.context.currentContentSettings.with { $0 }) != nil { - } else if self.chatPresentationInterfaceState.search != nil { - } else { - hasTranslationPanel = true - } - } - - /*#if DEBUG - if "".isEmpty { - hasTranslationPanel = true - } - #endif*/ - - if hasTranslationPanel { - let translationPanelNode: ChatTranslationPanelNode - if let current = self.chatTranslationPanel { - translationPanelNode = current - } else { - translationPanelNode = ChatTranslationPanelNode(context: self.context) - } - translationPanelNode.interfaceInteraction = self.interfaceInteraction - - if self.chatTranslationPanel != translationPanelNode { - dismissedTranslationPanelNode = self.chatTranslationPanel - self.chatTranslationPanel = translationPanelNode - immediatelyLayoutTranslationPanelNodeAndAnimateAppearance = true - self.titleAccessoryPanelContainer.addSubnode(translationPanelNode) - - translationPanelNode.clipsToBounds = true - } - - let height = translationPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, leftDisplayInset: 0.0, transition: immediatelyLayoutTitleAccessoryPanelNodeAndAnimateAppearance ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState) - translationPanelHeight = height - if immediatelyLayoutTranslationPanelNodeAndAnimateAppearance { - translationPanelNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) - translationPanelNode.subnodeTransform = CATransform3DMakeTranslation(0.0, -height, 0.0) - transition.updateSublayerTransformOffset(layer: translationPanelNode.layer, offset: CGPoint()) - } - } else if let chatTranslationPanel = self.chatTranslationPanel { - dismissedTranslationPanelNode = chatTranslationPanel - self.chatTranslationPanel = nil - } - - var dismissedImportStatusPanelNode: ChatImportStatusPanel? - var importStatusPanelHeight: CGFloat? - if let importState = self.chatPresentationInterfaceState.importState { - let importStatusPanelNode: ChatImportStatusPanel - if let current = self.chatImportStatusPanel { - importStatusPanelNode = current - } else { - importStatusPanelNode = ChatImportStatusPanel() - } - - if self.chatImportStatusPanel != importStatusPanelNode { - dismissedImportStatusPanelNode = self.chatImportStatusPanel - self.chatImportStatusPanel = importStatusPanelNode - self.contentContainerNode.contentNode.addSubnode(importStatusPanelNode) - } - - importStatusPanelHeight = importStatusPanelNode.update(context: self.context, progress: CGFloat(importState.progress), presentationData: ChatPresentationData(theme: ChatPresentationThemeData(theme: self.chatPresentationInterfaceState.theme, wallpaper: self.chatPresentationInterfaceState.chatWallpaper), fontSize: self.chatPresentationInterfaceState.fontSize, strings: self.chatPresentationInterfaceState.strings, dateTimeFormat: self.chatPresentationInterfaceState.dateTimeFormat, nameDisplayOrder: self.chatPresentationInterfaceState.nameDisplayOrder, disableAnimations: false, largeEmoji: false, chatBubbleCorners: PresentationChatBubbleCorners(mainRadius: 0.0, auxiliaryRadius: 0.0, mergeBubbleCorners: false)), width: layout.size.width) - } else if let importStatusPanelNode = self.chatImportStatusPanel { - dismissedImportStatusPanelNode = importStatusPanelNode - self.chatImportStatusPanel = nil - } - - var dismissedAdPanelNode: ChatAdPanelNode? - var adPanelHeight: CGFloat? - - var displayAdPanel = false - if let _ = self.chatPresentationInterfaceState.adMessage { - if let chatHistoryState = self.chatPresentationInterfaceState.chatHistoryState, case .loaded(false, _) = chatHistoryState { - if let user = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser, user.botInfo != nil && !self.chatPresentationInterfaceState.peerIsBlocked && self.chatPresentationInterfaceState.hasAtLeast3Messages { - displayAdPanel = true - } - } - } - - if displayAdPanel { - var animateAppearance = false - let adPanelNode: ChatAdPanelNode - if let current = self.adPanelNode { - adPanelNode = current - } else { - adPanelNode = ChatAdPanelNode(context: self.context, animationCache: self.controllerInteraction.presentationContext.animationCache, animationRenderer: self.controllerInteraction.presentationContext.animationRenderer) - adPanelNode.controllerInteraction = self.controllerInteraction - adPanelNode.clipsToBounds = true - animateAppearance = true - } - - if self.adPanelNode != adPanelNode { - dismissedAdPanelNode = self.adPanelNode - self.adPanelNode = adPanelNode - self.titleAccessoryPanelContainer.addSubnode(adPanelNode) - } - - let height = adPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: transition, interfaceState: self.chatPresentationInterfaceState) - if let adMessage = self.chatPresentationInterfaceState.adMessage, let opaqueId = adMessage.adAttribute?.opaqueId { - self.historyNode.markAdAsSeen(opaqueId: opaqueId) - } - - adPanelHeight = height - if transition.isAnimated && animateAppearance { - adPanelNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) - adPanelNode.subnodeTransform = CATransform3DMakeTranslation(0.0, -height, 0.0) - transition.updateSublayerTransformOffset(layer: adPanelNode.layer, offset: CGPoint()) - } - } else if let adPanelNode = self.adPanelNode { - dismissedAdPanelNode = adPanelNode - self.adPanelNode = nil - } - - var dismissedFeePanelNode: ChatFeePanelNode? - var feePanelHeight: CGFloat? - - var displayFeePanel = false - if let chatHistoryState = self.chatPresentationInterfaceState.chatHistoryState, case .loaded(false, _) = chatHistoryState { - if let user = self.chatPresentationInterfaceState.renderedPeer?.peer as? TelegramUser, user.botInfo == nil { - if !self.chatPresentationInterfaceState.peerIsBlocked, let paidMessageStars = self.chatPresentationInterfaceState.contactStatus?.peerStatusSettings?.paidMessageStars, paidMessageStars.value > 0 { - displayFeePanel = true - } - } else if self.chatPresentationInterfaceState.removePaidMessageFeeData != nil { - displayFeePanel = true - } - } - - var immediatelyLayoutFeePanelNodeAndAnimateAppearance = false - if displayFeePanel { - var animateAppearance = false - let feePanelNode: ChatFeePanelNode - if let current = self.feePanelNode { - feePanelNode = current - } else { - feePanelNode = ChatFeePanelNode(context: self.context) - feePanelNode.controllerInteraction = self.controllerInteraction - feePanelNode.clipsToBounds = true - animateAppearance = true - } - - if self.feePanelNode != feePanelNode { - dismissedFeePanelNode = self.feePanelNode - self.feePanelNode = feePanelNode - self.titleAccessoryPanelContainer.addSubnode(feePanelNode) - } - - let height = feePanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, leftDisplayInset: 0.0, transition: animateAppearance ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState) - - feePanelHeight = height - if transition.isAnimated && animateAppearance { - immediatelyLayoutFeePanelNodeAndAnimateAppearance = true - } - - if immediatelyLayoutFeePanelNodeAndAnimateAppearance { - feePanelNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) - feePanelNode.subnodeTransform = CATransform3DMakeTranslation(0.0, -height, 0.0) - transition.updateSublayerTransformOffset(layer: feePanelNode.layer, offset: CGPoint()) - } - } else if let feePanelNode = self.feePanelNode { - dismissedFeePanelNode = feePanelNode - self.feePanelNode = nil - } - var isSidebarOpen = false if let floatingTopicsPanel = self.floatingTopicsPanel { isSidebarOpen = floatingTopicsPanel.component.location == .side @@ -1830,54 +1926,10 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { transition.updateFrame(node: self.inputContextPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height))) transition.updateFrame(node: self.inputContextOverTextPanelContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: layout.size.height))) - - var extraNavigationBarHeight: CGFloat = 0.0 - var extraNavigationBarHitTestSlop: CGFloat = 0.0 - var titlePanelsContentOffset: CGFloat = 0.0 - - var titleAccessoryPanelFrame: CGRect? - let titleAccessoryPanelBaseY = titlePanelsContentOffset - if let _ = self.titleAccessoryPanelNode, let panelHeight = titleAccessoryPanelHeight { - titleAccessoryPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: titlePanelsContentOffset), size: CGSize(width: layout.size.width, height: panelHeight)) - insets.top += panelHeight - extraNavigationBarHeight += titleAccessoryPanelBackgroundHeight ?? 0.0 - extraNavigationBarHitTestSlop = titleAccessoryPanelHitTestSlop ?? 0.0 - titlePanelsContentOffset += panelHeight - } + updateExtraNavigationBarBackgroundHeight(0.0, 0.0, nil, transition) - let feePanelBaseY = titlePanelsContentOffset - - var translationPanelFrame: CGRect? - if let _ = self.chatTranslationPanel, let panelHeight = translationPanelHeight { - translationPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: extraNavigationBarHeight), size: CGSize(width: layout.size.width, height: panelHeight)) - insets.top += panelHeight - extraNavigationBarHeight += panelHeight - } - - var importStatusPanelFrame: CGRect? - if let _ = self.chatImportStatusPanel, let panelHeight = importStatusPanelHeight { - importStatusPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: panelHeight)) - insets.top += panelHeight - } - - var adPanelFrame: CGRect? - if let _ = self.adPanelNode, let panelHeight = adPanelHeight { - adPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: extraNavigationBarHeight), size: CGSize(width: layout.size.width, height: panelHeight)) - insets.top += panelHeight - extraNavigationBarHeight += panelHeight - } - - var feePanelFrame: CGRect? - if let _ = self.feePanelNode, let panelHeight = feePanelHeight { - feePanelFrame = CGRect(origin: CGPoint(x: 0.0, y: extraNavigationBarHeight), size: CGSize(width: layout.size.width, height: panelHeight)) - insets.top += panelHeight - extraNavigationBarHeight += panelHeight - } - - updateExtraNavigationBarBackgroundHeight(extraNavigationBarHeight, extraNavigationBarHitTestSlop, nil, transition) - - let sidePanelTopInset: CGFloat = insets.top + var sidePanelTopInset: CGFloat = insets.top + 4.0 let contentBounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width - wrappingInsets.left - wrappingInsets.right, height: layout.size.height - wrappingInsets.top - wrappingInsets.bottom) @@ -2129,7 +2181,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { additionalOffset = 80.0 } if let _ = inputPanelSize { - inputPanelHideOffset += -40.0 - additionalOffset + inputPanelHideOffset += -48.0 - additionalOffset } if let accessoryPanelSize = accessoryPanelSize { inputPanelHideOffset += -accessoryPanelSize.height - additionalOffset @@ -2146,7 +2198,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { } if self.secondaryInputPanelNode != nil { - secondaryInputPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - bottomOverflowOffset - inputPanelsHeight - secondaryInputPanelSize!.height - 8.0), size: CGSize(width: layout.size.width, height: secondaryInputPanelSize!.height)) + secondaryInputPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - insets.bottom - bottomOverflowOffset - inputPanelsHeight - 8.0 - secondaryInputPanelSize!.height - 8.0), size: CGSize(width: layout.size.width, height: secondaryInputPanelSize!.height)) if self.dismissedAsOverlay { secondaryInputPanelFrame!.origin.y = layout.size.height } @@ -2199,12 +2251,13 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { } if let bottomBackgroundEdgeEffectNode { var blurFrame = inputBackgroundFrame - blurFrame.origin.y -= 26.0 + blurFrame.origin.y -= 18.0 blurFrame.size.height = max(100.0, layout.size.height - blurFrame.origin.y) transition.updateFrame(node: bottomBackgroundEdgeEffectNode, frame: blurFrame) bottomBackgroundEdgeEffectNode.update( rect: blurFrame, - edge: WallpaperEdgeEffectEdge(edge: .bottom, size: 80.0), + edge: WallpaperEdgeEffectEdge(edge: .bottom, size: 100.0), + blur: false, containerSize: wallpaperBounds.size, transition: transition ) @@ -2328,15 +2381,6 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { } if displayTopDimNode { - var topInset = listInsets.bottom + UIScreenPixel - if let titleAccessoryPanelHeight = titleAccessoryPanelHeight { - if expandTopDimNode { - topInset -= titleAccessoryPanelHeight - } else { - topInset -= UIScreenPixel - } - } - let inputPanelOrigin = layout.size.height - insets.bottom - bottomOverflowOffset - inputPanelsHeight if expandTopDimNode { @@ -2399,6 +2443,29 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { } }) + var topBackgroundEdgeEffectNode: WallpaperEdgeEffectNode? + if let current = self.topBackgroundEdgeEffectNode { + topBackgroundEdgeEffectNode = current + } else { + if let value = self.backgroundNode.makeEdgeEffectNode() { + topBackgroundEdgeEffectNode = value + self.topBackgroundEdgeEffectNode = value + self.historyNodeContainer.view.superview?.insertSubview(value.view, aboveSubview: self.historyNodeContainer.view) + } + } + if let topBackgroundEdgeEffectNode { + var blurFrame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: max(100.0, listInsets.bottom + 10.0))) + blurFrame.origin.y = listInsets.bottom + 10.0 - blurFrame.height + transition.updateFrame(node: topBackgroundEdgeEffectNode, frame: blurFrame) + topBackgroundEdgeEffectNode.update( + rect: blurFrame, + edge: WallpaperEdgeEffectEdge(edge: .top, size: 100.0), + blur: false, + containerSize: wallpaperBounds.size, + transition: transition + ) + } + if self.isScrollingLockedAtTop { switch self.historyNode.visibleContentOffset() { case let .known(value) where value <= CGFloat.ulpOfOne: @@ -2454,6 +2521,18 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { transition.updateBounds(node: self.inputPanelOverlayNode, bounds: CGRect(origin: CGPoint(x: 0.0, y: apparentInputBackgroundFrame.origin.y), size: layout.size), beginWithCurrentState: true) transition.updateFrame(node: self.inputPanelBackgroundNode, frame: apparentInputBackgroundFrame, beginWithCurrentState: true) + if let headerPanelsComponentView = self.headerPanelsView?.view, let headerPanelsSize { + let headerPanelsFrame = CGRect(origin: CGPoint(x: layout.safeInsets.left, y: sidePanelTopInset), size: headerPanelsSize) + var headerPanelsTransition = ComponentTransition(transition) + if headerPanelsComponentView.superview == nil { + headerPanelsTransition.animateAlpha(view: headerPanelsComponentView, from: 0.0, to: 1.0) + headerPanelsTransition = headerPanelsTransition.withAnimation(.none) + self.floatingTopicsPanelContainer.view.addSubview(headerPanelsComponentView) + } + headerPanelsTransition.setFrame(view: headerPanelsComponentView, frame: headerPanelsFrame) + sidePanelTopInset += headerPanelsSize.height + 2.0 + } + let floatingTopicsPanelContainerFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: 0.0, height: layout.size.height)) transition.updateFrame(node: self.floatingTopicsPanelContainer, frame: floatingTopicsPanelContainerFrame) if let floatingTopicsPanel = self.floatingTopicsPanel { @@ -2502,11 +2581,6 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { })*/ dismissedFloatingTopicsPanelView.removeFromSuperview() } - - if let navigationBarBackgroundContent = self.navigationBarBackgroundContent { - transition.updateFrame(node: navigationBarBackgroundContent, frame: CGRect(origin: .zero, size: CGSize(width: layout.size.width, height: navigationBarHeight + (titleAccessoryPanelBackgroundHeight ?? 0.0) + (translationPanelHeight ?? 0.0))), beginWithCurrentState: true) - navigationBarBackgroundContent.update(rect: CGRect(origin: .zero, size: CGSize(width: layout.size.width, height: navigationBarHeight + (titleAccessoryPanelBackgroundHeight ?? 0.0) + (translationPanelHeight ?? 0.0))), within: layout.size, transition: transition) - } transition.updateFrame(node: self.contentDimNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: apparentInputBackgroundFrame.origin.y))) @@ -2517,47 +2591,6 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { transition.updateFrame(node: self.navigateButtons, frame: apparentNavigateButtonsFrame) self.navigateButtons.update(rect: apparentNavigateButtonsFrame, within: layout.size, transition: transition) - - if let titleAccessoryPanelNode = self.titleAccessoryPanelNode, let titleAccessoryPanelFrame, !titleAccessoryPanelNode.frame.equalTo(titleAccessoryPanelFrame) { - let previousFrame = titleAccessoryPanelNode.frame - titleAccessoryPanelNode.frame = titleAccessoryPanelFrame - if transition.isAnimated && previousFrame.width != titleAccessoryPanelFrame.width { - } else if immediatelyLayoutAccessoryPanelAndAnimateAppearance { - transition.animatePositionAdditive(node: titleAccessoryPanelNode, offset: CGPoint(x: 0.0, y: -titleAccessoryPanelFrame.height)) - } else if previousFrame.minY != titleAccessoryPanelFrame.minY { - transition.animatePositionAdditive(node: titleAccessoryPanelNode, offset: CGPoint(x: 0.0, y: previousFrame.minY - titleAccessoryPanelFrame.minY)) - } - } - - if let chatTranslationPanel = self.chatTranslationPanel, let translationPanelFrame, !chatTranslationPanel.frame.equalTo(translationPanelFrame) { - let previousFrame = chatTranslationPanel.frame - chatTranslationPanel.frame = translationPanelFrame - if transition.isAnimated && previousFrame.width != translationPanelFrame.width { - } else if immediatelyLayoutTranslationPanelNodeAndAnimateAppearance { - transition.animatePositionAdditive(node: chatTranslationPanel, offset: CGPoint(x: 0.0, y: -translationPanelFrame.height)) - } else if previousFrame.minY != translationPanelFrame.minY { - transition.animatePositionAdditive(node: chatTranslationPanel, offset: CGPoint(x: 0.0, y: previousFrame.minY - translationPanelFrame.minY)) - } - } - - if let chatImportStatusPanel = self.chatImportStatusPanel, let importStatusPanelFrame, !chatImportStatusPanel.frame.equalTo(importStatusPanelFrame) { - chatImportStatusPanel.frame = importStatusPanelFrame - } - - if let adPanelNode = self.adPanelNode, let adPanelFrame, !adPanelNode.frame.equalTo(adPanelFrame) { - adPanelNode.frame = adPanelFrame - } - - if let feePanelNode = self.feePanelNode, let feePanelFrame, !feePanelNode.frame.equalTo(feePanelFrame) { - let previousFrame = feePanelNode.frame - feePanelNode.frame = feePanelFrame - if transition.isAnimated && previousFrame.width != feePanelFrame.width { - } else if immediatelyLayoutFeePanelNodeAndAnimateAppearance { - transition.animatePositionAdditive(node: feePanelNode, offset: CGPoint(x: 0.0, y: -feePanelFrame.height)) - } else if previousFrame.minY != feePanelFrame.minY { - transition.animatePositionAdditive(node: feePanelNode, offset: CGPoint(x: 0.0, y: previousFrame.minY - feePanelFrame.minY)) - } - } if let secondaryInputPanelNode = self.secondaryInputPanelNode, let apparentSecondaryInputPanelFrame = apparentSecondaryInputPanelFrame, !secondaryInputPanelNode.frame.equalTo(apparentSecondaryInputPanelFrame) { if immediatelyLayoutSecondaryInputPanelAndAnimateAppearance { @@ -2599,10 +2632,8 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { inputContextPanelNode.updateLayout(size: panelFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: insets.bottom + inputPanelsHeight + 8.0, transition: .immediate, interfaceState: self.chatPresentationInterfaceState) } - if !inputContextPanelNode.frame.equalTo(panelFrame) || inputContextPanelNode.theme !== self.chatPresentationInterfaceState.theme { - transition.updateFrame(node: inputContextPanelNode, frame: panelFrame) - inputContextPanelNode.updateLayout(size: panelFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: 0.0, transition: transition, interfaceState: self.chatPresentationInterfaceState) - } + transition.updateFrame(node: inputContextPanelNode, frame: panelFrame) + inputContextPanelNode.updateLayout(size: panelFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: insets.bottom + inputPanelsHeight + 8.0, transition: transition, interfaceState: self.chatPresentationInterfaceState) } if let overlayContextPanelNode = self.overlayContextPanelNode { @@ -2640,60 +2671,6 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { } } - if let dismissedTitleAccessoryPanelNode { - var dismissedPanelFrame = dismissedTitleAccessoryPanelNode.frame - transition.updateSublayerTransformOffset(layer: dismissedTitleAccessoryPanelNode.layer, offset: CGPoint(x: 0.0, y: -dismissedPanelFrame.height)) - dismissedPanelFrame.origin.y = titleAccessoryPanelBaseY - dismissedTitleAccessoryPanelNode.clipsToBounds = true - dismissedPanelFrame.size.height = 0.0 - if transition.isAnimated { - dismissedTitleAccessoryPanelNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) - } - transition.updateFrame(node: dismissedTitleAccessoryPanelNode, frame: dismissedPanelFrame, completion: { [weak dismissedTitleAccessoryPanelNode] _ in - dismissedTitleAccessoryPanelNode?.removeFromSupernode() - }) - } - - if let dismissedTranslationPanelNode { - var dismissedPanelFrame = dismissedTranslationPanelNode.frame - dismissedPanelFrame.origin.y = -dismissedPanelFrame.size.height - transition.updateAlpha(node: dismissedTranslationPanelNode, alpha: 0.0, completion: { [weak dismissedTranslationPanelNode] _ in - dismissedTranslationPanelNode?.removeFromSupernode() - }) - dismissedTranslationPanelNode.animateOut() - } - - if let dismissedImportStatusPanelNode { - var dismissedPanelFrame = dismissedImportStatusPanelNode.frame - dismissedPanelFrame.origin.y = -dismissedPanelFrame.size.height - transition.updateFrame(node: dismissedImportStatusPanelNode, frame: dismissedPanelFrame, completion: { [weak dismissedImportStatusPanelNode] _ in - dismissedImportStatusPanelNode?.removeFromSupernode() - }) - } - - if let dismissedAdPanelNode { - var dismissedPanelFrame = dismissedAdPanelNode.frame - dismissedPanelFrame.origin.y = -dismissedPanelFrame.size.height - transition.updateAlpha(node: dismissedAdPanelNode, alpha: 0.0) - transition.updateFrame(node: dismissedAdPanelNode, frame: dismissedPanelFrame, completion: { [weak dismissedAdPanelNode] _ in - dismissedAdPanelNode?.removeFromSupernode() - }) - } - - if let dismissedFeePanelNode { - var dismissedPanelFrame = dismissedFeePanelNode.frame - transition.updateSublayerTransformOffset(layer: dismissedFeePanelNode.layer, offset: CGPoint(x: 0.0, y: -dismissedPanelFrame.height)) - dismissedPanelFrame.origin.y = feePanelBaseY - dismissedFeePanelNode.clipsToBounds = true - dismissedPanelFrame.size.height = 0.0 - if transition.isAnimated { - dismissedFeePanelNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) - } - transition.updateFrame(node: dismissedFeePanelNode, frame: dismissedPanelFrame, completion: { [weak dismissedFeePanelNode] _ in - dismissedFeePanelNode?.removeFromSupernode() - }) - } - if let inputPanelNode = self.inputPanelNode, let apparentInputPanelFrame = apparentInputPanelFrame, !inputPanelNode.frame.equalTo(apparentInputPanelFrame) { if immediatelyLayoutInputPanelAndAnimateAppearance { inputPanelNode.frame = apparentInputPanelFrame.offsetBy(dx: 0.0, dy: apparentInputPanelFrame.height + previousInputPanelBackgroundFrame.maxY - apparentInputBackgroundFrame.maxY) @@ -3485,22 +3462,6 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { self.updatePlainInputSeparator(transition: .immediate) self.backgroundNode.updateBubbleTheme(bubbleTheme: chatPresentationInterfaceState.theme, bubbleCorners: chatPresentationInterfaceState.bubbleCorners) - - if self.backgroundNode.hasExtraBubbleBackground() { - if self.navigationBarBackgroundContent == nil { - if let navigationBarBackgroundContent = self.backgroundNode.makeBubbleBackground(for: .free) { - self.navigationBarBackgroundContent = navigationBarBackgroundContent - - navigationBarBackgroundContent.allowsGroupOpacity = true - navigationBarBackgroundContent.implicitContentUpdate = false - navigationBarBackgroundContent.alpha = 0.3 - self.navigationBar?.insertSubnode(navigationBarBackgroundContent, at: 1) - } - } - } else { - self.navigationBarBackgroundContent?.removeFromSupernode() - self.navigationBarBackgroundContent = nil - } } let keepSendButtonEnabled = chatPresentationInterfaceState.interfaceState.forwardMessageIds != nil || chatPresentationInterfaceState.interfaceState.editMessage != nil @@ -4445,11 +4406,11 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { } } - if !found { + if !found, let controller = self.controller { let authorName: String = (replyMessage.author.flatMap(EnginePeer.init))?.compactDisplayTitle ?? "" let errorTextData = self.chatPresentationInterfaceState.strings.Chat_ErrorQuoteOutdatedText(authorName) let errorText = errorTextData.string - self.controller?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.context.sharedContext.currentPresentationData.with({ $0 })), title: self.chatPresentationInterfaceState.strings.Chat_ErrorQuoteOutdatedTitle, text: errorText, actions: [ + controller.present(textAlertController(context: self.context, title: self.chatPresentationInterfaceState.strings.Chat_ErrorQuoteOutdatedTitle, text: errorText, actions: [ TextAlertAction(type: .genericAction, title: self.chatPresentationInterfaceState.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: self.chatPresentationInterfaceState.strings.Chat_ErrorQuoteOutdatedActionEdit, action: { [weak self] in guard let self, let controller = self.controller else { @@ -4909,11 +4870,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { titleViewSnapshotState: ChatTitleView.SnapshotState?, avatarSnapshotState: ChatAvatarNavigationNode.SnapshotState? ) -> SnapshotState { - var titleAccessoryPanelSnapshot: UIView? - if let titleAccessoryPanelNode = self.titleAccessoryPanelNode, let snapshot = titleAccessoryPanelNode.view.snapshotView(afterScreenUpdates: false) { - snapshot.frame = titleAccessoryPanelNode.frame - titleAccessoryPanelSnapshot = snapshot - } + let titleAccessoryPanelSnapshot: UIView? = nil var inputPanelNodeSnapshot: UIView? if let inputPanelNode = self.inputPanelNode, let snapshot = inputPanelNode.view.snapshotView(afterScreenUpdates: false) { snapshot.frame = inputPanelNode.frame @@ -4944,25 +4901,9 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { if let titleAccessoryPanelSnapshot = snapshotState.titleAccessoryPanelSnapshot { self.titleAccessoryPanelContainer.view.addSubview(titleAccessoryPanelSnapshot) - if let _ = self.titleAccessoryPanelNode { - titleAccessoryPanelSnapshot.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak titleAccessoryPanelSnapshot] _ in - titleAccessoryPanelSnapshot?.removeFromSuperview() - }) - titleAccessoryPanelSnapshot.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -10.0), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true) - } else { - titleAccessoryPanelSnapshot.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -titleAccessoryPanelSnapshot.bounds.height), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true, completion: { [weak titleAccessoryPanelSnapshot] _ in - titleAccessoryPanelSnapshot?.removeFromSuperview() - }) - } - } - - if let titleAccessoryPanelNode = self.titleAccessoryPanelNode { - if let _ = snapshotState.titleAccessoryPanelSnapshot { - titleAccessoryPanelNode.layer.animatePosition(from: CGPoint(x: 0.0, y: 10.0), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: true, additive: true) - titleAccessoryPanelNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3, removeOnCompletion: true) - } else { - titleAccessoryPanelNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -titleAccessoryPanelNode.bounds.height), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: true, additive: true) - } + titleAccessoryPanelSnapshot.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -titleAccessoryPanelSnapshot.bounds.height), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true, completion: { [weak titleAccessoryPanelSnapshot] _ in + titleAccessoryPanelSnapshot?.removeFromSuperview() + }) } if let navigationBar = self.navigationBar { diff --git a/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift b/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift index 551db3f3..5ea627c2 100644 --- a/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift +++ b/submodules/TelegramUI/Sources/ChatControllerOpenAttachmentMenu.swift @@ -1013,14 +1013,14 @@ extension ChatControllerImpl { } else { text = strongSelf.presentationData.strings.Chat_AttachmentLimitReached } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) }, presentCantSendMultipleFiles: { guard let strongSelf = self else { return } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.Chat_AttachmentMultipleFilesDisabled, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Chat_AttachmentMultipleFilesDisabled, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) }, presentJpegConversionAlert: { completion in - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.MediaPicker_JpegConversionText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.MediaPicker_KeepHeic, action: { + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.MediaPicker_JpegConversionText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.MediaPicker_KeepHeic, action: { completion(false) }), TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.MediaPicker_ConvertToJpeg, action: { completion(true) @@ -1463,7 +1463,7 @@ extension ChatControllerImpl { text = strongSelf.presentationData.strings.Chat_AttachmentLimitReached } - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) }, presentSchedulePicker: { [weak self] media, done in if let strongSelf = self { strongSelf.presentScheduleTimePicker(style: media ? .media : .default, completion: { [weak self] time, repeatPeriod in @@ -1588,19 +1588,19 @@ extension ChatControllerImpl { switch itemType { case .image: if bannedSendPhotos != nil { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.restrictedSendingContentsText(), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.restrictedSendingContentsText(), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) return false } case .video: if bannedSendVideos != nil { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.restrictedSendingContentsText(), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.restrictedSendingContentsText(), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) return false } case .gif: if bannedSendGifs != nil { - strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.restrictedSendingContentsText(), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) + strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.restrictedSendingContentsText(), actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) return false } diff --git a/submodules/TelegramUI/Sources/ChatControllerOpenMessageReactionContextMenu.swift b/submodules/TelegramUI/Sources/ChatControllerOpenMessageReactionContextMenu.swift index 9640bc72..bd1b5f7e 100644 --- a/submodules/TelegramUI/Sources/ChatControllerOpenMessageReactionContextMenu.swift +++ b/submodules/TelegramUI/Sources/ChatControllerOpenMessageReactionContextMenu.swift @@ -20,6 +20,7 @@ import ChatMessageItemCommon import ChatMessageItemView import ReactionSelectionNode import AnimatedTextComponent +import PresentationDataUtils extension ChatControllerImpl { func presentTagPremiumPaywall() { @@ -392,7 +393,7 @@ extension ChatControllerImpl { if case let .known(reactionSettings) = reactionSettings, let starsAllowed = reactionSettings.starsAllowed, !starsAllowed { if let peer = self.presentationInterfaceState.renderedPeer?.chatMainPeer { - self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Chat_ToastStarsReactionsDisabled(peer.debugDisplayTitle).string, actions: [ + self.present(textAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, title: nil, text: self.presentationData.strings.Chat_ToastStarsReactionsDisabled(peer.debugDisplayTitle).string, actions: [ TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_OK, action: {}) ]), in: .window(.root)) } diff --git a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift index b02cb643..44097d73 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift @@ -37,11 +37,11 @@ func chatHistoryEntriesForView( currentState: ChatHistoryEntriesForViewState, context: AccountContext, location: ChatLocation, + subject: ChatControllerSubject?, view: MessageHistoryView, includeUnreadEntry: Bool, includeEmptyEntry: Bool, includeChatInfoEntry: Bool, - includeSearchEntry: Bool, includeEmbeddedSavedChatInfo: Bool, reverse: Bool, groupMessages: Bool, @@ -332,7 +332,10 @@ func chatHistoryEntriesForView( } } var attributes = attributes - attributes.displayContinueThreadFooter = true + if let subject, case .pinnedMessages = subject { + } else { + attributes.displayContinueThreadFooter = true + } entries[i] = .MessageEntry(message, presentationData, isRead, location, selection, attributes) break outer default: @@ -508,7 +511,12 @@ func chatHistoryEntriesForView( entries.insert(.ChatInfoEntry(.botInfo(title: "", text: presentationData.strings.VerificationCodes_DescriptionText, photo: nil, video: nil), presentationData), at: 0) } else if let cachedPeerData = cachedPeerData as? CachedUserData { if let botInfo = cachedPeerData.botInfo, !botInfo.description.isEmpty { - entries.insert(.ChatInfoEntry(.botInfo(title: presentationData.strings.Bot_DescriptionTitle, text: botInfo.description, photo: botInfo.photo, video: botInfo.video), presentationData), at: 0) + if location.threadId == nil { + if let subject, case .pinnedMessages = subject { + } else { + entries.insert(.ChatInfoEntry(.botInfo(title: presentationData.strings.Bot_DescriptionTitle, text: botInfo.description, photo: botInfo.photo, video: botInfo.video), presentationData), at: 0) + } + } } else if let peerStatusSettings = cachedPeerData.peerStatusSettings, peerStatusSettings.registrationDate != nil || peerStatusSettings.phoneCountry != nil { if peerStatusSettings.flags.contains(.canAddContact) || peerStatusSettings.flags.contains(.canReport) || peerStatusSettings.flags.contains(.canBlock) { @@ -680,15 +688,12 @@ func chatHistoryEntriesForView( entries.append(.MessageEntry(updatedMessage, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false, authorStoryStats: nil, displayContinueThreadFooter: false))) } } - } else if includeSearchEntry { - if view.laterId == nil { - if !view.entries.isEmpty { - entries.append(.SearchEntry(presentationData.theme.theme, presentationData.strings)) - } - } } if addBotForumHeader { - entries.append(.ChatInfoEntry(.newThreadInfo, presentationData)) + if let subject, case .pinnedMessages = subject { + } else { + entries.append(.ChatInfoEntry(.newThreadInfo, presentationData)) + } } if includeEmbeddedSavedChatInfo, let peerId = location.peerId { if !view.isLoading && view.laterId == nil { diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 83bfb769..eec79b5e 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -11,9 +11,9 @@ import TelegramUIPreferences import MediaResources import AccountContext import TemporaryCachedPeerDataManager -import ChatListSearchItemNode import Emoji import AppBundle +import ItemListUI import ListMessageItem import AccountContext import ChatInterfaceState @@ -216,7 +216,7 @@ extension ListMessageItemInteraction { } } -private func mappedInsertEntries(context: AccountContext, chatLocation: ChatLocation, associatedData: ChatMessageItemAssociatedData, controllerInteraction: ChatControllerInteraction, mode: ChatHistoryListMode, lastHeaderId: Int64, isSavedMusic: Bool, canReorder: Bool, entries: [ChatHistoryViewTransitionInsertEntry]) -> [ListViewInsertItem] { +private func mappedInsertEntries(context: AccountContext, chatLocation: ChatLocation, associatedData: ChatMessageItemAssociatedData, controllerInteraction: ChatControllerInteraction, mode: ChatHistoryListMode, lastHeaderId: Int64, isSavedMusic: Bool, canReorder: Bool, entries: [ChatHistoryViewTransitionInsertEntry], systemStyle: ItemListSystemStyle) -> [ListViewInsertItem] { var disableFloatingDateHeaders = false if case .customChatContents = chatLocation { disableFloatingDateHeaders = true @@ -229,7 +229,7 @@ private func mappedInsertEntries(context: AccountContext, chatLocation: ChatLoca switch mode { case .bubbles: item = ChatMessageItemImpl(presentationData: presentationData, context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, content: .message(message: message, read: read, selection: selection, attributes: attributes, location: location), disableDate: disableFloatingDateHeaders || message.timestamp < 10) - case let .list(_, _, _, displayHeaders, hintLinks, isGlobalSearch): + case let .list(_, _, displayHeaders, hintLinks, isGlobalSearch): let displayHeader: Bool switch displayHeaders { case .none: @@ -239,7 +239,7 @@ private func mappedInsertEntries(context: AccountContext, chatLocation: ChatLoca case .allButLast: displayHeader = listMessageDateHeaderId(timestamp: message.timestamp) != lastHeaderId } - item = ListMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, interaction: ListMessageItemInteraction(controllerInteraction: controllerInteraction), message: message, translateToLanguage: associatedData.translateToLanguage, selection: selection, displayHeader: displayHeader, hintIsLink: hintLinks, isGlobalSearchResult: isGlobalSearch, isSavedMusic: isSavedMusic, canReorder: canReorder) + item = ListMessageItem(presentationData: presentationData, systemStyle: systemStyle, context: context, chatLocation: chatLocation, interaction: ListMessageItemInteraction(controllerInteraction: controllerInteraction), message: message, translateToLanguage: associatedData.translateToLanguage, selection: selection, displayHeader: displayHeader, hintIsLink: hintLinks, isGlobalSearchResult: isGlobalSearch, isSavedMusic: isSavedMusic, canReorder: canReorder) } return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint) case let .MessageGroupEntry(_, messages, presentationData): @@ -249,7 +249,7 @@ private func mappedInsertEntries(context: AccountContext, chatLocation: ChatLoca item = ChatMessageItemImpl(presentationData: presentationData, context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, content: .group(messages: messages), disableDate: disableFloatingDateHeaders) case .list: assertionFailure() - item = ListMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, interaction: ListMessageItemInteraction(controllerInteraction: controllerInteraction), message: messages[0].0, selection: .none, displayHeader: false) + item = ListMessageItem(presentationData: presentationData, systemStyle: systemStyle, context: context, chatLocation: chatLocation, interaction: ListMessageItemInteraction(controllerInteraction: controllerInteraction), message: messages[0].0, selection: .none, displayHeader: false) } return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint) case let .UnreadEntry(_, presentationData): @@ -267,15 +267,11 @@ private func mappedInsertEntries(context: AccountContext, chatLocation: ChatLoca item = ChatNewThreadInfoItem(controllerInteraction: controllerInteraction, presentationData: presentationData, context: context) } return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint) - case let .SearchEntry(theme, strings): - return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSearchItem(theme: theme, placeholder: strings.Common_Search, activate: { - controllerInteraction.openSearch() - }), directionHint: entry.directionHint) } } } -private func mappedUpdateEntries(context: AccountContext, chatLocation: ChatLocation, associatedData: ChatMessageItemAssociatedData, controllerInteraction: ChatControllerInteraction, mode: ChatHistoryListMode, lastHeaderId: Int64, isSavedMusic: Bool, canReorder: Bool, entries: [ChatHistoryViewTransitionUpdateEntry]) -> [ListViewUpdateItem] { +private func mappedUpdateEntries(context: AccountContext, chatLocation: ChatLocation, associatedData: ChatMessageItemAssociatedData, controllerInteraction: ChatControllerInteraction, mode: ChatHistoryListMode, lastHeaderId: Int64, isSavedMusic: Bool, canReorder: Bool, entries: [ChatHistoryViewTransitionUpdateEntry], systemStyle: ItemListSystemStyle) -> [ListViewUpdateItem] { var disableFloatingDateHeaders = false if case .customChatContents = chatLocation { disableFloatingDateHeaders = true @@ -288,7 +284,7 @@ private func mappedUpdateEntries(context: AccountContext, chatLocation: ChatLoca switch mode { case .bubbles: item = ChatMessageItemImpl(presentationData: presentationData, context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, content: .message(message: message, read: read, selection: selection, attributes: attributes, location: location), disableDate: disableFloatingDateHeaders || message.timestamp < 10) - case let .list(_, _, _, displayHeaders, hintLinks, isGlobalSearch): + case let .list(_, _, displayHeaders, hintLinks, isGlobalSearch): let displayHeader: Bool switch displayHeaders { case .none: @@ -298,7 +294,7 @@ private func mappedUpdateEntries(context: AccountContext, chatLocation: ChatLoca case .allButLast: displayHeader = listMessageDateHeaderId(timestamp: message.timestamp) != lastHeaderId } - item = ListMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, interaction: ListMessageItemInteraction(controllerInteraction: controllerInteraction), message: message, translateToLanguage: associatedData.translateToLanguage, selection: selection, displayHeader: displayHeader, hintIsLink: hintLinks, isGlobalSearchResult: isGlobalSearch, isSavedMusic: isSavedMusic, canReorder: canReorder) + item = ListMessageItem(presentationData: presentationData, systemStyle: systemStyle, context: context, chatLocation: chatLocation, interaction: ListMessageItemInteraction(controllerInteraction: controllerInteraction), message: message, translateToLanguage: associatedData.translateToLanguage, selection: selection, displayHeader: displayHeader, hintIsLink: hintLinks, isGlobalSearchResult: isGlobalSearch, isSavedMusic: isSavedMusic, canReorder: canReorder) } return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint) case let .MessageGroupEntry(_, messages, presentationData): @@ -308,7 +304,7 @@ private func mappedUpdateEntries(context: AccountContext, chatLocation: ChatLoca item = ChatMessageItemImpl(presentationData: presentationData, context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, content: .group(messages: messages), disableDate: disableFloatingDateHeaders) case .list: assertionFailure() - item = ListMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, interaction: ListMessageItemInteraction(controllerInteraction: controllerInteraction), message: messages[0].0, selection: .none, displayHeader: false) + item = ListMessageItem(presentationData: presentationData, systemStyle: systemStyle, context: context, chatLocation: chatLocation, interaction: ListMessageItemInteraction(controllerInteraction: controllerInteraction), message: messages[0].0, selection: .none, displayHeader: false) } return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint) case let .UnreadEntry(_, presentationData): @@ -326,16 +322,12 @@ private func mappedUpdateEntries(context: AccountContext, chatLocation: ChatLoca item = ChatNewThreadInfoItem(controllerInteraction: controllerInteraction, presentationData: presentationData, context: context) } return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: item, directionHint: entry.directionHint) - case let .SearchEntry(theme, strings): - return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSearchItem(theme: theme, placeholder: strings.Common_Search, activate: { - controllerInteraction.openSearch() - }), directionHint: entry.directionHint) } } } -private func mappedChatHistoryViewListTransition(context: AccountContext, chatLocation: ChatLocation, associatedData: ChatMessageItemAssociatedData, controllerInteraction: ChatControllerInteraction, mode: ChatHistoryListMode, lastHeaderId: Int64, isSavedMusic: Bool, canReorder: Bool, animateFromPreviousFilter: Bool, transition: ChatHistoryViewTransition) -> ChatHistoryListViewTransition { - return ChatHistoryListViewTransition(historyView: transition.historyView, deleteItems: transition.deleteItems, insertItems: mappedInsertEntries(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, lastHeaderId: lastHeaderId, isSavedMusic: isSavedMusic, canReorder: canReorder, entries: transition.insertEntries), updateItems: mappedUpdateEntries(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, lastHeaderId: lastHeaderId, isSavedMusic: isSavedMusic, canReorder: canReorder, entries: transition.updateEntries), options: transition.options, scrollToItem: transition.scrollToItem, stationaryItemRange: transition.stationaryItemRange, initialData: transition.initialData, keyboardButtonsMessage: transition.keyboardButtonsMessage, cachedData: transition.cachedData, cachedDataMessages: transition.cachedDataMessages, readStateData: transition.readStateData, scrolledToIndex: transition.scrolledToIndex, scrolledToSomeIndex: transition.scrolledToSomeIndex, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, animateIn: transition.animateIn, reason: transition.reason, flashIndicators: transition.flashIndicators, animateFromPreviousFilter: animateFromPreviousFilter) +private func mappedChatHistoryViewListTransition(context: AccountContext, chatLocation: ChatLocation, associatedData: ChatMessageItemAssociatedData, controllerInteraction: ChatControllerInteraction, mode: ChatHistoryListMode, lastHeaderId: Int64, isSavedMusic: Bool, canReorder: Bool, animateFromPreviousFilter: Bool, transition: ChatHistoryViewTransition, systemStyle: ItemListSystemStyle) -> ChatHistoryListViewTransition { + return ChatHistoryListViewTransition(historyView: transition.historyView, deleteItems: transition.deleteItems, insertItems: mappedInsertEntries(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, lastHeaderId: lastHeaderId, isSavedMusic: isSavedMusic, canReorder: canReorder, entries: transition.insertEntries, systemStyle: systemStyle), updateItems: mappedUpdateEntries(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, lastHeaderId: lastHeaderId, isSavedMusic: isSavedMusic, canReorder: canReorder, entries: transition.updateEntries, systemStyle: systemStyle), options: transition.options, scrollToItem: transition.scrollToItem, stationaryItemRange: transition.stationaryItemRange, initialData: transition.initialData, keyboardButtonsMessage: transition.keyboardButtonsMessage, cachedData: transition.cachedData, cachedDataMessages: transition.cachedDataMessages, readStateData: transition.readStateData, scrolledToIndex: transition.scrolledToIndex, scrolledToSomeIndex: transition.scrolledToSomeIndex, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, animateIn: transition.animateIn, reason: transition.reason, flashIndicators: transition.flashIndicators, animateFromPreviousFilter: animateFromPreviousFilter) } final class ChatHistoryTransactionOpaqueState { @@ -475,6 +467,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto static let fixedAdMessageStableId: UInt32 = UInt32.max - 5000 public let context: AccountContext + private let systemStyle: ItemListSystemStyle private(set) var chatLocation: ChatLocation private let chatLocationContextHolder: Atomic private let source: ChatHistoryListSource @@ -762,7 +755,23 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto private let initTimestamp: Double - public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal), chatLocation: ChatLocation, chatLocationContextHolder: Atomic, adMessagesContext: AdMessagesHistoryContext?, tag: HistoryViewInputTag?, source: ChatHistoryListSource, subject: ChatControllerSubject?, controllerInteraction: ChatControllerInteraction, selectedMessages: Signal?, NoError>, mode: ChatHistoryListMode = .bubbles, rotated: Bool = false, isChatPreview: Bool, messageTransitionNode: @escaping () -> ChatMessageTransitionNodeImpl?) { + public init( + context: AccountContext, + updatedPresentationData: (initial: PresentationData, signal: Signal), + systemStyle: ItemListSystemStyle = .legacy, + chatLocation: ChatLocation, + chatLocationContextHolder: Atomic, + adMessagesContext: AdMessagesHistoryContext?, + tag: HistoryViewInputTag?, + source: ChatHistoryListSource, + subject: ChatControllerSubject?, + controllerInteraction: ChatControllerInteraction, + selectedMessages: Signal?, NoError>, + mode: ChatHistoryListMode = .bubbles, + rotated: Bool = false, + isChatPreview: Bool, + messageTransitionNode: @escaping () -> ChatMessageTransitionNodeImpl? + ) { self.initTimestamp = CFAbsoluteTimeGetCurrent() var tag = tag @@ -771,6 +780,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto } self.context = context + self.systemStyle = systemStyle self.chatLocation = chatLocation self.chatLocationContextHolder = chatLocationContextHolder self.source = source @@ -915,7 +925,6 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto } } - self.dynamicBounceEnabled = !self.currentPresentationData.disableAnimations self.experimentalSnapScrollToItem = false //self.debugInfo = true @@ -1321,6 +1330,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto let messageTransitionNode = self.messageTransitionNode let mode = self.mode let rotated = self.rotated + let systemStyle = self.systemStyle var resetScrollingMessageId: (index: MessageIndex, offset: CGFloat)? @@ -1801,7 +1811,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto return historyViewUpdateValue } } - + let startTime = CFAbsoluteTimeGetCurrent() var measure_isFirstTime = true let messageViewQueue = Queue.mainQueue() @@ -1931,7 +1941,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto let forceSynchronous = true let rawTransition = preparedChatHistoryViewTransition(from: previous, to: processedView, reason: reason, reverse: false, chatLocation: chatLocation, source: source, controllerInteraction: controllerInteraction, scrollPosition: nil, scrollAnimationCurve: nil, initialData: initialData?.initialData, keyboardButtonsMessage: nil, cachedData: initialData?.cachedData, cachedDataMessages: initialData?.cachedDataMessages, readStateData: initialData?.readStateData, flashIndicators: false, updatedMessageSelection: previousSelectedMessages != selectedMessages, messageTransitionNode: messageTransitionNode(), allUpdated: false) - var mappedTransition = mappedChatHistoryViewListTransition(context: context, chatLocation: chatLocation, associatedData: previousViewValue.associatedData, controllerInteraction: controllerInteraction, mode: mode, lastHeaderId: 0, isSavedMusic: isSavedMusic, canReorder: canReorder, animateFromPreviousFilter: resetScrolling, transition: rawTransition) + var mappedTransition = mappedChatHistoryViewListTransition(context: context, chatLocation: chatLocation, associatedData: previousViewValue.associatedData, controllerInteraction: controllerInteraction, mode: mode, lastHeaderId: 0, isSavedMusic: isSavedMusic, canReorder: canReorder, animateFromPreviousFilter: resetScrolling, transition: rawTransition, systemStyle: systemStyle) if disableAnimations { mappedTransition.options.remove(.AnimateInsertion) @@ -2022,14 +2032,11 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto var reverse = false var reverseGroups = false - var includeSearchEntry = false - if case let .list(search, reverseValue, reverseGroupsValue, _, _, _) = mode { - includeSearchEntry = search + if case let .list(reverseValue, reverseGroupsValue, _, _, _) = mode { reverse = reverseValue reverseGroups = reverseGroupsValue } - var isPremium = false if case let .user(user) = accountPeer, user.isPremium { isPremium = true @@ -2083,11 +2090,11 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto currentState: previousChatHistoryEntriesForViewState, context: context, location: chatLocation, + subject: subject, view: view, includeUnreadEntry: mode == .bubbles, includeEmptyEntry: mode == .bubbles && tag == nil, includeChatInfoEntry: mode == .bubbles, - includeSearchEntry: includeSearchEntry && tag != nil, includeEmbeddedSavedChatInfo: includeEmbeddedSavedChatInfo, reverse: reverse, groupMessages: mode == .bubbles, @@ -2317,12 +2324,15 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto } var keyboardButtonsMessage = view.topTaggedMessages.first + if keyboardButtonsMessage != nil && keyboardButtonsMessage?.threadId != chatLocation.threadId { + keyboardButtonsMessage = nil + } if let keyboardButtonsMessageValue = keyboardButtonsMessage, keyboardButtonsMessageValue.isRestricted(platform: "ios", contentSettings: context.currentContentSettings.with({ $0 })) { keyboardButtonsMessage = nil } let rawTransition = preparedChatHistoryViewTransition(from: previous, to: processedView, reason: reason, reverse: reverse, chatLocation: chatLocation, source: source, controllerInteraction: controllerInteraction, scrollPosition: updatedScrollPosition, scrollAnimationCurve: scrollAnimationCurve, initialData: initialData?.initialData, keyboardButtonsMessage: keyboardButtonsMessage, cachedData: initialData?.cachedData, cachedDataMessages: initialData?.cachedDataMessages, readStateData: initialData?.readStateData, flashIndicators: flashIndicators, updatedMessageSelection: previousSelectedMessages != selectedMessages, messageTransitionNode: messageTransitionNode(), allUpdated: !isSavedMusic || forceUpdateAll) - var mappedTransition = mappedChatHistoryViewListTransition(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, lastHeaderId: lastHeaderId, isSavedMusic: isSavedMusic, canReorder: processedView.filteredEntries.count > 1 && canReorder, animateFromPreviousFilter: resetScrolling, transition: rawTransition) + var mappedTransition = mappedChatHistoryViewListTransition(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, lastHeaderId: lastHeaderId, isSavedMusic: isSavedMusic, canReorder: processedView.filteredEntries.count > 1 && canReorder, animateFromPreviousFilter: resetScrolling, transition: rawTransition, systemStyle: systemStyle) if disableAnimations { mappedTransition.options.remove(.AnimateInsertion) @@ -2447,7 +2457,6 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto let chatPresentationData = ChatPresentationData(theme: themeData, fontSize: presentationData.chatFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: true, largeEmoji: presentationData.largeEmoji, chatBubbleCorners: presentationData.chatBubbleCorners, animatedEmojiScale: animatedEmojiConfig.scale) strongSelf.currentPresentationData = chatPresentationData - strongSelf.dynamicBounceEnabled = false strongSelf.forEachItemHeaderNode { itemHeaderNode in if let dateNode = itemHeaderNode as? ChatMessageDateHeaderNodeImpl { @@ -4551,7 +4560,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto switch self.mode { case .bubbles: item = ChatMessageItemImpl(presentationData: presentationData, context: self.context, chatLocation: self.chatLocation, associatedData: associatedData, controllerInteraction: self.controllerInteraction, content: .message(message: message, read: read, selection: selection, attributes: attributes, location: location), disableDate: disableFloatingDateHeaders) - case let .list(_, _, _, displayHeaders, hintLinks, isGlobalSearch): + case let .list(_, _, displayHeaders, hintLinks, isGlobalSearch): let displayHeader: Bool switch displayHeaders { case .none: @@ -4561,7 +4570,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto case .allButLast: displayHeader = listMessageDateHeaderId(timestamp: message.timestamp) != historyView.lastHeaderId } - item = ListMessageItem(presentationData: presentationData, context: self.context, chatLocation: self.chatLocation, interaction: ListMessageItemInteraction(controllerInteraction: self.controllerInteraction), message: message, translateToLanguage: associatedData.translateToLanguage, selection: selection, displayHeader: displayHeader, hintIsLink: hintLinks, isGlobalSearchResult: isGlobalSearch) + item = ListMessageItem(presentationData: presentationData, systemStyle: self.systemStyle, context: self.context, chatLocation: self.chatLocation, interaction: ListMessageItemInteraction(controllerInteraction: self.controllerInteraction), message: message, translateToLanguage: associatedData.translateToLanguage, selection: selection, displayHeader: displayHeader, hintIsLink: hintLinks, isGlobalSearchResult: isGlobalSearch) } let updateItem = ListViewUpdateItem(index: index, previousIndex: index, item: item, directionHint: nil) @@ -4582,7 +4591,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto item = ChatMessageItemImpl(presentationData: presentationData, context: self.context, chatLocation: self.chatLocation, associatedData: associatedData, controllerInteraction: self.controllerInteraction, content: .group(messages: messages), disableDate: disableFloatingDateHeaders) case .list: assertionFailure() - item = ListMessageItem(presentationData: presentationData, context: context, chatLocation: chatLocation, interaction: ListMessageItemInteraction(controllerInteraction: controllerInteraction), message: messages[0].0, selection: .none, displayHeader: false) + item = ListMessageItem(presentationData: presentationData, systemStyle: self.systemStyle, context: self.context, chatLocation: chatLocation, interaction: ListMessageItemInteraction(controllerInteraction: controllerInteraction), message: messages[0].0, selection: .none, displayHeader: false) } let updateItem = ListViewUpdateItem(index: index, previousIndex: index, item: item, directionHint: nil) @@ -4629,7 +4638,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto switch self.mode { case .bubbles: item = ChatMessageItemImpl(presentationData: presentationData, context: self.context, chatLocation: self.chatLocation, associatedData: associatedData, controllerInteraction: self.controllerInteraction, content: .message(message: message, read: read, selection: selection, attributes: attributes, location: location), disableDate: disableFloatingDateHeaders) - case let .list(_, _, _, displayHeaders, hintLinks, isGlobalSearch): + case let .list(_, _, displayHeaders, hintLinks, isGlobalSearch): let displayHeader: Bool switch displayHeaders { case .none: @@ -4639,7 +4648,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto case .allButLast: displayHeader = listMessageDateHeaderId(timestamp: message.timestamp) != historyView.lastHeaderId } - item = ListMessageItem(presentationData: presentationData, context: self.context, chatLocation: self.chatLocation, interaction: ListMessageItemInteraction(controllerInteraction: self.controllerInteraction), message: message, translateToLanguage: associatedData.translateToLanguage, selection: selection, displayHeader: displayHeader, hintIsLink: hintLinks, isGlobalSearchResult: isGlobalSearch) + item = ListMessageItem(presentationData: presentationData, systemStyle: self.systemStyle, context: self.context, chatLocation: self.chatLocation, interaction: ListMessageItemInteraction(controllerInteraction: self.controllerInteraction), message: message, translateToLanguage: associatedData.translateToLanguage, selection: selection, displayHeader: displayHeader, hintIsLink: hintLinks, isGlobalSearchResult: isGlobalSearch) } let updateItem = ListViewUpdateItem(index: index, previousIndex: index, item: item, directionHint: nil) self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [updateItem], options: [.AnimateInsertion], scrollToItem: nil, additionalScrollDistance: 0.0, updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) diff --git a/submodules/TelegramUI/Sources/ChatHistoryNavigationButtonNode.swift b/submodules/TelegramUI/Sources/ChatHistoryNavigationButtonNode.swift index 57811058..616a8f94 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryNavigationButtonNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryNavigationButtonNode.swift @@ -20,21 +20,21 @@ enum ChatHistoryNavigationButtonType { class ChatHistoryNavigationButtonNode: ContextControllerSourceNode { let containerNode: ContextExtractedContentContainingNode - let buttonNode: HighlightTrackingButtonNode private let backgroundView: GlassBackgroundView let imageView: GlassBackgroundView.ContentImageView private let badgeBackgroundView: GlassBackgroundView private let badgeTextNode: ImmediateAnimatedCountLabelNode + private var tapRecognizer: UITapGestureRecognizer? var tapped: (() -> Void)? { didSet { - if (oldValue != nil) != (self.tapped != nil) { - if self.tapped != nil { - self.buttonNode.addTarget(self, action: #selector(self.onTap), forControlEvents: .touchUpInside) - } else { - self.buttonNode.removeTarget(self, action: #selector(self.onTap), forControlEvents: .touchUpInside) - } - } + self.tapRecognizer?.isEnabled = self.tapped != nil && self.isEnabled + } + } + + var isEnabled: Bool = true { + didSet { + self.tapRecognizer?.isEnabled = self.tapped != nil && self.isEnabled } } @@ -54,7 +54,6 @@ class ChatHistoryNavigationButtonNode: ContextControllerSourceNode { self.type = type self.containerNode = ContextExtractedContentContainingNode() - self.buttonNode = HighlightTrackingButtonNode() self.backgroundView = GlassBackgroundView() @@ -80,7 +79,10 @@ class ChatHistoryNavigationButtonNode: ContextControllerSourceNode { super.init() - self.targetNodeForActivationProgress = self.buttonNode + let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.onTapGesture(_:))) + self.tapRecognizer = tapRecognizer + self.backgroundView.contentView.addGestureRecognizer(tapRecognizer) + tapRecognizer.isEnabled = false self.addSubnode(self.containerNode) @@ -89,18 +91,16 @@ class ChatHistoryNavigationButtonNode: ContextControllerSourceNode { self.containerNode.contentNode.frame = CGRect(origin: CGPoint(), size: size) self.containerNode.contentRect = CGRect(origin: CGPoint(), size: size) - self.buttonNode.frame = CGRect(origin: CGPoint(), size: size) - self.containerNode.contentNode.addSubnode(self.buttonNode) + self.containerNode.contentNode.view.addSubview(self.backgroundView) - self.buttonNode.view.addSubview(self.backgroundView) self.backgroundView.frame = CGRect(origin: CGPoint(), size: size) - self.backgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), transition: .immediate) + self.backgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), isInteractive: true, transition: .immediate) self.imageView.tintColor = theme.chat.inputPanel.panelControlColor self.backgroundView.contentView.addSubview(self.imageView) self.imageView.frame = CGRect(origin: CGPoint(), size: size) - self.buttonNode.view.addSubview(self.badgeBackgroundView) + self.containerNode.contentNode.view.addSubview(self.badgeBackgroundView) self.badgeBackgroundView.contentView.addSubview(self.badgeTextNode.view) self.frame = CGRect(origin: CGPoint(), size: size) @@ -143,9 +143,11 @@ class ChatHistoryNavigationButtonNode: ContextControllerSourceNode { self.absoluteRect = (rect, containerSize) } - @objc func onTap() { - if let tapped = self.tapped { - tapped() + @objc private func onTapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + if let tapped = self.tapped { + tapped() + } } } diff --git a/submodules/TelegramUI/Sources/ChatHistoryNavigationButtons.swift b/submodules/TelegramUI/Sources/ChatHistoryNavigationButtons.swift index 190d7b52..c860391d 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryNavigationButtons.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryNavigationButtons.swift @@ -183,7 +183,7 @@ final class ChatHistoryNavigationButtons: ASDisplayNode { if let down = self.directionButtonState.down { self.downButton.imageView.alpha = down.isEnabled ? 1.0 : 0.5 - self.downButton.buttonNode.isEnabled = down.isEnabled + self.downButton.isEnabled = down.isEnabled mentionsOffset += buttonSize.height + 12.0 upOffset += buttonSize.height + 12.0 @@ -203,7 +203,7 @@ final class ChatHistoryNavigationButtons: ASDisplayNode { if let up = self.directionButtonState.up { self.upButton.imageView.alpha = up.isEnabled ? 1.0 : 0.5 - self.upButton.buttonNode.isEnabled = up.isEnabled + self.upButton.isEnabled = up.isEnabled mentionsOffset += buttonSize.height + 12.0 diff --git a/submodules/TelegramUI/Sources/ChatInterfaceInputContextPanels.swift b/submodules/TelegramUI/Sources/ChatInterfaceInputContextPanels.swift index e0a074b4..0944f199 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceInputContextPanels.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceInputContextPanels.swift @@ -153,35 +153,8 @@ func textInputContextPanel(context: AccountContext, chatPresentationInterfaceSta } else { return nil } - case let .contextRequestResult(_, results): - let _ = results + case .contextRequestResult: return nil - /*if let results = results, (!results.results.isEmpty || results.switchPeer != nil || results.webView != nil) { - switch results.presentation { - case .list: - if let currentPanel = currentPanel as? VerticalListContextResultsChatInputContextPanelNode { - currentPanel.updateResults(results) - return currentPanel - } else { - let panel = VerticalListContextResultsChatInputContextPanelNode(context: context, theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings, fontSize: chatPresentationInterfaceState.fontSize, chatPresentationContext: controllerInteraction.presentationContext) - panel.interfaceInteraction = interfaceInteraction - panel.updateResults(results) - return panel - } - case .media: - if let currentPanel = currentPanel as? HorizontalListContextResultsChatInputContextPanelNode { - currentPanel.updateResults(results) - return currentPanel - } else { - let panel = HorizontalListContextResultsChatInputContextPanelNode(context: context, theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings, fontSize: chatPresentationInterfaceState.fontSize, chatPresentationContext: controllerInteraction.presentationContext) - panel.interfaceInteraction = interfaceInteraction - panel.updateResults(results) - return panel - } - } - } else { - return nil - }*/ } } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateAccessoryPanels.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateAccessoryPanels.swift index 7c213a88..9358698e 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateAccessoryPanels.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateAccessoryPanels.swift @@ -18,6 +18,7 @@ import Display import Markdown import TextFormat import TelegramPresentationData +import AlertComponent func textInputAccessoryPanel( context: AccountContext, @@ -112,7 +113,6 @@ func textInputAccessoryPanel( let theme = chatPresentationInterfaceState.theme let strings = chatPresentationInterfaceState.strings let nameDisplayOrder = chatPresentationInterfaceState.nameDisplayOrder - let fontSize = chatPresentationInterfaceState.fontSize return AnyComponentWithIdentity(id: "forward", component: AnyComponent(ChatInputMessageAccessoryPanel( context: context, @@ -158,23 +158,45 @@ func textInputAccessoryPanel( string = strings.Conversation_ForwardOptions_Text(messages, peerDisplayTitle) } - let font = Font.regular(floor(fontSize.baseDisplaySize * 15.0 / 17.0)) - let boldFont = Font.semibold(floor(fontSize.baseDisplaySize * 15.0 / 17.0)) - let body = MarkdownAttributeSet(font: font, textColor: theme.actionSheet.secondaryTextColor) - let bold = MarkdownAttributeSet(font: boldFont, textColor: theme.actionSheet.secondaryTextColor) + let font = Font.regular(15.0) + let boldFont = Font.semibold(15.0) + let body = MarkdownAttributeSet(font: font, textColor: theme.actionSheet.primaryTextColor) + let bold = MarkdownAttributeSet(font: boldFont, textColor: theme.actionSheet.primaryTextColor) + let text = addAttributesToStringWithRanges(string._tuple, body: body, argumentAttributes: [0: bold, 1: bold], textAlignment: .natural) - let title = NSAttributedString(string: strings.Conversation_ForwardOptions_Title(messageCount), font: Font.semibold(floor(fontSize.baseDisplaySize)), textColor: theme.actionSheet.primaryTextColor, paragraphAlignment: .center) - let text = addAttributesToStringWithRanges(string._tuple, body: body, argumentAttributes: [0: bold, 1: bold], textAlignment: .center) + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent( + title: strings.Conversation_ForwardOptions_Title(messageCount) + ) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .attributed(text)) + ) + )) - let alertController = richTextAlertController(context: context, title: title, text: text, actions: [TextAlertAction(type: .genericAction, title: strings.Conversation_ForwardOptions_ShowOptions, action: { - guard let sourceView else { - return - } - interfaceInteraction?.presentForwardOptions(sourceView) - let _ = ApplicationSpecificNotice.incrementChatForwardOptionsTip(accountManager: context.sharedContext.accountManager, count: 3).start() - }), TextAlertAction(type: .destructiveAction, title: strings.Conversation_ForwardOptions_CancelForwarding, action: { - interfaceInteraction?.dismissForwardMessages() - })], actionLayout: .vertical) + let alertController = AlertScreen( + context: context, + configuration: AlertScreen.Configuration(actionAlignment: .vertical), + content: content, + actions: [ + .init(title: strings.Conversation_ForwardOptions_ShowOptions, action: { + guard let sourceView else { + return + } + interfaceInteraction?.presentForwardOptions(sourceView) + let _ = ApplicationSpecificNotice.incrementChatForwardOptionsTip(accountManager: context.sharedContext.accountManager, count: 3).start() + }), + .init(title: strings.Conversation_ForwardOptions_CancelForwarding, type: .destructive, action: { + interfaceInteraction?.dismissForwardMessages() + }) + ] + ) interfaceInteraction?.presentController(alertController, nil) } } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 81195abb..0d05ccc3 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -37,6 +37,10 @@ import ChatMessageItemView import ChatMessageBubbleItemNode import AdsInfoScreen import AdsReportScreen +import LegacyComponents +import LocalMediaResources +import MediaResources +import AVFoundation private struct MessageContextMenuData { let starStatus: Bool? @@ -647,8 +651,13 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState if let restrictedText = restrictedText { storeMessageTextInPasteboard(restrictedText, entities: nil) } else { - if let translationState = chatPresentationInterfaceState.translationState, translationState.isEnabled, - let translation = message.attributes.first(where: { ($0 as? TranslationMessageAttribute)?.toLang == translationState.toLang }) as? TranslationMessageAttribute, !translation.text.isEmpty { + var translateToLang: String? + if let translationState = chatPresentationInterfaceState.translationState, translationState.isEnabled { + translateToLang = translationState.toLang + } + if controllerInteraction.summarizedMessageIds.contains(message.id), let attribute = message.attributes.first(where: { $0 is SummarizationMessageAttribute }) as? SummarizationMessageAttribute, let summary = attribute.summaryForLang(translateToLang) { + storeMessageTextInPasteboard(summary.text, entities: summary.entities) + } else if let translateToLang, let translation = message.attributes.first(where: { ($0 as? TranslationMessageAttribute)?.toLang == translateToLang }) as? TranslationMessageAttribute, !translation.text.isEmpty { storeMessageTextInPasteboard(translation.text, entities: translation.entities) } else { storeMessageTextInPasteboard(message.text, entities: messageEntities) @@ -1178,8 +1187,8 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState c?.dismiss(completion: { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - controllerInteraction.presentController(standardTextAlertController( - theme: AlertControllerTheme(presentationData: presentationData), + controllerInteraction.presentController(textAlertController( + context: context, title: presentationData.strings.Chat_ScheduledForceSendProcessingVideo_Title, text: presentationData.strings.Chat_ScheduledForceSendProcessingVideo_Text, actions: [ @@ -1282,11 +1291,16 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState if let restrictedText = restrictedText { storeMessageTextInPasteboard(restrictedText, entities: nil) } else { - if let translationState = chatPresentationInterfaceState.translationState, translationState.isEnabled, - let translation = message.attributes.first(where: { ($0 as? TranslationMessageAttribute)?.toLang == translationState.toLang }) as? TranslationMessageAttribute, !translation.text.isEmpty { + var translateToLang: String? + if let translationState = chatPresentationInterfaceState.translationState, translationState.isEnabled { + translateToLang = translationState.toLang + } + if controllerInteraction.summarizedMessageIds.contains(message.id), let attribute = message.attributes.first(where: { $0 is SummarizationMessageAttribute }) as? SummarizationMessageAttribute, let summary = attribute.summaryForLang(translateToLang) { + storeMessageTextInPasteboard(summary.text, entities: summary.entities) + } else if let translateToLang, let translation = message.attributes.first(where: { ($0 as? TranslationMessageAttribute)?.toLang == translateToLang }) as? TranslationMessageAttribute, !translation.text.isEmpty { storeMessageTextInPasteboard(translation.text, entities: translation.entities) } else { - storeMessageTextInPasteboard(messageText, entities: messageEntities) + storeMessageTextInPasteboard(message.text, entities: messageEntities) } } @@ -1355,10 +1369,17 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Message"), color: theme.actionSheet.primaryTextColor) }, action: { _, f in var text = messageText - if let translationState = chatPresentationInterfaceState.translationState, translationState.isEnabled, - let translation = message.attributes.first(where: { ($0 as? TranslationMessageAttribute)?.toLang == translationState.toLang }) as? TranslationMessageAttribute, !translation.text.isEmpty { + + var translateToLang: String? + if let translationState = chatPresentationInterfaceState.translationState, translationState.isEnabled { + translateToLang = translationState.toLang + } + if controllerInteraction.summarizedMessageIds.contains(message.id), let attribute = message.attributes.first(where: { $0 is SummarizationMessageAttribute }) as? SummarizationMessageAttribute, let summary = attribute.summaryForLang(translateToLang) { + text = summary.text + } else if let translateToLang, let translation = message.attributes.first(where: { ($0 as? TranslationMessageAttribute)?.toLang == translateToLang }) as? TranslationMessageAttribute, !translation.text.isEmpty { text = translation.text } + controllerInteraction.performTextSelectionAction(message, !isCopyProtected, NSAttributedString(string: text), .speak) f(.default) }))) @@ -1392,6 +1413,16 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState }) f(.default) }))) + + // MARK: - Ghostgram: Send as Circle (Video Message) + if isVideo, let file = message.media.first(where: { $0 is TelegramMediaFile }) as? TelegramMediaFile { + actions.append(.action(ContextMenuActionItem(text: "Отправить как кружок", icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/VideoMessage"), color: theme.actionSheet.primaryTextColor) + }, action: { (controller: ContextControllerProtocol?, dismiss: @escaping (ContextMenuActionResult) -> Void) in + dismiss(.default) + convertVideoToCircleAndSend(context: context, message: message, file: file, controllerInteraction: controllerInteraction) + }))) + } } } @@ -1426,16 +1457,8 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState } } - if (loggingSettings.logToFile || loggingSettings.logToConsole) && !downloadableMediaResourceInfos.isEmpty { - actions.append(.action(ContextMenuActionItem(text: "Send Logs", icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Message"), color: theme.actionSheet.primaryTextColor) - }, action: { _, f in - triggerDebugSendLogsUI(context: context, additionalInfo: "User has requested download logs for \(downloadableMediaResourceInfos)", pushController: { c in - controllerInteraction.navigationController()?.pushViewController(c) - }) - f(.default) - }))) - } + // REMOVED: Send Logs button + var threadId: Int64? var threadMessageCount: Int = 0 @@ -1511,6 +1534,56 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState }))) } + // MARK: - Ghostgram: Local Edit (Client-Side Only) + // Allows editing any message text locally without sending to server + if !message.text.isEmpty { + actions.append(.action(ContextMenuActionItem(text: "Изменить локально", icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.actionSheet.primaryTextColor) + }, action: { [weak controllerInteraction] _, f in + f(.dismissWithoutContent) + + guard let controllerInteraction = controllerInteraction else { return } + + let peerId = message.id.peerId.toInt64() + let messageId = message.id.id + let currentText = LocalEditManager.shared.getLocalEdit(peerId: peerId, messageId: messageId) ?? message.text + + let alertController = UIAlertController( + title: "Изменить локально", + message: "Это изменение видно только вам и сбросится после перезапуска приложения", + preferredStyle: .alert + ) + + alertController.addTextField { textField in + textField.text = currentText + textField.placeholder = "Введите новый текст" + } + + alertController.addAction(UIAlertAction(title: "Отмена", style: .cancel, handler: nil)) + alertController.addAction(UIAlertAction(title: "Сохранить", style: .default) { [weak alertController] _ in + guard let textField = alertController?.textFields?.first, + let newText = textField.text, !newText.isEmpty else { + return + } + + LocalEditManager.shared.setLocalEdit(peerId: peerId, messageId: messageId, newText: newText) + + // Request UI update + controllerInteraction.requestMessageUpdate(message.id, true) + }) + + if let chatControllerNode = controllerInteraction.chatControllerNode() { + if let viewController = chatControllerNode.view.window?.rootViewController { + var topController = viewController + while let presented = topController.presentedViewController { + topController = presented + } + topController.present(alertController, animated: true, completion: nil) + } + } + }))) + } + if let message = messages.first, message.id.namespace == Namespaces.Message.Cloud, let channel = message.peers[message.id.peerId] as? TelegramChannel, channel.isMonoForum { var canSuggestPost = true for media in message.media { @@ -2012,6 +2085,33 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState } actions.insert(.custom(ChatReadReportContextItem(context: context, message: message, hasReadReports: false, isEdit: true, stats: MessageReadStats(reactionCount: 0, peers: [], readTimestamps: [:]), action: nil), false), at: 0) } + + // GHOSTGRAM: Show edit history button if message was edited and has saved history + if isEdited && EditHistoryManager.shared.hasEditHistory(peerId: message.id.peerId.toInt64(), messageId: message.id.id) { + actions.append(.action(ContextMenuActionItem(text: "История", icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.actionSheet.primaryTextColor) + }, action: { c, f in + let editHistory = EditHistoryManager.shared.getEditHistory(peerId: message.id.peerId.toInt64(), messageId: message.id.id) + var historyText = "Оригинальные версии:\n\n" + for (index, record) in editHistory.enumerated() { + let date = Date(timeIntervalSince1970: TimeInterval(record.editDate)) + let formatter = DateFormatter() + formatter.dateStyle = .short + formatter.timeStyle = .short + historyText += "\(index + 1). [\(formatter.string(from: date))]\n\(record.text)\n\n" + } + + c?.dismiss(completion: { + let alertController = textAlertController( + context: context, + title: "История правок", + text: historyText, + actions: [TextAlertAction(type: .defaultAction, title: "OK", action: {})] + ) + controllerInteraction.presentController(alertController, nil) + }) + }))) + } if canViewAuthor { actions.insert(.custom(ChatMessageAuthorContextItem(context: context, message: message, action: { c, f, peer in @@ -2162,8 +2262,13 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState if let restrictedText = restrictedText { storeMessageTextInPasteboard(restrictedText, entities: nil) } else { - if let translationState = chatPresentationInterfaceState.translationState, translationState.isEnabled, - let translation = message.attributes.first(where: { ($0 as? TranslationMessageAttribute)?.toLang == translationState.toLang }) as? TranslationMessageAttribute, !translation.text.isEmpty { + var translateToLang: String? + if let translationState = chatPresentationInterfaceState.translationState, translationState.isEnabled { + translateToLang = translationState.toLang + } + if controllerInteraction.summarizedMessageIds.contains(message.id), let attribute = message.attributes.first(where: { $0 is SummarizationMessageAttribute }) as? SummarizationMessageAttribute, let summary = attribute.summaryForLang(translateToLang) { + storeMessageTextInPasteboard(summary.text, entities: summary.entities) + } else if let translateToLang, let translation = message.attributes.first(where: { ($0 as? TranslationMessageAttribute)?.toLang == translateToLang }) as? TranslationMessageAttribute, !translation.text.isEmpty { storeMessageTextInPasteboard(translation.text, entities: translation.entities) } else { storeMessageTextInPasteboard(message.text, entities: messageEntities) @@ -2331,7 +2436,7 @@ func chatAvailableMessageActionsImpl(engine: TelegramEngine, accountPeerId: Peer } } - if message.isCopyProtected() || message.containsSecretMedia { + if (message.isCopyProtected() || message.containsSecretMedia) && !MiscSettingsManager.shared.shouldBypassCopyProtection { isCopyProtected = true } for media in message.media { @@ -2437,7 +2542,8 @@ func chatAvailableMessageActionsImpl(engine: TelegramEngine, accountPeerId: Peer } } if !message.containsSecretMedia && !isAction && !isShareProtected { - if message.id.peerId.namespace != Namespaces.Peer.SecretChat && !message.isCopyProtected() { + let copyProtectedCheck = message.isCopyProtected() && !MiscSettingsManager.shared.shouldBypassCopyProtection + if message.id.peerId.namespace != Namespaces.Peer.SecretChat && !copyProtectedCheck { if !(message.flags.isSending || message.flags.contains(.Failed)) { optionsMap[id]!.insert(.forward) } @@ -2453,7 +2559,8 @@ func chatAvailableMessageActionsImpl(engine: TelegramEngine, accountPeerId: Peer } } else if let group = peer as? TelegramGroup { if message.id.peerId.namespace != Namespaces.Peer.SecretChat && !message.containsSecretMedia { - if !isAction && !message.isCopyProtected() && !isShareProtected { + let copyProtectedCheck = message.isCopyProtected() && !MiscSettingsManager.shared.shouldBypassCopyProtection + if !isAction && !copyProtectedCheck && !isShareProtected { if !(message.flags.isSending || message.flags.contains(.Failed)) { optionsMap[id]!.insert(.forward) } @@ -2472,7 +2579,8 @@ func chatAvailableMessageActionsImpl(engine: TelegramEngine, accountPeerId: Peer optionsMap[id]!.insert(.report) } } else if let user = peer as? TelegramUser { - if !isScheduled && message.id.peerId.namespace != Namespaces.Peer.SecretChat && !message.containsSecretMedia && !isAction && !message.id.peerId.isReplies && !message.isCopyProtected() && !isShareProtected { + let copyProtectedCheck = message.isCopyProtected() && !MiscSettingsManager.shared.shouldBypassCopyProtection + if !isScheduled && message.id.peerId.namespace != Namespaces.Peer.SecretChat && !message.containsSecretMedia && !isAction && !message.id.peerId.isReplies && !copyProtectedCheck && !isShareProtected { if !(message.flags.isSending || message.flags.contains(.Failed)) { optionsMap[id]!.insert(.forward) } @@ -3860,3 +3968,222 @@ private final class ChatRateTranscriptionContextItemNode: ASDisplayNode, Context return self } } + +// MARK: - Ghostgram: Video to Circle Conversion Helper +private func convertVideoToCircleAndSend( + context: AccountContext, + message: Message, + file: TelegramMediaFile, + controllerInteraction: ChatControllerInteraction +) { + // Show progress + controllerInteraction.displayUndo(.info(title: nil, text: "Конвертация в кружок...", timeout: 5.0, customUndoText: nil)) + + // Get video file path + let resourcePath = context.account.postbox.mediaBox.completedResourcePath(file.resource) + + guard let videoPath = resourcePath, FileManager.default.fileExists(atPath: videoPath) else { + controllerInteraction.displayUndo(.info(title: "Ошибка", text: "Видео ещё не загружено. Откройте его для загрузки.", timeout: nil, customUndoText: nil)) + return + } + + // Get video dimensions + var videoDimensions = CGSize(width: 240, height: 240) + for attr in file.attributes { + switch attr { + case let .Video(_, size, _, _, _, _): + videoDimensions = size.cgSize + default: + break + } + } + + // Create square crop (center crop to 1:1 aspect ratio) + let minDim = min(videoDimensions.width, videoDimensions.height) + let cropX = (videoDimensions.width - minDim) / 2 + let cropY = (videoDimensions.height - minDim) / 2 + let cropRect = CGRect(x: cropX, y: cropY, width: minDim, height: minDim) + + // Create adjustments for video message format + let adjustments = TGVideoEditAdjustments( + originalSize: videoDimensions, + cropRect: cropRect, + cropOrientation: .up, + cropRotation: 0.0, + cropLockedAspectRatio: 1.0, + cropMirrored: false, + trimStartValue: 0.0, + trimEndValue: 0.0, + toolValues: nil, + paintingData: nil, + sendAsGif: false, + preset: TGMediaVideoConversionPresetVideoMessage + ) + + // Generate output path + let outputPath = NSTemporaryDirectory() + "circle_\(Int64.random(in: Int64.min...Int64.max)).mp4" + + print("[Ghostgram Circle] Starting conversion from: \(videoPath)") + print("[Ghostgram Circle] Output path: \(outputPath)") + print("[Ghostgram Circle] Video dimensions: \(videoDimensions)") + + // Start conversion in background + DispatchQueue.global(qos: .userInitiated).async { + // AVFoundation needs file extension to detect format + // Postbox stores files without extension, so copy to temp with .mp4 + let tempSourcePath = NSTemporaryDirectory() + "source_\(Int64.random(in: Int64.min...Int64.max)).mp4" + + do { + try FileManager.default.copyItem(atPath: videoPath, toPath: tempSourcePath) + print("[Ghostgram Circle] Copied to temp with extension: \(tempSourcePath)") + } catch { + print("[Ghostgram Circle] ERROR: Failed to copy file: \(error)") + DispatchQueue.main.async { + controllerInteraction.displayUndo(.info(title: "Ошибка", text: "Не удалось подготовить видео", timeout: nil, customUndoText: nil)) + } + return + } + + let videoUrl = URL(fileURLWithPath: tempSourcePath) + let avAsset = AVURLAsset(url: videoUrl) + + print("[Ghostgram Circle] Created AVAsset, starting conversion...") + + // Use TGMediaVideoConverter with correct signature + guard let signal = TGMediaVideoConverter.convert( + avAsset, + adjustments: adjustments, + path: outputPath, + watcher: nil, + entityRenderer: nil + ) else { + print("[Ghostgram Circle] ERROR: Failed to create conversion signal!") + DispatchQueue.main.async { + controllerInteraction.displayUndo(.info(title: "Ошибка", text: "Не удалось запустить конвертацию", timeout: nil, customUndoText: nil)) + } + return + } + + print("[Ghostgram Circle] Conversion signal created, starting...") + + // Use SBlockDisposable instead to keep signal alive + let disposable = signal.start(next: { result in + print("[Ghostgram Circle] Got result: \(String(describing: result))") + + guard let result = result as? TGMediaVideoConversionResult, + let resultUrl = result.fileURL else { + print("[Ghostgram Circle] ERROR: Result is not TGMediaVideoConversionResult or no fileURL") + DispatchQueue.main.async { + controllerInteraction.displayUndo(.info(title: "Ошибка", text: "Не удалось конвертировать видео", timeout: nil, customUndoText: nil)) + } + return + } + + print("[Ghostgram Circle] Conversion SUCCESS! File: \(resultUrl.path)") + + let finalDuration = result.duration + let finalDimensions = result.dimensions + + DispatchQueue.main.async { + // Create preview image + var previewRepresentations: [TelegramMediaImageRepresentation] = [] + if let previewImage = result.coverImage { + let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min...Int64.max)) + let thumbnailSize = CGSize(width: 240, height: 240) + if let thumbnailImage = TGScaleImageToPixelSize(previewImage, thumbnailSize), + let thumbnailData = thumbnailImage.jpegData(compressionQuality: 0.4) { + context.account.postbox.mediaBox.storeResourceData(resource.id, data: thumbnailData) + previewRepresentations.append(TelegramMediaImageRepresentation( + dimensions: PixelDimensions(thumbnailSize), + resource: resource, + progressiveSizes: [], + immediateThumbnailData: nil, + hasVideo: false, + isPersonal: false + )) + } + } + + // Create local resource for converted video + let videoResource = LocalFileVideoMediaResource( + randomId: Int64.random(in: Int64.min...Int64.max), + path: resultUrl.path, + adjustments: nil + ) + + // Create TelegramMediaFile with instantRoundVideo flag + let media = TelegramMediaFile( + fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min...Int64.max)), + partialReference: nil, + resource: videoResource, + previewRepresentations: previewRepresentations, + videoThumbnails: [], + immediateThumbnailData: nil, + mimeType: "video/mp4", + size: nil, + attributes: [ + .FileName(fileName: "video.mp4"), + .Video(duration: finalDuration, size: PixelDimensions(finalDimensions), flags: [.instantRoundVideo], preloadSize: nil, coverTime: nil, videoCodec: nil) + ], + alternativeRepresentations: [] + ) + + // Create message + let outMessage: EnqueueMessage = .message( + text: "", + attributes: [], + inlineStickers: [:], + mediaReference: .standalone(media: media), + threadId: nil, + replyToMessageId: nil, + replyToStoryId: nil, + localGroupingKey: nil, + correlationId: nil, + bubbleUpEmojiOrStickersets: [] + ) + + print("[Ghostgram Circle] Sending message...") + + // Send message + let _ = enqueueMessages(account: context.account, peerId: message.id.peerId, messages: [outMessage]).start() + + // Show success + controllerInteraction.displayUndo(.succeed(text: "Кружок отправлен!", timeout: nil, customUndoText: nil)) + } + }, error: { error in + print("[Ghostgram Circle] ERROR in conversion: \(String(describing: error))") + DispatchQueue.main.async { + controllerInteraction.displayUndo(.info(title: "Ошибка", text: "Не удалось конвертировать видео", timeout: nil, customUndoText: nil)) + } + }, completed: { + print("[Ghostgram Circle] Conversion completed callback!") + }) + + // Keep disposable alive - hold reference until task completes + // This is a workaround for ObjC signal disposal + DispatchQueue.main.async { + CircleConversionHolder.shared.add(disposable) + } + } +} + +// Helper class to hold conversion disposables +private final class CircleConversionHolder { + static let shared = CircleConversionHolder() + private var disposables: [AnyObject] = [] + private let lock = NSLock() + + func add(_ disposable: AnyObject?) { + guard let disposable = disposable else { return } + lock.lock() + disposables.append(disposable) + lock.unlock() + + // Auto-cleanup after 2 minutes + DispatchQueue.main.asyncAfter(deadline: .now() + 120) { [weak self] in + self?.lock.lock() + self?.disposables.removeAll { $0 === disposable } + self?.lock.unlock() + } + } +} diff --git a/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift b/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift index 2c827245..ac69da09 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceTitlePanelNodes.swift @@ -6,6 +6,7 @@ import ChatPresentationInterfaceState import ChatControllerInteraction import ComponentFlow import ChatSideTopicsPanel +import LegacyChatHeaderPanelComponent func titlePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, currentPanel: ChatTitleAccessoryPanelNode?, controllerInteraction: ChatControllerInteraction?, interfaceInteraction: ChatPanelInterfaceInteraction?, force: Bool) -> ChatTitleAccessoryPanelNode? { if !force, case .standard(.embedded) = chatPresentationInterfaceState.mode { diff --git a/submodules/TelegramUI/Sources/ChatInviteRequestsTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatInviteRequestsTitlePanelNode.swift index 1fb6330e..b80d3cdb 100644 --- a/submodules/TelegramUI/Sources/ChatInviteRequestsTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatInviteRequestsTitlePanelNode.swift @@ -11,6 +11,7 @@ import TelegramNotices import AnimatedAvatarSetNode import AccountContext import ChatPresentationInterfaceState +import LegacyChatHeaderPanelComponent private final class ChatInfoTitlePanelPeerNearbyInfoNode: ASDisplayNode { private var theme: PresentationTheme? diff --git a/submodules/TelegramUI/Sources/ChatManagingBotTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatManagingBotTitlePanelNode.swift index cfcdc984..79e53b0a 100644 --- a/submodules/TelegramUI/Sources/ChatManagingBotTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatManagingBotTitlePanelNode.swift @@ -14,6 +14,7 @@ import TelegramCore import BundleIconComponent import ContextUI import SwiftSignalKit +import LegacyChatHeaderPanelComponent private final class ChatManagingBotTitlePanelComponent: Component { let context: AccountContext diff --git a/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift b/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift index b3700c97..6f493bf6 100644 --- a/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift @@ -933,7 +933,7 @@ public final class ChatMessageTransitionNodeImpl: ASDisplayNode, ChatMessageTran } if itemNode == nil || itemNode === currentItemNode { if let contextController = self.contextController { - contextController.addRelativeContentOffset(CGPoint(x: 0.0, y: offset), transition: transition) + contextController.addRelativeContentOffset(CGPoint(x: 0.0, y: -offset), transition: transition) } if let standaloneReactionAnimation = self.standaloneReactionAnimation { standaloneReactionAnimation.addRelativeContentOffset(CGPoint(x: 0.0, y: -offset), transition: transition) diff --git a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift index bc7e3b87..5f5f9f17 100644 --- a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift @@ -23,6 +23,7 @@ import AnimationCache import MultiAnimationRenderer import TranslateUI import ChatControllerInteraction +import LegacyChatHeaderPanelComponent private enum PinnedMessageAnimation { case slideToTop @@ -67,8 +68,6 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { private let imageNode: TransformImageNode private let imageNodeContainer: ASDisplayNode - private let separatorNode: ASDisplayNode - private var currentLayout: (CGFloat, CGFloat, CGFloat)? private var currentMessage: ChatPinnedMessage? private var previousMediaReference: AnyMediaReference? @@ -132,9 +131,6 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { self.activityIndicator.alpha = 0.0 ContainedViewLayoutTransition.immediate.updateSublayerTransformScale(node: self.activityIndicatorContainer, scale: 0.1) - self.separatorNode = ASDisplayNode() - self.separatorNode.isLayerBacked = true - self.contextContainer = ContextControllerSourceNode() self.clippingContainer = ASDisplayNode() @@ -219,8 +215,6 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { self.tapButton.addTarget(self, action: #selector(self.tapped), forControlEvents: [.touchUpInside]) self.contextContainer.addSubnode(self.tapButton) - self.addSubnode(self.separatorNode) - self.contextContainer.activated = { [weak self] gesture, _ in guard let strongSelf = self else { return @@ -249,9 +243,19 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { if self.theme !== interfaceState.theme { themeUpdated = true self.theme = interfaceState.theme - self.closeButton.setImage(PresentationResourcesChat.chatInputPanelCloseIconImage(interfaceState.theme), for: []) - self.listButton.setImage(PresentationResourcesChat.chatInputPanelPinnedListIconImage(interfaceState.theme), for: []) - self.separatorNode.backgroundColor = interfaceState.theme.rootController.navigationBar.separatorColor + self.closeButton.setImage(generateImage(CGSize(width: 12.0, height: 12.0), contextGenerator: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.setStrokeColor(interfaceState.theme.chat.inputPanel.panelControlColor.cgColor) + context.setLineWidth(1.33) + context.setLineCap(.round) + context.move(to: CGPoint(x: 1.0, y: 1.0)) + context.addLine(to: CGPoint(x: size.width - 1.0, y: size.height - 1.0)) + context.strokePath() + context.move(to: CGPoint(x: size.width - 1.0, y: 1.0)) + context.addLine(to: CGPoint(x: 1.0, y: size.height - 1.0)) + context.strokePath() + }), for: []) + self.listButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/PinnedList"), color: interfaceState.theme.chat.inputPanel.panelControlColor), for: []) self.actionButtonBackgroundNode.image = generateStretchableFilledCircleImage(diameter: 14.0 * 2.0, color: interfaceState.theme.list.itemCheckColors.fillColor, strokeColor: nil, strokeWidth: nil, backgroundColor: nil) } @@ -473,7 +477,6 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { transition.updateFrame(node: self.activityIndicatorContainer, frame: CGRect(origin: CGPoint(x: width - rightInset - indicatorSize.width + 5.0, y: 15.0), size: indicatorSize)) transition.updateFrame(node: self.activityIndicator, frame: CGRect(origin: CGPoint(), size: indicatorSize)) - transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: UIScreenPixel))) self.tapButton.frame = CGRect(origin: CGPoint(), size: CGSize(width: width - tapButtonRightInset, height: panelHeight)) self.clippingContainer.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: panelHeight)) @@ -534,7 +537,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { transition.updateSublayerTransformScale(node: self.buttonsContainer, scale: 0.1) if let theme = self.theme { - self.activityIndicator.transitionToState(.progress(color: theme.chat.inputPanel.panelControlAccentColor, lineWidth: nil, value: nil, cancelEnabled: false, animateRotation: true), animated: false, completion: { + self.activityIndicator.transitionToState(.progress(color: theme.chat.inputPanel.panelControlColor, lineWidth: nil, value: nil, cancelEnabled: false, animateRotation: true), animated: false, completion: { }) } } @@ -609,20 +612,20 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { var titleStrings: [AnimatedCountLabelNode.Segment] = [] if let _ = giveaway { - titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedGiveaway) ", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor))) + titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedGiveaway) ", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlColor))) } else { if pinnedMessage.totalCount == 2 { if pinnedMessage.index == 0 { - titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedPreviousMessage) ", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor))) + titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedPreviousMessage) ", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlColor))) } else { - titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedMessage) ", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor))) + titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedMessage) ", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlColor))) } } else if pinnedMessage.totalCount > 1 && pinnedMessage.index != pinnedMessage.totalCount - 1 { - titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedMessage)", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor))) - titleStrings.append(.text(1, NSAttributedString(string: " #", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor))) - titleStrings.append(.number(pinnedMessage.index + 1, NSAttributedString(string: "\(pinnedMessage.index + 1)", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor))) + titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedMessage)", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlColor))) + titleStrings.append(.text(1, NSAttributedString(string: " #", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlColor))) + titleStrings.append(.number(pinnedMessage.index + 1, NSAttributedString(string: "\(pinnedMessage.index + 1)", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlColor))) } else { - titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedMessage) ", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor))) + titleStrings.append(.text(0, NSAttributedString(string: "\(strings.Conversation_PinnedMessage) ", font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlColor))) } } @@ -675,14 +678,14 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { } else { titleString = "" } - titleStrings = [.text(0, NSAttributedString(string: titleString, font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor))] + titleStrings = [.text(0, NSAttributedString(string: titleString, font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlColor))] } else { for media in message.media { if let media = media as? TelegramMediaInvoice { - titleStrings = [.text(0, NSAttributedString(string: media.title, font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor))] + titleStrings = [.text(0, NSAttributedString(string: media.title, font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlColor))] break } else if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content, content.type == "telegram_call" { - titleStrings = [.text(0, NSAttributedString(string: strings.Chat_PinnedGroupCallTitle, font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor))] + titleStrings = [.text(0, NSAttributedString(string: strings.Chat_PinnedGroupCallTitle, font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlColor))] break } } @@ -810,7 +813,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { animationTransition.updateFrameAdditive(node: strongSelf.contentTextContainer, frame: CGRect(origin: CGPoint(x: contentLeftInset + textLineInset, y: 0.0), size: CGSize(width: width, height: panelHeight))) - strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 5.0), size: titleLayout.size) + strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 6.0), size: titleLayout.size) let textFrame = CGRect(origin: CGPoint(x: 0.0, y: 23.0), size: textLayout.size) strongSelf.textNode.textNode.frame = textFrame @@ -857,8 +860,8 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { animationTransition.updateFrame(node: strongSelf.lineNode, frame: lineFrame) strongSelf.lineNode.update( colors: AnimatedNavigationStripeNode.Colors( - foreground: theme.chat.inputPanel.panelControlAccentColor, - background: theme.chat.inputPanel.panelControlAccentColor.withAlphaComponent(0.5), + foreground: theme.chat.inputPanel.panelControlColor, + background: theme.chat.inputPanel.panelControlColor.withAlphaComponent(0.5), clearBackground: theme.chat.inputPanel.panelBackgroundColor ), configuration: AnimatedNavigationStripeNode.Configuration( @@ -869,7 +872,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { transition: animationTransition ) - strongSelf.imageNodeContainer.frame = CGRect(origin: CGPoint(x: contentLeftInset + 9.0, y: 7.0), size: CGSize(width: 35.0, height: 35.0)) + strongSelf.imageNodeContainer.frame = CGRect(origin: CGPoint(x: contentLeftInset + 9.0, y: 8.0), size: CGSize(width: 35.0, height: 35.0)) strongSelf.imageNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 35.0, height: 35.0)) if let applyImage = applyImage { diff --git a/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift index f08548e6..0697955c 100644 --- a/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift @@ -16,6 +16,7 @@ import AnimationCache import MultiAnimationRenderer import AccountContext import PremiumUI +import LegacyChatHeaderPanelComponent private enum ChatReportPeerTitleButton: Equatable { case block diff --git a/submodules/TelegramUI/Sources/ChatRequestInProgressTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatRequestInProgressTitlePanelNode.swift index 9148b240..7b7e4981 100644 --- a/submodules/TelegramUI/Sources/ChatRequestInProgressTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatRequestInProgressTitlePanelNode.swift @@ -4,6 +4,7 @@ import Display import AsyncDisplayKit import TelegramPresentationData import ChatPresentationInterfaceState +import LegacyChatHeaderPanelComponent final class ChatRequestInProgressTitlePanelNode: ChatTitleAccessoryPanelNode { private let separatorNode: ASDisplayNode diff --git a/submodules/TelegramUI/Sources/ChatRestrictedInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatRestrictedInputPanelNode.swift index d889eeaa..a13a8cb7 100644 --- a/submodules/TelegramUI/Sources/ChatRestrictedInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatRestrictedInputPanelNode.swift @@ -157,7 +157,7 @@ final class ChatRestrictedInputPanelNode: ChatInputPanelNode { self.tintSubtitleNode.attributedText = NSAttributedString(string: self.subtitleNode.attributedText?.string ?? "", font: Font.regular(13.0), textColor: .black) let panelHeight = defaultHeight(metrics: metrics) - let textSize = self.textNode.updateLayout(CGSize(width: width - leftInset - rightInset - 8.0 * 2.0, height: panelHeight)) + let textSize = self.textNode.updateLayout(CGSize(width: width - leftInset - rightInset - 16.0 * 2.0, height: panelHeight)) let subtitleSize = self.subtitleNode.updateLayout(CGSize(width: width - leftInset - rightInset - 8.0 * 2.0, height: panelHeight)) var originX: CGFloat = leftInset + floor((width - leftInset - rightInset - textSize.width) / 2.0) @@ -196,7 +196,8 @@ final class ChatRestrictedInputPanelNode: ChatInputPanelNode { combinedFrame = combinedFrame.union(iconView.frame) } combinedFrame = combinedFrame.insetBy(dx: -12.0, dy: -6.0) - combinedFrame.origin.y += 1.0 + combinedFrame.size.height = 40.0 + combinedFrame.origin.y = floorToScreenPixels((panelHeight - 40.0) * 0.5) self.textNode.frame = textFrame.offsetBy(dx: -combinedFrame.minX, dy: -combinedFrame.minY) self.tintTextNode.frame = self.textNode.frame diff --git a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift index 26b8afe9..b0148af5 100644 --- a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift +++ b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift @@ -291,7 +291,6 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, ASScrollViewDe }, hideChatFolderUpdates: { }, openStories: { _, _ in }, openStarsTopup: { _ in - }, dismissNotice: { _ in }, editPeer: { _ in }, openWebApp: { _ in }, openPhotoSetup: { diff --git a/submodules/TelegramUI/Sources/ChatSearchResultsController.swift b/submodules/TelegramUI/Sources/ChatSearchResultsController.swift index 4e35b65c..c686bf1d 100644 --- a/submodules/TelegramUI/Sources/ChatSearchResultsController.swift +++ b/submodules/TelegramUI/Sources/ChatSearchResultsController.swift @@ -43,7 +43,7 @@ final class ChatSearchResultsController: ViewController { |> deliverOnMainQueue).startStrict(next: { [weak self] presentationData in if let strongSelf = self { strongSelf.presentationData = presentationData - strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationTheme: presentationData.theme, presentationStrings: presentationData.strings)) + strongSelf.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationTheme: presentationData.theme, presentationStrings: presentationData.strings), transition: .immediate) strongSelf.controllerNode.updatePresentationData(presentationData) } }) diff --git a/submodules/TelegramUI/Sources/ChatSearchTitleAccessoryPanelNode.swift b/submodules/TelegramUI/Sources/ChatSearchTitleAccessoryPanelNode.swift index f4507ed7..5653241c 100644 --- a/submodules/TelegramUI/Sources/ChatSearchTitleAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatSearchTitleAccessoryPanelNode.swift @@ -17,6 +17,7 @@ import ContextUI import PromptUI import BundleIconComponent import SavedTagNameAlertController +import LegacyChatHeaderPanelComponent private let backgroundTagImage: UIImage? = { if let image = UIImage(bundleImageName: "Chat/Title Panels/SearchTagTab") { @@ -557,7 +558,7 @@ final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, Chat self.update(params: params, transition: transition) } - let panelHeight: CGFloat = 39.0 + let panelHeight: CGFloat = 40.0 return LayoutResult(backgroundHeight: panelHeight, insetHeight: panelHeight, hitTestSlop: 0.0) } @@ -567,7 +568,7 @@ final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, Chat } private func update(params: Params, transition: ContainedViewLayoutTransition) { - let panelHeight: CGFloat = 39.0 + let panelHeight: CGFloat = 40.0 let containerInsets = UIEdgeInsets(top: 0.0, left: params.leftInset + 16.0, bottom: 0.0, right: params.rightInset + 16.0) let itemSpacing: CGFloat = 24.0 @@ -598,7 +599,7 @@ final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, Chat } let itemSize = promoView.update(theme: params.interfaceState.theme, strings: params.interfaceState.strings, height: panelHeight, isUnlock: !self.items.isEmpty, transition: .immediate) - let itemFrame = CGRect(origin: CGPoint(x: contentSize.width, y: -5.0), size: itemSize) + let itemFrame = CGRect(origin: CGPoint(x: contentSize.width, y: 0.0), size: itemSize) itemTransition.updatePosition(layer: promoView.layer, position: itemFrame.center) promoView.bounds = CGRect(origin: CGPoint(), size: itemFrame.size) @@ -704,7 +705,7 @@ final class ChatSearchTitleAccessoryPanelNode: ChatTitleAccessoryPanelNode, Chat } } let itemSize = itemView.update(item: item, isSelected: isSelected, isLocked: !params.interfaceState.isPremium, theme: params.interfaceState.theme, height: panelHeight, transition: .immediate) - let itemFrame = CGRect(origin: CGPoint(x: contentSize.width, y: -5.0), size: itemSize) + let itemFrame = CGRect(origin: CGPoint(x: contentSize.width, y: 0.0), size: itemSize) itemTransition.updatePosition(layer: itemView.layer, position: itemFrame.center) itemTransition.updateBounds(layer: itemView.layer, bounds: CGRect(origin: CGPoint(), size: itemFrame.size)) diff --git a/submodules/TelegramUI/Sources/ChatTagSearchInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTagSearchInputPanelNode.swift index 952ecd5d..e240d853 100644 --- a/submodules/TelegramUI/Sources/ChatTagSearchInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTagSearchInputPanelNode.swift @@ -59,6 +59,7 @@ final class ChatTagSearchInputPanelNode: ChatInputPanelNode { } } + private let backgroundContainerView: GlassBackgroundContainerView private let leftControlsBackgroundView: GlassBackgroundView private let rightControlsBackgroundView: GlassBackgroundView private let calendarButton = ComponentView() @@ -93,13 +94,15 @@ final class ChatTagSearchInputPanelNode: ChatInputPanelNode { init(theme: PresentationTheme, alwaysShowTotalMessagesCount: Bool) { self.alwaysShowTotalMessagesCount = alwaysShowTotalMessagesCount + self.backgroundContainerView = GlassBackgroundContainerView() self.leftControlsBackgroundView = GlassBackgroundView() self.rightControlsBackgroundView = GlassBackgroundView() super.init() - self.view.addSubview(self.leftControlsBackgroundView) - self.view.addSubview(self.rightControlsBackgroundView) + self.view.addSubview(self.backgroundContainerView) + self.backgroundContainerView.contentView.addSubview(self.leftControlsBackgroundView) + self.backgroundContainerView.contentView.addSubview(self.rightControlsBackgroundView) } deinit { @@ -108,6 +111,14 @@ final class ChatTagSearchInputPanelNode: ChatInputPanelNode { } override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, maxOverlayHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, isMediaInputExpanded: Bool) -> CGFloat { + var leftInset = leftInset + 8.0 + var rightInset = rightInset + 8.0 + + if bottomInset <= 32.0 { + leftInset += 18.0 + rightInset += 18.0 + } + let params = Params(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, maxOverlayHeight: maxOverlayHeight, isSecondary: isSecondary, interfaceState: interfaceState, metrics: metrics, isMediaInputExpanded: isMediaInputExpanded) if let currentLayout = self.currentLayout, currentLayout.params == params { return currentLayout.height @@ -332,7 +343,7 @@ final class ChatTagSearchInputPanelNode: ChatInputPanelNode { buttonView.alpha = 0.0 self.view.addSubview(buttonView) } - let listModeFrame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 20.0 - 8.0 - buttonSize.width, y: floor((size.height - buttonSize.height) * 0.5)), size: buttonSize) + let listModeFrame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 8.0 - buttonSize.width, y: floor((size.height - buttonSize.height) * 0.5)), size: buttonSize) listModeButtonFrameValue = listModeFrame listModeButtonTransition.setPosition(view: buttonView, position: CGPoint(x: listModeFrame.minX + listModeFrame.width * buttonView.layer.anchorPoint.x, y: listModeFrame.minY + listModeFrame.height * buttonView.layer.anchorPoint.y)) listModeButtonTransition.setBounds(view: buttonView, bounds: CGRect(origin: CGPoint(), size: listModeFrame.size)) @@ -349,7 +360,7 @@ final class ChatTagSearchInputPanelNode: ChatInputPanelNode { } } - var nextLeftX: CGFloat = 16.0 + 8.0 + var nextLeftX: CGFloat = params.leftInset + 4.0 var calendarButtonFrameValue: CGRect? var membersButtonFrameValue: CGRect? @@ -547,7 +558,7 @@ final class ChatTagSearchInputPanelNode: ChatInputPanelNode { } } - var leftControlsBackgroundFrame = CGRect(origin: CGPoint(x: 20.0, y: floor((height - 40.0) * 0.5)), size: CGSize(width: 0.0, height: 40.0)) + var leftControlsBackgroundFrame = CGRect(origin: CGPoint(x: params.leftInset, y: floor((height - 40.0) * 0.5)), size: CGSize(width: 0.0, height: 40.0)) leftControlsBackgroundFrame.size.width = max(40.0, leftControlsRect.maxX - leftControlsBackgroundFrame.minX) transition.setFrame(view: self.leftControlsBackgroundView, frame: leftControlsBackgroundFrame) self.leftControlsBackgroundView.update(size: leftControlsBackgroundFrame.size, cornerRadius: leftControlsBackgroundFrame.height * 0.5, isDark: params.interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: params.interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), transition: transition) @@ -568,12 +579,15 @@ final class ChatTagSearchInputPanelNode: ChatInputPanelNode { } } - var rightControlsBackgroundFrame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 20.0, y: floor((height - 40.0) * 0.5)), size: CGSize(width: 0.0, height: 40.0)) + var rightControlsBackgroundFrame = CGRect(origin: CGPoint(x: params.width - params.rightInset, y: floor((height - 40.0) * 0.5)), size: CGSize(width: 0.0, height: 40.0)) rightControlsBackgroundFrame.size.width = max(40.0, rightControlsRect.maxX - rightControlsRect.minX + 8.0 * 2.0) rightControlsBackgroundFrame.origin.x -= rightControlsBackgroundFrame.width transition.setFrame(view: self.rightControlsBackgroundView, frame: rightControlsBackgroundFrame) self.rightControlsBackgroundView.update(size: rightControlsBackgroundFrame.size, cornerRadius: rightControlsBackgroundFrame.height * 0.5, isDark: params.interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: params.interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7)), transition: transition) - transition.setAlpha(view: self.rightControlsBackgroundView, alpha: rightControlsRect.isEmpty ? 0.0 : 1.0) + self.rightControlsBackgroundView.isHidden = rightControlsRect.isEmpty + + transition.setFrame(view: self.backgroundContainerView, frame: CGRect(origin: CGPoint(), size: CGSize(width: params.width, height: height))) + self.backgroundContainerView.update(size: CGSize(width: params.width, height: height), isDark: params.interfaceState.theme.overallDarkAppearance, transition: transition) return height } diff --git a/submodules/TelegramUI/Sources/ChatToastAlertPanelNode.swift b/submodules/TelegramUI/Sources/ChatToastAlertPanelNode.swift index 0ab0c145..3beeadf2 100644 --- a/submodules/TelegramUI/Sources/ChatToastAlertPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatToastAlertPanelNode.swift @@ -3,6 +3,7 @@ import UIKit import Display import AsyncDisplayKit import ChatPresentationInterfaceState +import LegacyChatHeaderPanelComponent final class ChatToastAlertPanelNode: ChatTitleAccessoryPanelNode { private let separatorNode: ASDisplayNode diff --git a/submodules/TelegramUI/Sources/ChatVerifiedPeerTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatVerifiedPeerTitlePanelNode.swift index 80ec6665..5cd2fb70 100644 --- a/submodules/TelegramUI/Sources/ChatVerifiedPeerTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatVerifiedPeerTitlePanelNode.swift @@ -15,6 +15,7 @@ import AnimationCache import MultiAnimationRenderer import AccountContext import TelegramNotices +import LegacyChatHeaderPanelComponent final class ChatVerifiedPeerTitlePanelNode: ChatTitleAccessoryPanelNode { private let context: AccountContext diff --git a/submodules/TelegramUI/Sources/CommandChatInputContextPanelNode.swift b/submodules/TelegramUI/Sources/CommandChatInputContextPanelNode.swift index 457fd8a8..144f6008 100644 --- a/submodules/TelegramUI/Sources/CommandChatInputContextPanelNode.swift +++ b/submodules/TelegramUI/Sources/CommandChatInputContextPanelNode.swift @@ -175,8 +175,6 @@ private struct CommandChatInputContextPanelEntry: Comparable, Identifiable { }, openStarsTopup: { _ in }, - dismissNotice: { _ in - }, editPeer: { _ in }, openWebApp: { _ in diff --git a/submodules/TelegramUI/Sources/CommandChatInputPanelItem.swift b/submodules/TelegramUI/Sources/CommandChatInputPanelItem.swift index 3b7fd427..8d27ec66 100644 --- a/submodules/TelegramUI/Sources/CommandChatInputPanelItem.swift +++ b/submodules/TelegramUI/Sources/CommandChatInputPanelItem.swift @@ -107,7 +107,7 @@ final class CommandChatInputPanelItemNode: ListViewItemNode { self.activateAreaNode = AccessibilityAreaNode() self.activateAreaNode.accessibilityTraits = [.button] - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.separatorNode) diff --git a/submodules/TelegramUI/Sources/CommandMenuChatInputPanelItem.swift b/submodules/TelegramUI/Sources/CommandMenuChatInputPanelItem.swift index 48db2d54..e27f05e5 100644 --- a/submodules/TelegramUI/Sources/CommandMenuChatInputPanelItem.swift +++ b/submodules/TelegramUI/Sources/CommandMenuChatInputPanelItem.swift @@ -144,7 +144,7 @@ final class CommandMenuChatInputPanelItemNode: ListViewItemNode { self.activateAreaNode = AccessibilityAreaNode() self.activateAreaNode.accessibilityTraits = [.button] - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.clippingNode) self.clippingNode.addSubnode(self.shadowNode) diff --git a/submodules/TelegramUI/Sources/ComposeController.swift b/submodules/TelegramUI/Sources/ComposeController.swift index f4881a50..d4825816 100644 --- a/submodules/TelegramUI/Sources/ComposeController.swift +++ b/submodules/TelegramUI/Sources/ComposeController.swift @@ -41,8 +41,9 @@ public class ComposeControllerImpl: ViewController, ComposeController { self.presentationData = context.sharedContext.currentPresentationData.with { $0 } - super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData)) + super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData, style: .glass)) + self._hasGlassStyle = true self.navigationPresentation = .modal self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style @@ -51,8 +52,6 @@ public class ComposeControllerImpl: ViewController, ComposeController { self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) - self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(cancelPressed)) - self.scrollToTop = { [weak self] in if let strongSelf = self { if let searchContentNode = strongSelf.searchContentNode { @@ -93,7 +92,7 @@ public class ComposeControllerImpl: ViewController, ComposeController { private func updateThemeAndStrings() { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData, style: .glass), transition: .immediate) self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search) self.title = self.presentationData.strings.Compose_NewMessage self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) diff --git a/submodules/TelegramUI/Sources/ComposeControllerNode.swift b/submodules/TelegramUI/Sources/ComposeControllerNode.swift index 20ec24de..574b4975 100644 --- a/submodules/TelegramUI/Sources/ComposeControllerNode.swift +++ b/submodules/TelegramUI/Sources/ComposeControllerNode.swift @@ -131,7 +131,7 @@ final class ComposeControllerNode: ASDisplayNode { self.requestOpenDisabledPeerFromSearch?(peer, reason) }, contextAction: nil), cancel: { [weak self] in self?.requestDeactivateSearch?() - }) + }, fieldStyle: placeholderNode.fieldStyle) self.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: navigationBarHeight, transition: .immediate) self.searchDisplayController?.activate(insertSubnode: { [weak self, weak placeholderNode] subnode, isSearchBar in diff --git a/submodules/TelegramUI/Sources/ContactMultiselectionController.swift b/submodules/TelegramUI/Sources/ContactMultiselectionController.swift index 043b0977..7dbf6599 100644 --- a/submodules/TelegramUI/Sources/ContactMultiselectionController.swift +++ b/submodules/TelegramUI/Sources/ContactMultiselectionController.swift @@ -109,7 +109,8 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection self.titleView = CounterControllerTitleView(theme: self.presentationData.theme) - super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData)) + super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData, style: .glass)) + self._hasGlassStyle = true self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style @@ -226,7 +227,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection private func updateThemeAndStrings() { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData, style: .glass), transition: .immediate) self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) self.updateTitle() self.contactsNode.updatePresentationData(self.presentationData) diff --git a/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift b/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift index 3a2d3bc9..08c8d27e 100644 --- a/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift @@ -277,7 +277,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode { } } - self.tokenListNode = EditableTokenListNode(context: self.context, presentationTheme: self.presentationData.theme, theme: EditableTokenListNodeTheme(backgroundColor: .clear, separatorColor: self.presentationData.theme.rootController.navigationBar.separatorColor, placeholderTextColor: self.presentationData.theme.list.itemPlaceholderTextColor, primaryTextColor: self.presentationData.theme.list.itemPrimaryTextColor, tokenBackgroundColor: self.presentationData.theme.list.itemCheckColors.strokeColor.withAlphaComponent(0.25), selectedTextColor: self.presentationData.theme.list.itemCheckColors.foregroundColor, selectedBackgroundColor: self.presentationData.theme.list.itemCheckColors.fillColor, accentColor: self.presentationData.theme.list.itemAccentColor, keyboardColor: self.presentationData.theme.rootController.keyboardColor), placeholder: placeholder, shortPlaceholder: shortPlaceholder) + self.tokenListNode = EditableTokenListNode(context: self.context, theme: self.presentationData.theme, placeholder: placeholder, shortPlaceholder: shortPlaceholder) super.init() @@ -413,6 +413,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode { var insets = layout.insets(options: [.input]) insets.top += navigationBarHeight insets.top += strongSelf.tokenListNode.bounds.size.height + insets.top += 10.0 + 10.0 var headerInsets = layout.insets(options: [.input]) headerInsets.top += actualNavigationBarHeight @@ -465,16 +466,17 @@ final class ContactMultiselectionControllerNode: ASDisplayNode { var insets = layout.insets(options: [.input]) insets.top += navigationBarHeight + insets.top += 10.0 - let tokenListHeight = self.tokenListNode.updateLayout(tokens: self.editableTokens, width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: transition) + let tokenListHeight = self.tokenListNode.updateLayout(tokens: self.editableTokens, width: layout.size.width - (16.0 + layout.safeInsets.left) * 2.0, leftInset: 0.0, rightInset: 0.0, transition: transition) - transition.updateFrame(node: self.tokenListNode, frame: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: tokenListHeight))) + transition.updateFrame(node: self.tokenListNode, frame: CGRect(origin: CGPoint(x: 16.0 + layout.safeInsets.left, y: insets.top), size: CGSize(width: layout.size.width - (16.0 + layout.safeInsets.left) * 2.0, height: tokenListHeight))) var headerInsets = layout.insets(options: [.input]) headerInsets.top += actualNavigationBarHeight - insets.top += tokenListHeight - headerInsets.top += tokenListHeight + headerInsets.top += 10.0 + tokenListHeight + 8.0 + insets = headerInsets if let footerPanelNode = self.footerPanelNode { var count = 0 diff --git a/submodules/TelegramUI/Sources/ContactSelectionController.swift b/submodules/TelegramUI/Sources/ContactSelectionController.swift index 5af60510..22a2c985 100644 --- a/submodules/TelegramUI/Sources/ContactSelectionController.swift +++ b/submodules/TelegramUI/Sources/ContactSelectionController.swift @@ -133,7 +133,9 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController self.presentationData = self.presentationData.withUpdated(theme: self.presentationData.theme.withModalBlocksBackground()) } - super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: self.presentationData.theme, hideBackground: glass, hideSeparator: glass), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings))) + super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: self.presentationData.theme, hideBackground: glass, hideSeparator: glass, style: .glass), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings))) + + self._hasGlassStyle = true self.blocksBackgroundWhenInOverlay = true self.acceptsFocusWhenInOverlay = true @@ -236,18 +238,19 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController guard case .glass = self.style else { return } - let barButtonSize = CGSize(width: 40.0, height: 40.0) + let barButtonSize = CGSize(width: 44.0, height: 44.0) let closeComponent: AnyComponentWithIdentity = AnyComponentWithIdentity( id: "close", component: AnyComponent(GlassBarButtonComponent( size: barButtonSize, - backgroundColor: self.presentationData.theme.rootController.navigationBar.glassBarButtonBackgroundColor, + backgroundColor: nil, isDark: self.presentationData.theme.overallDarkAppearance, state: .generic, + animateScale: false, component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: self.presentationData.theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: self.presentationData.theme.chat.inputPanel.panelControlColor ) )), action: { [weak self] _ in @@ -262,13 +265,14 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController id: "search", component: AnyComponent(GlassBarButtonComponent( size: barButtonSize, - backgroundColor: self.presentationData.theme.rootController.navigationBar.glassBarButtonBackgroundColor, + backgroundColor: nil, isDark: self.presentationData.theme.overallDarkAppearance, state: .generic, + animateScale: false, component: AnyComponentWithIdentity(id: "search", component: AnyComponent( BundleIconComponent( name: "Navigation/Search", - tintColor: self.presentationData.theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: self.presentationData.theme.chat.inputPanel.panelControlColor ) )), action: { [weak self] _ in @@ -289,15 +293,19 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController self.closeButtonNode = closeButtonNode self.navigationItem.leftBarButtonItem = UIBarButtonItem(customDisplayNode: closeButtonNode) } - - let searchButtonNode: BarComponentHostNode - if let current = self.searchButtonNode { - searchButtonNode = current - searchButtonNode.component = searchComponent + + if searchComponent != nil { + let searchButtonNode: BarComponentHostNode + if let current = self.searchButtonNode { + searchButtonNode = current + searchButtonNode.component = searchComponent + } else { + searchButtonNode = BarComponentHostNode(component: searchComponent, size: barButtonSize) + self.searchButtonNode = searchButtonNode + self.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: searchButtonNode) + } } else { - searchButtonNode = BarComponentHostNode(component: searchComponent, size: barButtonSize) - self.searchButtonNode = searchButtonNode - self.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: searchButtonNode) + self.navigationItem.rightBarButtonItem = nil } } @@ -307,7 +315,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController if case .glass = self.style { glass = true } - self.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: self.presentationData.theme, hideBackground: glass, hideSeparator: glass), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings))) + self.navigationBar?.updatePresentationData(NavigationBarPresentationData(theme: NavigationBarTheme(rootControllerTheme: self.presentationData.theme, hideBackground: glass, hideSeparator: glass, style: .glass), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)), transition: .immediate) (self.searchContentNode as? NavigationBarSearchContentNode)?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search) self.title = self.titleProducer(self.presentationData.strings) self.tabBarItem.title = self.presentationData.strings.Contacts_Title @@ -571,7 +579,7 @@ final class ContactsSearchNavigationContentNode: NavigationBarContentNode { init(presentationData: PresentationData, dismissSearch: @escaping () -> Void, updateSearchQuery: @escaping (String) -> Void) { self.presentationData = presentationData - self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: presentationData.theme, hasSeparator: false), strings: presentationData.strings, fieldStyle: .modern) + self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: presentationData.theme, hasSeparator: false), presentationTheme: presentationData.theme, strings: presentationData.strings, fieldStyle: .modern) self.searchBar.placeholderString = NSAttributedString(string: presentationData.strings.Common_Search, font: searchBarFont, textColor: presentationData.theme.rootController.navigationSearchBar.inputPlaceholderTextColor) super.init() @@ -591,10 +599,12 @@ final class ContactsSearchNavigationContentNode: NavigationBarContentNode { return 56.0 } - override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { + override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { let searchBarFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - self.nominalHeight), size: CGSize(width: size.width, height: 56.0)) self.searchBar.frame = searchBarFrame self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition) + + return size } func activate() { @@ -615,7 +625,7 @@ final class ContactsSearchNavigationContentNode: NavigationBarContentNode { func updatePresentationData(_ presentationData: PresentationData) { self.presentationData = presentationData - self.searchBar.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: presentationData.theme, hasSeparator: false), strings: presentationData.strings) + self.searchBar.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: presentationData.theme, hasSeparator: false), presentationTheme: presentationData.theme, strings: presentationData.strings) } } diff --git a/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift b/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift index e15dafce..0e32c4d1 100644 --- a/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift @@ -477,7 +477,7 @@ final class ContactSelectionControllerNode: ASDisplayNode { if let requestDeactivateSearch = self?.requestDeactivateSearch { requestDeactivateSearch() } - }) + }, fieldStyle: placeholderNode.fieldStyle) self.searchDisplayController?.containerLayoutUpdated(containerLayout, navigationBarHeight: navigationBarHeight, transition: .immediate) self.searchDisplayController?.activate(insertSubnode: { [weak self, weak placeholderNode] subnode, isSearchBar in diff --git a/submodules/TelegramUI/Sources/CreateChannelController.swift b/submodules/TelegramUI/Sources/CreateChannelController.swift index c537e8b8..b8550916 100644 --- a/submodules/TelegramUI/Sources/CreateChannelController.swift +++ b/submodules/TelegramUI/Sources/CreateChannelController.swift @@ -194,7 +194,7 @@ private enum CreateChannelEntry: ItemListNodeEntry { let arguments = arguments as! CreateChannelArguments switch self { case let .channelInfo(_, _, dateTimeFormat, peer, state, avatar): - return ItemListAvatarAndNameInfoItem(itemContext: .accountContext(arguments.context), presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .editSettings, peer: peer.flatMap(EnginePeer.init), presence: nil, memberCount: nil, state: state, sectionId: ItemListSectionId(self.section), style: .blocks(withTopInset: false, withExtendedBottomInset: false), editingNameUpdated: { editingName in + return ItemListAvatarAndNameInfoItem(itemContext: .accountContext(arguments.context), presentationData: presentationData, systemStyle: .glass, dateTimeFormat: dateTimeFormat, mode: .editSettings, peer: peer.flatMap(EnginePeer.init), presence: nil, memberCount: nil, state: state, sectionId: ItemListSectionId(self.section), style: .blocks(withTopInset: false, withExtendedBottomInset: false), editingNameUpdated: { editingName in arguments.updateEditingName(editingName) }, editingNameCompleted: { arguments.focusOnDescription() @@ -202,11 +202,11 @@ private enum CreateChannelEntry: ItemListNodeEntry { arguments.changeProfilePhoto() }, updatingImage: avatar, tag: CreateChannelEntryTag.info) case let .setProfilePhoto(_, text): - return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: ItemListSectionId(self.section), style: .blocks, action: { + return ItemListActionItem(presentationData: presentationData, systemStyle: .glass, title: text, kind: .generic, alignment: .natural, sectionId: ItemListSectionId(self.section), style: .blocks, action: { arguments.changeProfilePhoto() }) case let .descriptionSetup(_, text, value): - return ItemListMultilineInputItem(presentationData: presentationData, text: value, placeholder: text, maxLength: ItemListMultilineInputItemTextLimit(value: 255, display: true), sectionId: self.section, style: .blocks, textUpdated: { updatedText in + return ItemListMultilineInputItem(presentationData: presentationData, systemStyle: .glass, text: value, placeholder: text, maxLength: ItemListMultilineInputItemTextLimit(value: 255, display: true), sectionId: self.section, style: .blocks, textUpdated: { updatedText in arguments.updateEditingDescriptionText(updatedText) }, tag: CreateChannelEntryTag.description) case let .descriptionInfo(_, text): @@ -214,7 +214,7 @@ private enum CreateChannelEntry: ItemListNodeEntry { case let .usernameHeader(_, title): return ItemListSectionHeaderItem(presentationData: presentationData, text: title, sectionId: self.section) case let .username(theme, placeholder, text): - return ItemListSingleLineInputItem(presentationData: presentationData, title: NSAttributedString(string: "t.me/", textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: placeholder, type: .username, clearType: .always, tag: nil, sectionId: self.section, textUpdated: { updatedText in + return ItemListSingleLineInputItem(presentationData: presentationData, systemStyle: .glass, title: NSAttributedString(string: "t.me/", textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: placeholder, type: .username, clearType: .always, tag: nil, sectionId: self.section, textUpdated: { updatedText in arguments.updatePublicLinkText(updatedText) }, action: { }) diff --git a/submodules/TelegramUI/Sources/CreateGroupController.swift b/submodules/TelegramUI/Sources/CreateGroupController.swift index d68f995c..05d3e8a2 100644 --- a/submodules/TelegramUI/Sources/CreateGroupController.swift +++ b/submodules/TelegramUI/Sources/CreateGroupController.swift @@ -321,7 +321,7 @@ private enum CreateGroupEntry: ItemListNodeEntry { let arguments = arguments as! CreateGroupArguments switch self { case let .groupInfo(_, _, dateTimeFormat, peer, state, avatar): - return ItemListAvatarAndNameInfoItem(itemContext: .accountContext(arguments.context), presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .editSettings, peer: peer.flatMap(EnginePeer.init), presence: nil, memberCount: nil, state: state, sectionId: ItemListSectionId(self.section), style: .blocks(withTopInset: false, withExtendedBottomInset: false), editingNameUpdated: { editingName in + return ItemListAvatarAndNameInfoItem(itemContext: .accountContext(arguments.context), presentationData: presentationData, systemStyle: .glass, dateTimeFormat: dateTimeFormat, mode: .editSettings, peer: peer.flatMap(EnginePeer.init), presence: nil, memberCount: nil, state: state, sectionId: ItemListSectionId(self.section), style: .blocks(withTopInset: false, withExtendedBottomInset: false), editingNameUpdated: { editingName in arguments.updateEditingName(editingName) }, editingNameCompleted: { arguments.done() @@ -329,13 +329,13 @@ private enum CreateGroupEntry: ItemListNodeEntry { arguments.changeProfilePhoto() }, updatingImage: avatar, tag: CreateGroupEntryTag.info) case let .setProfilePhoto(_, text): - return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: ItemListSectionId(self.section), style: .blocks, action: { + return ItemListActionItem(presentationData: presentationData, systemStyle: .glass, title: text, kind: .generic, alignment: .natural, sectionId: ItemListSectionId(self.section), style: .blocks, action: { arguments.changeProfilePhoto() }) case let .usernameHeader(_, title): return ItemListSectionHeaderItem(presentationData: presentationData, text: title, sectionId: self.section) case let .username(theme, placeholder, text): - return ItemListSingleLineInputItem(presentationData: presentationData, title: NSAttributedString(string: "t.me/", textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: placeholder, type: .username, clearType: .always, tag: nil, sectionId: self.section, textUpdated: { updatedText in + return ItemListSingleLineInputItem(presentationData: presentationData, systemStyle: .glass, title: NSAttributedString(string: "t.me/", textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: placeholder, type: .username, clearType: .always, tag: nil, sectionId: self.section, textUpdated: { updatedText in arguments.updatePublicLinkText(updatedText) }, action: { }) @@ -364,24 +364,24 @@ private enum CreateGroupEntry: ItemListNodeEntry { case let .usernameInfo(_, text): return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section) case let .topics(_, text): - return ItemListSwitchItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/Topics")?.precomposed(), title: text, value: true, enabled: false, sectionId: self.section, style: .blocks, updated: { _ in }) + return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, icon: UIImage(bundleImageName: "Settings/Menu/Topics")?.precomposed(), title: text, value: true, enabled: false, sectionId: self.section, style: .blocks, updated: { _ in }) case let .topicsInfo(_, text): return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) case let .autoDelete(text, value): - return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .optionArrows, action: { + return ItemListDisclosureItem(presentationData: presentationData, systemStyle: .glass, title: text, label: value, sectionId: self.section, style: .blocks, disclosureStyle: .optionArrows, action: { arguments.updateAutoDelete() }, tag: CreateGroupEntryTag.autoDelete) case let .autoDeleteInfo(text): return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section) case let .member(_, _, _, dateTimeFormat, nameDisplayOrder, peer, presence): - return ItemListPeerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: arguments.context, peer: EnginePeer(peer), presence: presence.flatMap(EnginePeer.Presence.init), text: .presence, label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: nil, enabled: true, selectable: true, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }) + return ItemListPeerItem(presentationData: presentationData, systemStyle: .glass, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, context: arguments.context, peer: EnginePeer(peer), presence: presence.flatMap(EnginePeer.Presence.init), text: .presence, label: .none, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: nil, enabled: true, selectable: true, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { _, _ in }, removePeer: { _ in }) case let .locationHeader(_, title): return ItemListSectionHeaderItem(presentationData: presentationData, text: title, sectionId: self.section) case let .location(theme, location): let imageSignal = chatMapSnapshotImage(engine: arguments.context.engine, resource: MapSnapshotMediaResource(latitude: location.latitude, longitude: location.longitude, width: 90, height: 90)) - return ItemListAddressItem(theme: theme, label: "", text: location.address.replacingOccurrences(of: ", ", with: "\n"), imageSignal: imageSignal, selected: nil, sectionId: self.section, style: .blocks, action: nil) + return ItemListAddressItem(theme: theme, systemStyle: .glass, label: "", text: location.address.replacingOccurrences(of: ", ", with: "\n"), imageSignal: imageSignal, selected: nil, sectionId: self.section, style: .blocks, action: nil) case let .changeLocation(_, text): - return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: ItemListSectionId(self.section), style: .blocks, action: { + return ItemListActionItem(presentationData: presentationData, systemStyle: .glass, title: text, kind: .generic, alignment: .natural, sectionId: ItemListSectionId(self.section), style: .blocks, action: { arguments.changeLocation() }) case let .locationInfo(_, text): @@ -389,7 +389,7 @@ private enum CreateGroupEntry: ItemListNodeEntry { case let .venueHeader(_, title): return ItemListSectionHeaderItem(presentationData: presentationData, text: title, sectionId: self.section) case let .venue(_, _, venue): - return ItemListVenueItem(presentationData: presentationData, engine: arguments.context.engine, venue: venue, sectionId: self.section, style: .blocks, action: { + return ItemListVenueItem(presentationData: presentationData, systemStyle: .glass, engine: arguments.context.engine, venue: venue, sectionId: self.section, style: .blocks, action: { arguments.updateWithVenue(venue) }) } diff --git a/submodules/TelegramUI/Sources/EmojisChatInputPanelItem.swift b/submodules/TelegramUI/Sources/EmojisChatInputPanelItem.swift index d193f2c4..99908fef 100644 --- a/submodules/TelegramUI/Sources/EmojisChatInputPanelItem.swift +++ b/submodules/TelegramUI/Sources/EmojisChatInputPanelItem.swift @@ -100,7 +100,7 @@ final class EmojisChatInputPanelItemNode: ListViewItemNode { self.symbolNode = TextNode() self.symbolNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0) - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.symbolNode) } diff --git a/submodules/TelegramUI/Sources/HashtagChatInputContextPanelNode.swift b/submodules/TelegramUI/Sources/HashtagChatInputContextPanelNode.swift index 216bf427..977345b9 100644 --- a/submodules/TelegramUI/Sources/HashtagChatInputContextPanelNode.swift +++ b/submodules/TelegramUI/Sources/HashtagChatInputContextPanelNode.swift @@ -176,7 +176,7 @@ final class HashtagChatInputContextPanelNode: ChatInputContextPanelNode { peer: peer, title: self.strings.Chat_HashtagSuggestion_UseLocal_Title("#\(query)@\(addressName)").string, text: isGroup ? self.strings.Chat_HashtagSuggestion_UseLocal_Group_Text : self.strings.Chat_HashtagSuggestion_UseLocal_Channel_Text, - badge: self.strings.ChatList_ContextMenuBadgeNew, + badge: nil, hashtag: "\(query)@\(addressName)", revealed: false, isAdditionalRecent: false diff --git a/submodules/TelegramUI/Sources/HashtagChatInputPanelItem.swift b/submodules/TelegramUI/Sources/HashtagChatInputPanelItem.swift index 0ffafe06..9dfef707 100644 --- a/submodules/TelegramUI/Sources/HashtagChatInputPanelItem.swift +++ b/submodules/TelegramUI/Sources/HashtagChatInputPanelItem.swift @@ -149,7 +149,7 @@ final class HashtagChatInputPanelItemNode: ListViewItemNode { self.activateAreaNode = AccessibilityAreaNode() self.activateAreaNode.accessibilityTraits = [.button] - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.separatorNode) self.addSubnode(self.titleNode) diff --git a/submodules/TelegramUI/Sources/HorizontalListContextResultsChatInputPanelItem.swift b/submodules/TelegramUI/Sources/HorizontalListContextResultsChatInputPanelItem.swift index 4209c7fd..ed79bfdb 100644 --- a/submodules/TelegramUI/Sources/HorizontalListContextResultsChatInputPanelItem.swift +++ b/submodules/TelegramUI/Sources/HorizontalListContextResultsChatInputPanelItem.swift @@ -133,7 +133,7 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode CMTimebaseSetRate(timebase!, rate: 0.0) self.timebase = timebase! - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.imageNodeBackground) diff --git a/submodules/TelegramUI/Sources/MentionChatInputPanelItem.swift b/submodules/TelegramUI/Sources/MentionChatInputPanelItem.swift index e59b7c15..928dba8a 100644 --- a/submodules/TelegramUI/Sources/MentionChatInputPanelItem.swift +++ b/submodules/TelegramUI/Sources/MentionChatInputPanelItem.swift @@ -126,7 +126,7 @@ final class MentionChatInputPanelItemNode: ListViewItemNode { self.activateAreaNode = AccessibilityAreaNode() self.activateAreaNode.accessibilityTraits = [.button] - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.separatorNode) diff --git a/submodules/TelegramUI/Sources/NotificationContainerControllerNode.swift b/submodules/TelegramUI/Sources/NotificationContainerControllerNode.swift index 4d055b9b..7221f5db 100644 --- a/submodules/TelegramUI/Sources/NotificationContainerControllerNode.swift +++ b/submodules/TelegramUI/Sources/NotificationContainerControllerNode.swift @@ -95,10 +95,7 @@ final class NotificationContainerControllerNode: ASDisplayNode { }) } - var useCompactLayout = false - if let validLayout = self.validLayout { - useCompactLayout = min(validLayout.size.width, validLayout.size.height) < 375.0 - } + let useCompactLayout = "".isEmpty let itemNode = item.node(compact: useCompactLayout) let containerNode = NotificationItemContainerNode(theme: self.presentationData.theme, contentNode: itemNode) @@ -165,10 +162,7 @@ final class NotificationContainerControllerNode: ASDisplayNode { } self.topItemAndNode = nil - var useCompactLayout = false - if let validLayout = self.validLayout { - useCompactLayout = min(validLayout.size.width, validLayout.size.height) < 375.0 - } + let useCompactLayout = "".isEmpty let itemNode = item.node(compact: useCompactLayout) let containerNode = NotificationItemContainerNode(theme: self.presentationData.theme, contentNode: itemNode) diff --git a/submodules/TelegramUI/Sources/NotificationItemContainerNode.swift b/submodules/TelegramUI/Sources/NotificationItemContainerNode.swift index aff92e02..f413f325 100644 --- a/submodules/TelegramUI/Sources/NotificationItemContainerNode.swift +++ b/submodules/TelegramUI/Sources/NotificationItemContainerNode.swift @@ -2,14 +2,16 @@ import Foundation import UIKit import AsyncDisplayKit import Display +import ComponentFlow import TelegramPresentationData import ChatMessageNotificationItem +import GlassBackgroundComponent final class NotificationItemContainerNode: ASDisplayNode { - private let backgroundNode: ASImageNode - - private var validLayout: ContainerViewLayout? + private let theme: PresentationTheme + private let backgroundView = GlassBackgroundView() + var item: NotificationItem? private var hapticFeedback: HapticFeedback? @@ -40,6 +42,8 @@ final class NotificationItemContainerNode: ASDisplayNode { } } + private var validLayout: ContainerViewLayout? + var dismissed: ((NotificationItem) -> Void)? var cancelTimeout: ((NotificationItem) -> Void)? var resumeTimeout: ((NotificationItem) -> Void)? @@ -48,15 +52,10 @@ final class NotificationItemContainerNode: ASDisplayNode { init(theme: PresentationTheme, contentNode: NotificationItemNode?) { self.contentNode = contentNode - - self.backgroundNode = ASImageNode() - self.backgroundNode.displayWithoutProcessing = true - self.backgroundNode.displaysAsynchronously = false - self.backgroundNode.image = PresentationResourcesRootController.inAppNotificationBackground(theme) - + self.theme = theme + super.init() - self.addSubnode(self.backgroundNode) if let contentNode { self.addSubnode(contentNode) } @@ -65,6 +64,8 @@ final class NotificationItemContainerNode: ASDisplayNode { override func didLoad() { super.didLoad() + self.view.insertSubview(self.backgroundView, at: 0) + if let contentNode = self.contentNode, !contentNode.acceptsTouches { self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:))) @@ -76,13 +77,13 @@ final class NotificationItemContainerNode: ASDisplayNode { func animateIn() { if let _ = self.validLayout { - self.layer.animatePosition(from: CGPoint(x: 0.0, y: -self.backgroundNode.bounds.size.height), to: CGPoint(), duration: 0.4, additive: true) + self.layer.animatePosition(from: CGPoint(x: 0.0, y: -self.backgroundView.frame.maxY), to: CGPoint(), duration: 0.4, additive: true) } } func animateOut(completion: @escaping () -> Void) { if let _ = self.validLayout { - self.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -self.backgroundNode.bounds.size.height), duration: 0.4, removeOnCompletion: false, additive: true, completion: { _ in + self.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -self.backgroundView.frame.maxY), duration: 0.4, removeOnCompletion: false, additive: true, completion: { _ in completion() }) } else { @@ -100,7 +101,7 @@ final class NotificationItemContainerNode: ASDisplayNode { if let statusBarHeight = layout.statusBarHeight, statusBarHeight >= 39.0 { if layout.deviceMetrics.hasDynamicIsland { - contentInsets.top = statusBarHeight + contentInsets.top = statusBarHeight + 6.0 } else if statusBarHeight >= 44.0 { contentInsets.top += 34.0 } else { @@ -113,7 +114,10 @@ final class NotificationItemContainerNode: ASDisplayNode { let contentWidth = containerWidth - contentInsets.left - contentInsets.right let contentHeight = contentNode.updateLayout(width: contentWidth, transition: transition) - transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - containerWidth - 8.0 * 2.0) / 2.0), y: contentInsets.top - 16.0), size: CGSize(width: containerWidth + 8.0 * 2.0, height: 8.0 + contentHeight + 20.0 + 8.0))) + let backgroundInset: CGFloat = 8.0 + let backgroundSize = CGSize(width: containerWidth - backgroundInset * 2.0, height: contentHeight) + self.backgroundView.update(size: backgroundSize, cornerRadius: 24.0, isDark: self.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), transition: ComponentTransition(transition)) + transition.updateFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - backgroundSize.width) / 2.0), y: contentInsets.top), size: backgroundSize)) transition.updateFrame(node: contentNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - contentWidth) / 2.0), y: contentInsets.top), size: CGSize(width: contentWidth, height: contentHeight))) } diff --git a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift index 355eacea..0a99f2c9 100644 --- a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift +++ b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift @@ -38,6 +38,7 @@ import TextFormat import BrowserUI import MediaEditorScreen import GiftSetupScreen +import AlertComponent private func defaultNavigationForPeerId(_ peerId: PeerId?, navigation: ChatControllerInteractionNavigateToPeer) -> ChatControllerInteractionNavigateToPeer { if case .default = navigation { @@ -95,7 +96,7 @@ func openResolvedUrlImpl( present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Resolve_ErrorNotFound, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) } case .inaccessiblePeer: - present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.Conversation_ErrorInaccessibleMessage, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) + present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Conversation_ErrorInaccessibleMessage, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) case let .botStart(peer, payload): openPeer(EnginePeer(peer), .withBotStartPayload(ChatControllerInitialBotStart(payload: payload, behavior: .interactive))) case let .groupBotStart(botPeerId, payload, adminRights, peerType): @@ -132,9 +133,8 @@ func openResolvedUrlImpl( let addMemberImpl = { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let theme = AlertControllerTheme(presentationData: presentationData) - let attributedTitle = NSAttributedString(string: presentationData.strings.Bot_AddToChat_Add_MemberAlertTitle, font: Font.semibold(presentationData.listsFontSize.baseDisplaySize), textColor: theme.primaryColor, paragraphAlignment: .center) - + let strings = presentationData.strings + var isGroup: Bool = false var peerTitle: String = "" if case let .legacyGroup(peer) = peer { @@ -147,51 +147,54 @@ func openResolvedUrlImpl( peerTitle = peer.title } - let text = isGroup ? presentationData.strings.Bot_AddToChat_Add_MemberAlertTextGroup(peerTitle).string : presentationData.strings.Bot_AddToChat_Add_MemberAlertTextChannel(peerTitle).string + let text = isGroup ? strings.Bot_AddToChat_Add_MemberAlertTextGroup(peerTitle).string : strings.Bot_AddToChat_Add_MemberAlertTextChannel(peerTitle).string - let body = MarkdownAttributeSet(font: Font.regular(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: theme.primaryColor) - let bold = MarkdownAttributeSet(font: Font.semibold(presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: theme.primaryColor) - let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .center) - - let controller = richTextAlertController(context: context, title: attributedTitle, text: attributedText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Bot_AddToChat_Add_MemberAlertAdd, action: { - if payload.isEmpty { - if peerId.namespace == Namespaces.Peer.CloudGroup { - let _ = (context.engine.peers.addGroupMember(peerId: peerId, memberId: botPeerId) - |> deliverOnMainQueue).startStandalone(completed: { - controller?.dismiss() - }) - } else { - let _ = (context.engine.peers.addChannelMember(peerId: peerId, memberId: botPeerId) - |> deliverOnMainQueue).startStandalone(completed: { - controller?.dismiss() - }) - } - } else { - let _ = (context.engine.messages.requestStartBotInGroup(botPeerId: botPeerId, groupPeerId: peerId, payload: payload) - |> deliverOnMainQueue).startStandalone(next: { result in - let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) - |> deliverOnMainQueue).startStandalone(next: { peer in - guard let peer = peer else { - return + let alertController = textAlertController( + context: context, + title: strings.Bot_AddToChat_Add_MemberAlertTitle, + text: text, + actions: [ + TextAlertAction(type: .defaultAction, title: strings.Bot_AddToChat_Add_MemberAlertAdd, action: { + if payload.isEmpty { + if peerId.namespace == Namespaces.Peer.CloudGroup { + let _ = (context.engine.peers.addGroupMember(peerId: peerId, memberId: botPeerId) + |> deliverOnMainQueue).startStandalone(completed: { + controller?.dismiss() + }) + } else { + let _ = (context.engine.peers.addChannelMember(peerId: peerId, memberId: botPeerId) + |> deliverOnMainQueue).startStandalone(completed: { + controller?.dismiss() + }) } - if let navigationController = navigationController { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer))) - } - switch result { - case let .channelParticipant(participant): - context.peerChannelMemberCategoriesContextsManager.externallyAdded(peerId: peerId, participant: participant) - case .none: - break - } - controller?.dismiss() - }) - }, error: { _ in - - }) - } - }), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - })], actionLayout: .vertical) - present(controller, nil) + } else { + let _ = (context.engine.messages.requestStartBotInGroup(botPeerId: botPeerId, groupPeerId: peerId, payload: payload) + |> deliverOnMainQueue).startStandalone(next: { result in + let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) + |> deliverOnMainQueue).startStandalone(next: { peer in + guard let peer = peer else { + return + } + if let navigationController = navigationController { + context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peer))) + } + switch result { + case let .channelParticipant(participant): + context.peerChannelMemberCategoriesContextsManager.externallyAdded(peerId: peerId, participant: participant) + case .none: + break + } + controller?.dismiss() + }) + }, error: { _ in + + }) + } + }), + TextAlertAction(type: .genericAction, title: strings.Common_Cancel, action: {}) + ] + ) + present(alertController, nil) } if case let .channel(peer) = peer { @@ -1340,15 +1343,21 @@ func openResolvedUrlImpl( storyProgressPauseContext.update(controller) } } else { - let controller = textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Chat_ErrorCantBoost, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) - present(controller, nil) - - controller.dismissed = { _ in + let alertController = AlertScreen( + context: context, + title: nil, + text: presentationData.strings.Chat_ErrorCantBoost, + actions: [ + .init(title: presentationData.strings.Common_OK, type: .default) + ] + ) + alertController.dismissed = { _ in dismissedImpl?() } + present(alertController, nil) if let storyProgressPauseContext = contentContext as? StoryProgressPauseContext { - storyProgressPauseContext.update(controller) + storyProgressPauseContext.update(alertController) } } case let .premiumGiftCode(slug): @@ -1515,6 +1524,7 @@ func openResolvedUrlImpl( let controller = context.sharedContext.makeGiftAuctionViewScreen( context: context, auctionContext: auctionContext, + peerId: nil, completion: { [weak navigationController] acquiredGifts, upgradeAttributes in if let upgradeAttributes { let controller = context.sharedContext.makeGiftAuctionWearPreviewScreen(context: context, auctionContext: auctionContext, acquiredGifts: acquiredGifts, attributes: upgradeAttributes, completion: { diff --git a/submodules/TelegramUI/Sources/OpenUrl.swift b/submodules/TelegramUI/Sources/OpenUrl.swift index 73d0f665..bc32dcc7 100644 --- a/submodules/TelegramUI/Sources/OpenUrl.swift +++ b/submodules/TelegramUI/Sources/OpenUrl.swift @@ -561,9 +561,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur } } if isToken { - context.sharedContext.presentGlobalController(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.AuthSessions_AddDevice_UrlLoginHint, actions: [ - TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: { - }), + context.sharedContext.presentGlobalController(textAlertController(context: context, title: nil, text: presentationData.strings.AuthSessions_AddDevice_UrlLoginHint, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {}), ], parseMarkdown: true), nil) return } @@ -770,6 +768,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur var startApp: String? var text: String? var profile: Bool = false + var direct: Bool = false var referrer: String? var albumId: Int64? var collectionId: Int64? @@ -823,6 +822,8 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur startChannel = "" } else if queryItem.name == "profile" { profile = true + } else if queryItem.name == "direct" { + direct = true } else if queryItem.name == "startapp" { startApp = "" } @@ -918,6 +919,13 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur convertedUrl = current + "?profile" } } + if direct, let current = convertedUrl { + if current.contains("?") { + convertedUrl = current + "&direct" + } else { + convertedUrl = current + "?direct" + } + } } } else if parsedUrl.host == "hostOverride" { if let components = URLComponents(string: "/?" + query) { diff --git a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift index 83114722..399419c8 100644 --- a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift +++ b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift @@ -259,7 +259,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu let chatLocationContextHolder = Atomic(value: nil) - self.historyNode = ChatHistoryListNodeImpl(context: context, updatedPresentationData: (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, adMessagesContext: nil, tag: .tag(tagMask), source: self.source, subject: .message(id: .id(initialMessageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil, setupReply: false), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, reverseGroups: !self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch), isChatPreview: false, messageTransitionNode: { return nil }) + self.historyNode = ChatHistoryListNodeImpl(context: context, updatedPresentationData: (context.sharedContext.currentPresentationData.with({ $0 }), context.sharedContext.presentationData), chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, adMessagesContext: nil, tag: .tag(tagMask), source: self.source, subject: .message(id: .id(initialMessageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil, setupReply: false), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(reversed: self.currentIsReversed, reverseGroups: !self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch), isChatPreview: false, messageTransitionNode: { return nil }) self.historyNode.clipsToBounds = true super.init() @@ -881,7 +881,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu } let chatLocationContextHolder = Atomic(value: nil) - let historyNode = ChatHistoryListNodeImpl(context: self.context, updatedPresentationData: (self.context.sharedContext.currentPresentationData.with({ $0 }), self.context.sharedContext.presentationData), chatLocation: self.chatLocation, chatLocationContextHolder: chatLocationContextHolder, adMessagesContext: nil, tag: .tag(tagMask), source: self.source, subject: .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil, setupReply: false), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(search: false, reversed: self.currentIsReversed, reverseGroups: !self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch), isChatPreview: false, messageTransitionNode: { return nil }) + let historyNode = ChatHistoryListNodeImpl(context: self.context, updatedPresentationData: (self.context.sharedContext.currentPresentationData.with({ $0 }), self.context.sharedContext.presentationData), chatLocation: self.chatLocation, chatLocationContextHolder: chatLocationContextHolder, adMessagesContext: nil, tag: .tag(tagMask), source: self.source, subject: .message(id: .id(messageId), highlight: ChatControllerSubject.MessageHighlight(quote: nil), timecode: nil, setupReply: false), controllerInteraction: self.controllerInteraction, selectedMessages: .single(nil), mode: .list(reversed: self.currentIsReversed, reverseGroups: !self.currentIsReversed, displayHeaders: .none, hintLinks: false, isGlobalSearch: self.isGlobalSearch), isChatPreview: false, messageTransitionNode: { return nil }) historyNode.clipsToBounds = true historyNode.preloadPages = true historyNode.stackFromBottom = true diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 90cafd6c..3d727ce1 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -10,6 +10,7 @@ import TelegramCallsUI import TelegramUIPreferences import AccountContext import DeviceLocationManager +import ItemListUI import LegacyUI import ChatListUI import PeersNearbyUI @@ -92,6 +93,8 @@ import AttachmentFileController import NewContactScreen import PasskeysScreen import GiftDemoScreen +import ChatTextLinkEditUI +import CocoonInfoScreen private final class AccountUserInterfaceInUseContext { let subscribers = Bag<(Bool) -> Void>() @@ -2242,6 +2245,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { return ChatHistoryListNodeImpl( context: context, updatedPresentationData: updatedPresentationData, + systemStyle: .glass, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, adMessagesContext: nil, @@ -3191,6 +3195,8 @@ public final class SharedAccountContextImpl: SharedAccountContext { guard let controller, case let .starGiftTransfer(_, _, gift, transferStars, _, _) = source else { return } + controller.view.window?.endEditing(true) + var dismissAlertImpl: (() -> Void)? let alertController = giftTransferAlertController( context: context, @@ -3317,7 +3323,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { controller.present(alertController, in: .current) dismissAlertImpl = { [weak alertController] in - alertController?.dismissAnimated() + alertController?.dismiss() } } @@ -3713,7 +3719,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { return stickerMediaPickerController(context: context, getSourceRect: getSourceRect, completion: completion, dismissed: dismissed) } - public func makeAvatarMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect?, canDelete: Bool, performDelete: @escaping () -> Void, completion: @escaping (Any?, UIView?, CGRect, UIImage?, Bool, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController { + public func makeAvatarMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect?, canDelete: Bool, performDelete: @escaping () -> Void, completion: @escaping (Any?, UIView?, CGRect, UIImage?, Bool, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> (ViewController?, Any?) { return avatarMediaPickerController(context: context, getSourceRect: getSourceRect, canDelete: canDelete, performDelete: performDelete, completion: completion, dismissed: dismissed) } @@ -3857,20 +3863,20 @@ public final class SharedAccountContextImpl: SharedAccountContext { return GiftAuctionBidScreen(context: context, toPeerId: toPeerId, text: text, entities: entities, hideName: hideName, auctionContext: auctionContext, acquiredGifts: acquiredGifts) } - public func makeGiftAuctionViewScreen(context: AccountContext, auctionContext: GiftAuctionContext, completion: @escaping (Signal<[GiftAuctionAcquiredGift], NoError>, [StarGift.UniqueGift.Attribute]?) -> Void) -> ViewController { - return GiftAuctionViewScreen(context: context, auctionContext: auctionContext, completion: completion) + public func makeGiftAuctionViewScreen(context: AccountContext, auctionContext: GiftAuctionContext, peerId: EnginePeer.Id?, completion: @escaping (Signal<[GiftAuctionAcquiredGift], NoError>, [StarGift.UniqueGift.Attribute]?) -> Void) -> ViewController { + return GiftAuctionViewScreen(context: context, auctionContext: auctionContext, peerId: peerId, completion: completion) } public func makeGiftAuctionActiveBidsScreen(context: AccountContext) -> ViewController { return GiftAuctionActiveBidsScreen(context: context) } - public func makeGiftOfferScreen(context: AccountContext, gift: StarGift.UniqueGift, peer: EnginePeer, amount: CurrencyAmount, commit: @escaping () -> Void) -> ViewController { - return giftOfferAlertController(context: context, gift: gift, peer: peer, amount: amount, commit: commit) + public func makeGiftOfferScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, gift: StarGift.UniqueGift, peer: EnginePeer, amount: CurrencyAmount, commit: @escaping () -> Void) -> ViewController { + return giftOfferAlertController(context: context, updatedPresentationData: updatedPresentationData, gift: gift, peer: peer, amount: amount, commit: commit) } - public func makeGiftUpgradeVariantsPreviewScreen(context: AccountContext, gift: StarGift, attributes: [StarGift.UniqueGift.Attribute]) -> ViewController { - return GiftUpgradePreviewScreen(context: context, gift: gift, attributes: attributes) + public func makeGiftUpgradeVariantsScreen(context: AccountContext, gift: StarGift, attributes: [StarGift.UniqueGift.Attribute], selectedAttributes: [StarGift.UniqueGift.Attribute]?, focusedAttribute: StarGift.UniqueGift.Attribute?) -> ViewController { + return GiftUpgradeVariantsScreen(context: context, gift: gift, attributes: attributes, selectedAttributes: selectedAttributes, focusedAttribute: focusedAttribute) } public func makeGiftAuctionWearPreviewScreen(context: AccountContext, auctionContext: GiftAuctionContext, acquiredGifts: Signal<[GiftAuctionAcquiredGift], NoError>?, attributes: [StarGift.UniqueGift.Attribute], completion: @escaping () -> Void) -> ViewController { @@ -4023,6 +4029,14 @@ public final class SharedAccountContextImpl: SharedAccountContext { return SendInviteLinkScreen(context: context, subject: subject, peers: peers, theme: theme) } + public func makeCocoonInfoScreen(context: AccountContext) -> ViewController { + return CocoonInfoScreen(context: context) + } + + public func makeLinkEditController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, text: String, link: String?, apply: @escaping (String?) -> Void) -> ViewController { + return chatTextLinkEditController(context: context, updatedPresentationData: updatedPresentationData, text: text, link: link, apply: apply) + } + @available(iOS 13.0, *) public func makePostSuggestionsSettingsScreen(context: AccountContext, peerId: EnginePeer.Id) async -> ViewController { return await PostSuggestionsSettingsScreen(context: context, peerId: peerId, completion: {}) diff --git a/submodules/TelegramUI/Sources/TelegramRootController.swift b/submodules/TelegramUI/Sources/TelegramRootController.swift index c0f1427e..8b4fee9e 100644 --- a/submodules/TelegramUI/Sources/TelegramRootController.swift +++ b/submodules/TelegramUI/Sources/TelegramRootController.swift @@ -31,7 +31,6 @@ import PeerInfoScreen import PeerInfoStoryGridScreen import ShareWithPeersScreen import ChatEmptyNode -import UndoUI private class DetailsChatPlaceholderNode: ASDisplayNode, NavigationDetailsPlaceholderNode { private var presentationData: PresentationData @@ -42,7 +41,7 @@ private class DetailsChatPlaceholderNode: ASDisplayNode, NavigationDetailsPlaceh init(context: AccountContext) { self.presentationData = context.sharedContext.currentPresentationData.with { $0 } - self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: context.account.peerId, mode: .standard(.default), chatLocation: .peer(id: context.account.peerId), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil, businessIntro: nil) + self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: context.account.peerId, mode: .standard(.default), chatLocation: .peer(id: context.account.peerId), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil, businessIntro: nil) self.wallpaperBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true, useSharedAnimationPhase: true) self.emptyNode = ChatEmptyNode(context: context, interaction: nil) @@ -55,7 +54,7 @@ private class DetailsChatPlaceholderNode: ASDisplayNode, NavigationDetailsPlaceh func updatePresentationData(_ presentationData: PresentationData) { self.presentationData = presentationData - self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.presentationInterfaceState.limitsConfiguration, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.presentationInterfaceState.accountPeerId, mode: .standard(.default), chatLocation: self.presentationInterfaceState.chatLocation, subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil, businessIntro: nil) + self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: self.presentationData.chatWallpaper, theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.presentationInterfaceState.limitsConfiguration, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.presentationInterfaceState.accountPeerId, mode: .standard(.default), chatLocation: self.presentationInterfaceState.chatLocation, subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil, businessIntro: nil) self.wallpaperBackgroundNode.update(wallpaper: presentationData.chatWallpaper, animated: false) } @@ -188,7 +187,7 @@ public final class TelegramRootController: NavigationController, TelegramRootCon } public func addRootControllers(showCallsTab: Bool) { - let tabBarController = TabBarControllerImpl(theme: self.presentationData.theme) + let tabBarController = TabBarControllerImpl(theme: self.presentationData.theme, strings: self.presentationData.strings) tabBarController.navigationPresentation = .master let chatListController = self.context.sharedContext.makeChatListController(context: self.context, location: .chatList(groupId: .root), controlsHistoryPreload: true, hideNetworkActivityStatus: false, previewing: false, enableDebugActions: !GlobalExperimentalSettings.isAppStoreBuild) if let sharedContext = self.context.sharedContext as? SharedAccountContextImpl { diff --git a/submodules/TelegramUI/Sources/VerticalListContextResultsChatInputPanelButtonItem.swift b/submodules/TelegramUI/Sources/VerticalListContextResultsChatInputPanelButtonItem.swift index f79a3a6d..9e944a28 100644 --- a/submodules/TelegramUI/Sources/VerticalListContextResultsChatInputPanelButtonItem.swift +++ b/submodules/TelegramUI/Sources/VerticalListContextResultsChatInputPanelButtonItem.swift @@ -96,7 +96,7 @@ final class VerticalListContextResultsChatInputPanelButtonItemNode: ListViewItem self.titleNode = TextNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.separatorNode) diff --git a/submodules/TelegramUI/Sources/VerticalListContextResultsChatInputPanelItem.swift b/submodules/TelegramUI/Sources/VerticalListContextResultsChatInputPanelItem.swift index 274d87db..86c40fa8 100644 --- a/submodules/TelegramUI/Sources/VerticalListContextResultsChatInputPanelItem.swift +++ b/submodules/TelegramUI/Sources/VerticalListContextResultsChatInputPanelItem.swift @@ -118,7 +118,7 @@ final class VerticalListContextResultsChatInputPanelItemNode: ListViewItemNode { self.iconImageNode.isLayerBacked = !smartInvertColorsEnabled() self.iconImageNode.displaysAsynchronously = false - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.separatorNode) diff --git a/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift b/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift index 319cb169..77f46844 100644 --- a/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift +++ b/submodules/TelegramUIPreferences/Sources/ExperimentalUISettings.swift @@ -32,7 +32,6 @@ public struct ExperimentalUISettings: Codable, Equatable { public var chatListPhotos: Bool public var knockoutWallpaper: Bool public var foldersTabAtBottom: Bool - public var playerEmbedding: Bool public var preferredVideoCodec: String? public var disableVideoAspectScaling: Bool public var enableVoipTcp: Bool @@ -80,7 +79,6 @@ public struct ExperimentalUISettings: Codable, Equatable { chatListPhotos: false, knockoutWallpaper: false, foldersTabAtBottom: false, - playerEmbedding: false, preferredVideoCodec: nil, disableVideoAspectScaling: false, enableVoipTcp: false, @@ -129,7 +127,6 @@ public struct ExperimentalUISettings: Codable, Equatable { chatListPhotos: Bool, knockoutWallpaper: Bool, foldersTabAtBottom: Bool, - playerEmbedding: Bool, preferredVideoCodec: String?, disableVideoAspectScaling: Bool, enableVoipTcp: Bool, @@ -175,7 +172,6 @@ public struct ExperimentalUISettings: Codable, Equatable { self.chatListPhotos = chatListPhotos self.knockoutWallpaper = knockoutWallpaper self.foldersTabAtBottom = foldersTabAtBottom - self.playerEmbedding = playerEmbedding self.preferredVideoCodec = preferredVideoCodec self.disableVideoAspectScaling = disableVideoAspectScaling self.enableVoipTcp = enableVoipTcp @@ -225,7 +221,6 @@ public struct ExperimentalUISettings: Codable, Equatable { self.chatListPhotos = (try container.decodeIfPresent(Int32.self, forKey: "chatListPhotos") ?? 0) != 0 self.knockoutWallpaper = (try container.decodeIfPresent(Int32.self, forKey: "knockoutWallpaper") ?? 0) != 0 self.foldersTabAtBottom = (try container.decodeIfPresent(Int32.self, forKey: "foldersTabAtBottom") ?? 0) != 0 - self.playerEmbedding = (try container.decodeIfPresent(Int32.self, forKey: "playerEmbedding") ?? 0) != 0 self.preferredVideoCodec = try container.decodeIfPresent(String.self.self, forKey: "preferredVideoCodec") self.disableVideoAspectScaling = (try container.decodeIfPresent(Int32.self, forKey: "disableVideoAspectScaling") ?? 0) != 0 self.enableVoipTcp = (try container.decodeIfPresent(Int32.self, forKey: "enableVoipTcp") ?? 0) != 0 @@ -275,7 +270,6 @@ public struct ExperimentalUISettings: Codable, Equatable { try container.encode((self.chatListPhotos ? 1 : 0) as Int32, forKey: "chatListPhotos") try container.encode((self.knockoutWallpaper ? 1 : 0) as Int32, forKey: "knockoutWallpaper") try container.encode((self.foldersTabAtBottom ? 1 : 0) as Int32, forKey: "foldersTabAtBottom") - try container.encode((self.playerEmbedding ? 1 : 0) as Int32, forKey: "playerEmbedding") try container.encodeIfPresent(self.preferredVideoCodec, forKey: "preferredVideoCodec") try container.encode((self.disableVideoAspectScaling ? 1 : 0) as Int32, forKey: "disableVideoAspectScaling") try container.encode((self.enableVoipTcp ? 1 : 0) as Int32, forKey: "enableVoipTcp") diff --git a/submodules/TelegramUpdateUI/Sources/UpdateInfoItem.swift b/submodules/TelegramUpdateUI/Sources/UpdateInfoItem.swift index d64c98a5..f0794345 100644 --- a/submodules/TelegramUpdateUI/Sources/UpdateInfoItem.swift +++ b/submodules/TelegramUpdateUI/Sources/UpdateInfoItem.swift @@ -152,7 +152,7 @@ class UpdateInfoItemNode: ListViewItemNode { self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.addSubnode(self.iconNode) self.addSubnode(self.overlayNode) diff --git a/submodules/TextFormat/Sources/ChatTextInputAttributes.swift b/submodules/TextFormat/Sources/ChatTextInputAttributes.swift index aceef6e4..50ee9631 100644 --- a/submodules/TextFormat/Sources/ChatTextInputAttributes.swift +++ b/submodules/TextFormat/Sources/ChatTextInputAttributes.swift @@ -412,6 +412,7 @@ public final class ChatTextInputTextCustomEmojiAttribute: NSObject, Codable { case ton(tinted: Bool) case animation(name: String) case verification + case dice } public let interactivelySelectedFromPackId: ItemCollectionId? diff --git a/submodules/TranslateUI/BUILD b/submodules/TranslateUI/BUILD index 6de2b55b..376c7130 100644 --- a/submodules/TranslateUI/BUILD +++ b/submodules/TranslateUI/BUILD @@ -31,7 +31,6 @@ swift_library( "//submodules/Components/BundleIconComponent:BundleIconComponent", "//submodules/UndoUI:UndoUI", "//submodules/ActivityIndicator:ActivityIndicator", - "//submodules/ChatListSearchItemNode:ChatListSearchItemNode", "//submodules/ShimmerEffect:ShimmerEffect", ], visibility = [ diff --git a/submodules/TranslateUI/Sources/ChatTranslation.swift b/submodules/TranslateUI/Sources/ChatTranslation.swift index f247b69c..f7d16c25 100644 --- a/submodules/TranslateUI/Sources/ChatTranslation.swift +++ b/submodules/TranslateUI/Sources/ChatTranslation.swift @@ -270,47 +270,55 @@ public func chatTranslationState(context: AccountContext, peerId: EnginePeer.Id, var count = 0 for message in messages { if message.effectivelyIncoming(context.account.peerId), message.text.count >= 10 { - var text = String(message.text.prefix(256)) - if var entities = message.textEntitiesAttribute?.entities.filter({ entity in - switch entity.type { - case .Pre, .Code, .Url, .Email, .Mention, .Hashtag, .BotCommand: - return true - default: - return false + if let summaryAttribute = message.attributes.first(where: { $0 is SummarizationMessageAttribute }) as? SummarizationMessageAttribute, !summaryAttribute.fromLang.isEmpty { + let fromLang = normalizeTranslationLanguage(summaryAttribute.fromLang) + if supportedTranslationLanguages.contains(fromLang) { + fromLangs[fromLang] = (fromLangs[fromLang] ?? 0) + message.text.count + count += 1 } - }) { - entities = entities.sorted(by: { $0.range.lowerBound > $1.range.lowerBound }) - var ranges: [Range] = [] - for entity in entities { - if entity.range.lowerBound > text.count || entity.range.upperBound > text.count { - continue + } else { + var text = String(message.text.prefix(256)) + if var entities = message.textEntitiesAttribute?.entities.filter({ entity in + switch entity.type { + case .Pre, .Code, .Url, .Email, .Mention, .Hashtag, .BotCommand: + return true + default: + return false } - ranges.append(text.index(text.startIndex, offsetBy: entity.range.lowerBound) ..< text.index(text.startIndex, offsetBy: entity.range.upperBound)) - } - for range in ranges { - if range.upperBound < text.endIndex { - text.removeSubrange(range) + }) { + entities = entities.sorted(by: { $0.range.lowerBound > $1.range.lowerBound }) + var ranges: [Range] = [] + for entity in entities { + if entity.range.lowerBound > text.count || entity.range.upperBound > text.count { + continue + } + ranges.append(text.index(text.startIndex, offsetBy: entity.range.lowerBound) ..< text.index(text.startIndex, offsetBy: entity.range.upperBound)) + } + for range in ranges { + if range.upperBound < text.endIndex { + text.removeSubrange(range) + } } } - } - - if message.text.count < 10 { - continue - } - - languageRecognizer.processString(text) - let hypotheses = languageRecognizer.languageHypotheses(withMaximum: 4) - languageRecognizer.reset() - - let filteredLanguages = hypotheses.filter { supportedTranslationLanguages.contains(normalizeTranslationLanguage($0.key.rawValue)) }.sorted(by: { $0.value > $1.value }) - if let language = filteredLanguages.first { - let fromLang = normalizeTranslationLanguage(language.key.rawValue) - if loggingEnabled && !["en", "ru"].contains(fromLang) && !dontTranslateLanguages.contains(fromLang) { - Logger.shared.log("ChatTranslation", "\(text)") - Logger.shared.log("ChatTranslation", "Recognized as: \(fromLang), other hypotheses: \(hypotheses.map { $0.key.rawValue }.joined(separator: ",")) ") + + if message.text.count < 10 { + continue + } + + languageRecognizer.processString(text) + let hypotheses = languageRecognizer.languageHypotheses(withMaximum: 4) + languageRecognizer.reset() + + let filteredLanguages = hypotheses.filter { supportedTranslationLanguages.contains(normalizeTranslationLanguage($0.key.rawValue)) }.sorted(by: { $0.value > $1.value }) + if let language = filteredLanguages.first { + let fromLang = normalizeTranslationLanguage(language.key.rawValue) + if loggingEnabled && !["en", "ru"].contains(fromLang) && !dontTranslateLanguages.contains(fromLang) { + Logger.shared.log("ChatTranslation", "\(text)") + Logger.shared.log("ChatTranslation", "Recognized as: \(fromLang), other hypotheses: \(hypotheses.map { $0.key.rawValue }.joined(separator: ",")) ") + } + fromLangs[fromLang] = (fromLangs[fromLang] ?? 0) + message.text.count + count += 1 } - fromLangs[fromLang] = (fromLangs[fromLang] ?? 0) + message.text.count - count += 1 } } if count >= 16 { diff --git a/submodules/TranslateUI/Sources/LocalizationListItem.swift b/submodules/TranslateUI/Sources/LocalizationListItem.swift index 5ea365bc..07ba6eba 100644 --- a/submodules/TranslateUI/Sources/LocalizationListItem.swift +++ b/submodules/TranslateUI/Sources/LocalizationListItem.swift @@ -7,7 +7,6 @@ import TelegramPresentationData import ItemListUI import PresentationDataUtils import ActivityIndicator -import ChatListSearchItemNode import ShimmerEffect public struct LocalizationListItemEditing: Equatable { @@ -185,7 +184,7 @@ class LocalizationListItemNode: ItemListRevealOptionsItemNode { self.activateArea = AccessibilityAreaNode() - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.containerNode) @@ -314,7 +313,7 @@ class LocalizationListItemNode: ItemListRevealOptionsItemNode { if strongSelf.maskNode.supernode == nil { strongSelf.addSubnode(strongSelf.maskNode) } - let hasCorners = itemListHasRoundedBlockLayout(params) + let hasCorners = itemListHasRoundedBlockLayout(params) && !item.alwaysPlain var hasTopCorners = false var hasBottomCorners = false switch neighbors.top { diff --git a/submodules/UndoUI/Sources/UndoOverlayController.swift b/submodules/UndoUI/Sources/UndoOverlayController.swift index e408d910..24ed6920 100644 --- a/submodules/UndoUI/Sources/UndoOverlayController.swift +++ b/submodules/UndoUI/Sources/UndoOverlayController.swift @@ -18,7 +18,7 @@ public enum UndoOverlayContent { case swipeToReply(title: String, text: String) case actionSucceeded(title: String?, text: String, cancel: String?, destructive: Bool) case stickersModified(title: String, text: String, undo: Bool, info: StickerPackCollectionInfo, topItem: StickerPackItem?, context: AccountContext) - case dice(dice: TelegramMediaDice, context: AccountContext, text: String, action: String?) + case dice(dice: TelegramMediaDice, context: AccountContext, text: String, action: String?, changeAction: String?) case chatAddedToFolder(context: AccountContext, chatTitle: String, folderTitle: NSAttributedString) case chatRemovedFromFolder(context: AccountContext, chatTitle: String, folderTitle: NSAttributedString) case messagesUnpinned(title: String, text: String, undo: Bool, isHidden: Bool) diff --git a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift index 44be88e5..0360f3cf 100644 --- a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift +++ b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift @@ -53,6 +53,10 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { private let buttonNode: HighlightTrackingButtonNode private let undoButtonTextNode: ImmediateTextNode private let undoButtonNode: HighlightTrackingButtonNode + + private let changeButtonBackground: ASImageNode + private let changeButtonTextNode: ImmediateTextNode + private let panelNode: ASDisplayNode private let panelWrapperNode: ASDisplayNode private let action: (UndoOverlayAction) -> Bool @@ -103,6 +107,14 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { self.buttonNode = HighlightTrackingButtonNode() + self.changeButtonBackground = ASImageNode() + self.changeButtonBackground.displaysAsynchronously = false + self.changeButtonBackground.isUserInteractionEnabled = false + + self.changeButtonTextNode = ImmediateTextNode() + self.changeButtonTextNode.displaysAsynchronously = false + self.changeButtonTextNode.isUserInteractionEnabled = false + var displayUndo = true var undoText = presentationData.strings.Undo_Undo var undoTextColor = presentationData.theme.list.itemAccentColor.withMultiplied(hue: 0.933, saturation: 0.61, brightness: 1.0) @@ -624,7 +636,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: context.account, resource: resource._asResource(), isVideo: isVideo), width: 80, height: 80, mode: .direct(cachePathPrefix: nil)) } } - case let .dice(dice, context, text, action): + case let .dice(dice, context, text, action, changeAction): self.avatarNode = nil self.iconNode = nil self.iconCheckNode = nil @@ -633,7 +645,11 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white) let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white) let link = MarkdownAttributeSet(font: Font.regular(14.0), textColor: undoTextColor) - let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { _ in return nil }), textAlignment: .natural) + let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { _ in return nil }), textAlignment: .natural).mutableCopy() as! NSMutableAttributedString + if let range = attributedText.string.range(of: "$"), let icon = generateTintedImage(image: UIImage(bundleImageName: "Ads/TonMedium"), color: .white) { + attributedText.addAttribute(.attachment, value: icon, range: NSRange(range, in: attributedText.string)) + attributedText.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: attributedText.string)) + } self.textNode.attributedText = attributedText if let action = action { displayUndo = true @@ -651,6 +667,11 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { default: break } + + if let changeAction { + self.changeButtonTextNode.attributedText = NSAttributedString(string: changeAction, font: Font.regular(12.0), textColor: undoTextColor) + self.changeButtonBackground.image = generateStretchableFilledCircleImage(diameter: 18.0, color: undoTextColor.withMultipliedAlpha(0.1)) + } if dice.emoji == "🎰" { let slotMachineNode = SlotMachineAnimationNode(account: context.account, size: CGSize(width: 42.0, height: 42.0)) @@ -670,8 +691,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { switch stickerPack { case let .result(_, items, _): let item = items[Int(value)] - - animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: context.account, resource: item.file._parse().resource), width: 120, height: 120, playbackMode: .once, mode: .direct(cachePathPrefix: nil)) + animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: context.account, resource: item.file._parse().resource), width: 120, height: 120, playbackMode: .still(.end), mode: .direct(cachePathPrefix: nil)) default: break } @@ -1553,7 +1573,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { self.panelNode.backgroundColor = .clear } self.panelNode.clipsToBounds = true - self.panelNode.cornerRadius = 14.0 + self.panelNode.cornerRadius = 25.0 self.panelWrapperNode = ASDisplayNode() @@ -1625,6 +1645,10 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { self.panelWrapperNode.addSubnode(self.undoButtonTextNode) self.panelWrapperNode.addSubnode(self.undoButtonNode) } + if self.changeButtonBackground.image != nil { + self.panelWrapperNode.addSubnode(self.changeButtonBackground) + self.panelWrapperNode.addSubnode(self.changeButtonTextNode) + } self.addSubnode(self.panelNode) self.addSubnode(self.panelWrapperNode) @@ -1962,7 +1986,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { } contentHeight += textSize.height - contentHeight = max(49.0, contentHeight) + contentHeight = max(50.0, contentHeight) var insets = layout.insets(options: [.input]) switch self.placementPosition { @@ -2024,6 +2048,13 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { transition.updateFrame(node: self.textNode, frame: textFrame) } + if self.changeButtonTextNode.supernode != nil { + let changeButtonTextSize = self.changeButtonTextNode.updateLayout(CGSize(width: 200.0, height: .greatestFiniteMagnitude)) + let changeButtonFrame = CGRect(origin: CGPoint(x: textFrame.maxX + 10.0, y: floorToScreenPixels(textFrame.midY - changeButtonTextSize.height * 0.5)), size: changeButtonTextSize) + transition.updateFrame(node: self.changeButtonTextNode, frame: changeButtonFrame) + transition.updateFrame(node: self.changeButtonBackground, frame: changeButtonFrame.insetBy(dx: -6.0, dy: -2.0)) + } + if let iconNode = self.iconNode { let iconSize: CGSize if let size = self.iconImageSize { diff --git a/submodules/UrlHandling/Sources/UrlHandling.swift b/submodules/UrlHandling/Sources/UrlHandling.swift index f691900a..82ace727 100644 --- a/submodules/UrlHandling/Sources/UrlHandling.swift +++ b/submodules/UrlHandling/Sources/UrlHandling.swift @@ -85,6 +85,7 @@ public enum ParsedInternalPeerUrlParameter { case boost case text(String) case profile + case direct case referrer(String) case storyFolder(Int64) case giftCollection(Int64) @@ -393,6 +394,8 @@ public func parseInternalUrl(sharedContext: SharedAccountContext, context: Accou return .peer(.name(peerName), .boost) } else if queryItem.name == "profile" { return .peer(.name(peerName), .profile) + } else if queryItem.name == "direct" { + return .peer(.name(peerName), .direct) } else if queryItem.name == "startapp" { var mode: ResolvedStartAppMode = .generic if let queryItems = components.queryItems { @@ -836,6 +839,26 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) switch parameter { case .profile: return .single(.result(.peer(peer._asPeer(), .info(nil)))) + case .direct: + if case let .channel(channel) = peer, let monoforumId = channel.linkedMonoforumId { + return context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: monoforumId)) + |> mapToSignal { peer -> Signal in + if let peer { + return .single(peer) + } else { + return context.engine.peers.findChannelById(channelId: monoforumId.id._internalGetInt64Value()) + } + } + |> map { peer -> ResolveInternalUrlResult in + if let peer { + return .result(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))) + } else { + return .result(.peer(nil, .info(nil))) + } + } + } else { + return .single(.result(.peer(nil, .info(nil)))) + } case let .text(text): var textInputState: ChatTextInputState? if !text.isEmpty { @@ -1238,9 +1261,9 @@ public func isTelegramMeLink(_ url: String) -> Bool { public func isTelegraPhLink(_ url: String) -> Bool { let schemes = ["http://", "https://", ""] - for basePath in baseTelegramMePaths { + for basePath in baseTelegraPhPaths { for scheme in schemes { - let basePrefix = scheme + basePath + "/" + let basePrefix = scheme + basePath if url.lowercased().hasPrefix(basePrefix) { return true } diff --git a/submodules/Utils/DeviceModel/Sources/DeviceModel.swift b/submodules/Utils/DeviceModel/Sources/DeviceModel.swift index c2392f3d..2717d827 100644 --- a/submodules/Utils/DeviceModel/Sources/DeviceModel.swift +++ b/submodules/Utils/DeviceModel/Sources/DeviceModel.swift @@ -52,7 +52,12 @@ public enum DeviceModel: CaseIterable, Equatable { .iPhone16, .iPhone16Plus, .iPhone16Pro, - .iPhone16ProMax + .iPhone16ProMax, + .iPhone16e, + .iPhone17, + .iPhone17Pro, + .iPhone17ProMax, + .iPhoneAir ] } diff --git a/submodules/WallpaperBackgroundNode/BUILD b/submodules/WallpaperBackgroundNode/BUILD index ccd9dff8..ccba786f 100644 --- a/submodules/WallpaperBackgroundNode/BUILD +++ b/submodules/WallpaperBackgroundNode/BUILD @@ -68,6 +68,9 @@ swift_library( "//submodules/AnimatedStickerNode:AnimatedStickerNode", "//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode", "//submodules/Components/HierarchyTrackingLayer:HierarchyTrackingLayer", + "//submodules/TelegramUI/Components/EdgeEffect", + "//submodules/ComponentFlow", + "//submodules/Components/ComponentDisplayAdapters", ], visibility = [ "//visibility:public", diff --git a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift index 8c96748f..80b974c6 100644 --- a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift +++ b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift @@ -16,6 +16,7 @@ import AppBundle import AnimatedStickerNode import TelegramAnimatedStickerNode import HierarchyTrackingLayer +import EdgeEffect private let motionAmount: CGFloat = 32.0 @@ -32,7 +33,7 @@ private func generateBlurredContents(image: UIImage, dimColor: UIColor?) -> UIIm telegramFastBlurMore(Int32(context.size.width), Int32(context.size.height), Int32(context.bytesPerRow), context.bytes) adjustSaturationInContext(context: context, saturation: 1.7) - + if let dimColor { context.withFlippedContext { c in c.setFillColor(dimColor.cgColor) @@ -43,6 +44,64 @@ private func generateBlurredContents(image: UIImage, dimColor: UIColor?) -> UIIm return context.generateImage() } +private func calculateWallpaperBrightness(from colors: [UInt32]) -> CGFloat { + guard !colors.isEmpty else { + return 1.0 + } + return UIColor.average(of: colors.map(UIColor.init(rgb:))).hsb.b +} + +private func calculateWallpaperBrightness(from image: UIImage) -> CGFloat { + guard let cgImage = image.cgImage else { + return 1.0 + } + + let sourceWidth = cgImage.width + let sourceHeight = cgImage.height + let topRegionHeight = max(1, Int(CGFloat(sourceHeight) * 0.1)) + let cropRect = CGRect(x: 0, y: 0, width: sourceWidth, height: topRegionHeight) + + guard let croppedImage = cgImage.cropping(to: cropRect) else { + return 1.0 + } + + let targetSize = CGSize(width: 10.0, height: 10.0) + let width = Int(targetSize.width) + let height = Int(targetSize.height) + let bytesPerPixel = 4 + let bytesPerRow = bytesPerPixel * width + let bitsPerComponent = 8 + + var pixelData = [UInt8](repeating: 0, count: width * height * bytesPerPixel) + + guard let context = CGContext( + data: &pixelData, + width: width, + height: height, + bitsPerComponent: bitsPerComponent, + bytesPerRow: bytesPerRow, + space: CGColorSpaceCreateDeviceRGB(), + bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue + ) else { + return 1.0 + } + + context.draw(croppedImage, in: CGRect(origin: .zero, size: targetSize)) + + var totalLuminance: CGFloat = 0.0 + let pixelCount = width * height + + for i in 0 ..< pixelCount { + let offset = i * bytesPerPixel + let r = CGFloat(pixelData[offset]) / 255.0 + let g = CGFloat(pixelData[offset + 1]) / 255.0 + let b = CGFloat(pixelData[offset + 2]) / 255.0 + totalLuminance += 0.299 * r + 0.587 * g + 0.114 * b + } + + return totalLuminance / CGFloat(pixelCount) +} + public enum WallpaperBubbleType { case incoming case outgoing @@ -97,12 +156,14 @@ public struct WallpaperEdgeEffectEdge: Equatable { } public protocol WallpaperEdgeEffectNode: ASDisplayNode { - func update(rect: CGRect, edge: WallpaperEdgeEffectEdge, containerSize: CGSize, transition: ContainedViewLayoutTransition) + func update(rect: CGRect, edge: WallpaperEdgeEffectEdge, blur: Bool, containerSize: CGSize, transition: ContainedViewLayoutTransition) } public protocol WallpaperBackgroundNode: ASDisplayNode { var isReady: Signal { get } var rotation: CGFloat { get set } + var isDark: Bool? { get } + var isDarkUpdated: (() -> Void)? { get set } func update(wallpaper: TelegramWallpaper, animated: Bool) func update(wallpaper: TelegramWallpaper, starGift: StarGift?, animated: Bool) @@ -122,7 +183,7 @@ public protocol WallpaperBackgroundNode: ASDisplayNode { func makeEdgeEffectNode() -> WallpaperEdgeEffectNode? } -private final class EffectImageLayer: SimpleLayer, GradientBackgroundPatternOverlayLayer { +final class EffectImageLayer: SimpleLayer, GradientBackgroundPatternOverlayLayer { final class CloneLayer: SimpleLayer { private weak var parentLayer: EffectImageLayer? private var index: SparseBag>.Index? @@ -785,9 +846,9 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou private let context: AccountContext private let useSharedAnimationPhase: Bool - private let contentNode: ASDisplayNode + let contentNode: ASDisplayNode - fileprivate let edgeEffectNodes = SparseBag>() + let edgeEffectNodes = SparseBag>() private var blurredBackgroundContents: UIImage? @@ -836,9 +897,9 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou } } - fileprivate var gradientBackgroundNode: GradientBackgroundNode? + var gradientBackgroundNode: GradientBackgroundNode? private var outgoingBubbleGradientBackgroundNode: GradientBackgroundNode? - fileprivate let patternImageLayer: EffectImageLayer + let patternImageLayer: EffectImageLayer private let dimLayer: SimpleLayer private var isGeneratingPatternImage: Bool = false @@ -980,11 +1041,21 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou } private static var cachedSharedPattern: (PatternKey, UIImage)? + public private(set) var isDark: Bool? + public var isDarkUpdated: (() -> Void)? + + private func updateIsDark(_ isDark: Bool?) { + if self.isDark != isDark { + self.isDark = isDark + self.isDarkUpdated?() + } + } + private let _isReady = ValuePromise(false, ignoreRepeated: true) public var isReady: Signal { return self._isReady.get() } - + init(context: AccountContext, useSharedAnimationPhase: Bool) { self.context = context self.useSharedAnimationPhase = useSharedAnimationPhase @@ -1132,6 +1203,12 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou self.blurredBackgroundContents = nil self.motionEnabled = false self.wallpaperDisposable.set(nil) + + if case let .file(file) = wallpaper, file.isPattern { + self.updateIsDark(nil) + } else { + self.updateIsDark(calculateWallpaperBrightness(from: gradientColors) <= 0.3) + } } else { if let gradientBackgroundNode = self.gradientBackgroundNode { self.gradientBackgroundNode = nil @@ -1160,17 +1237,20 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou self.contentNode.contents = image?.cgImage self.blurredBackgroundContents = image self.wallpaperDisposable.set(nil) + self.updateIsDark(calculateWallpaperBrightness(from: gradientColors) <= 0.3) } else if gradientColors.count >= 1 { self.contentNode.backgroundColor = UIColor(rgb: gradientColors[0]) self.contentNode.contents = nil self.blurredBackgroundContents = nil self.wallpaperDisposable.set(nil) + self.updateIsDark(calculateWallpaperBrightness(from: gradientColors) <= 0.3) } else { self.contentNode.backgroundColor = .white if let image = chatControllerBackgroundImage(theme: nil, wallpaper: wallpaper, mediaBox: self.context.sharedContext.accountManager.mediaBox, knockoutMode: false) { self.contentNode.contents = image.cgImage self.blurredBackgroundContents = generateBlurredContents(image: image, dimColor: wallpaperDimColor) self.wallpaperDisposable.set(nil) + self.updateIsDark(calculateWallpaperBrightness(from: image) <= 0.55) Queue.mainQueue().justDispatch { self._isReady.set(true) } @@ -1178,6 +1258,7 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou self.contentNode.contents = image.cgImage self.blurredBackgroundContents = generateBlurredContents(image: image, dimColor: wallpaperDimColor) self.wallpaperDisposable.set(nil) + self.updateIsDark(calculateWallpaperBrightness(from: image) <= 0.55) Queue.mainQueue().justDispatch { self._isReady.set(true) } @@ -1190,10 +1271,16 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou strongSelf.contentNode.contents = image?.0?.cgImage if let image = image?.0 { strongSelf.blurredBackgroundContents = generateBlurredContents(image: image, dimColor: wallpaperDimColor) + strongSelf.updateIsDark(calculateWallpaperBrightness(from: image) <= 0.55) } else { strongSelf.blurredBackgroundContents = nil } strongSelf.updateBubbles() + for edgeEffectNode in strongSelf.edgeEffectNodes { + if let edgeEffectNode = edgeEffectNode.value { + edgeEffectNode.updateContents() + } + } strongSelf._isReady.set(true) })) } @@ -1227,8 +1314,13 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou } } self.updateBubbles() - self.updateDimming() + + for edgeEffectNode in self.edgeEffectNodes { + if let edgeEffectNode = edgeEffectNode.value { + edgeEffectNode.updateContents() + } + } } public func _internalUpdateIsSettingUpWallpaper() { @@ -1792,156 +1884,6 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou } } -private final class WallpaperEdgeEffectNodeImpl: ASDisplayNode, WallpaperEdgeEffectNode { - private struct Params: Equatable { - let rect: CGRect - let edge: WallpaperEdgeEffectEdge - let containerSize: CGSize - - init(rect: CGRect, edge: WallpaperEdgeEffectEdge, containerSize: CGSize) { - self.rect = rect - self.edge = edge - self.containerSize = containerSize - } - } - - private var gradientNode: GradientBackgroundNode.CloneNode? - private let patternImageLayer: EffectImageLayer.CloneLayer - - private let containerNode: ASDisplayNode - private let containerMaskingNode: ASDisplayNode - private let overlayNode: ASDisplayNode - private let maskView: UIImageView - - private weak var parentNode: WallpaperBackgroundNodeImpl? - private var index: Int? - private var params: Params? - - private var isInverted: Bool = false - - init(parentNode: WallpaperBackgroundNodeImpl) { - self.parentNode = parentNode - - if let gradientBackgroundNode = parentNode.gradientBackgroundNode { - self.gradientNode = GradientBackgroundNode.CloneNode(parentNode: gradientBackgroundNode, isDimmed: false) - } else { - self.gradientNode = nil - } - - self.patternImageLayer = EffectImageLayer.CloneLayer(parentLayer: parentNode.patternImageLayer) - - self.containerNode = ASDisplayNode() - self.containerNode.anchorPoint = CGPoint() - self.containerNode.clipsToBounds = true - - self.containerMaskingNode = ASDisplayNode() - self.containerMaskingNode.addSubnode(self.containerNode) - - self.overlayNode = ASDisplayNode() - - self.maskView = UIImageView() - - super.init() - - if let gradientNode = self.gradientNode { - self.containerNode.addSubnode(gradientNode) - } - //self.layer.addSublayer(self.patternImageLayer) - - self.addSubnode(self.containerMaskingNode) - self.containerMaskingNode.view.mask = self.maskView - - self.containerNode.addSubnode(self.overlayNode) - - self.index = parentNode.edgeEffectNodes.add(Weak(self)) - } - - deinit { - if let index = self.index, let parentNode = self.parentNode { - parentNode.edgeEffectNodes.remove(index) - } - } - - func updateGradientNode() { - if let gradientBackgroundNode = self.parentNode?.gradientBackgroundNode { - if self.gradientNode == nil { - let gradientNode = GradientBackgroundNode.CloneNode(parentNode: gradientBackgroundNode, isDimmed: false) - self.gradientNode = gradientNode - self.containerNode.insertSubnode(gradientNode, at: 0) - - if let params = self.params { - self.updateImpl(rect: params.rect, edge: params.edge, containerSize: params.containerSize, transition: .immediate) - } - } - } else { - if let gradientNode = self.gradientNode { - self.gradientNode = nil - gradientNode.removeFromSupernode() - } - } - } - - func updatePattern(isInverted: Bool) { - if self.isInverted != isInverted { - self.isInverted = isInverted - - self.overlayNode.backgroundColor = isInverted ? .black : .clear - } - } - - func update(rect: CGRect, edge: WallpaperEdgeEffectEdge, containerSize: CGSize, transition: ContainedViewLayoutTransition) { - let params = Params(rect: rect, edge: edge, containerSize: containerSize) - if self.params != params { - self.params = params - self.updateImpl(rect: params.rect, edge: params.edge, containerSize: params.containerSize, transition: transition) - } - } - - private func updateImpl(rect: CGRect, edge: WallpaperEdgeEffectEdge, containerSize: CGSize, transition: ContainedViewLayoutTransition) { - transition.updateFrame(node: self.containerMaskingNode, frame: CGRect(origin: CGPoint(), size: rect.size)) - transition.updateBounds(node: self.containerNode, bounds: CGRect(origin: CGPoint(x: rect.minX, y: rect.minY), size: rect.size)) - - if self.maskView.image?.size.height != edge.size { - let baseGradientAlpha: CGFloat = 0.75 - let numSteps = 8 - let firstStep = 1 - let firstLocation = 0.0 - let colors: [UIColor] = (0 ..< numSteps).map { i in - if i < firstStep { - return UIColor(white: 1.0, alpha: 1.0) - } else { - let step: CGFloat = CGFloat(i - firstStep) / CGFloat(numSteps - firstStep - 1) - let value: CGFloat = bezierPoint(0.42, 0.0, 0.58, 1.0, step) - return UIColor(white: 1.0, alpha: baseGradientAlpha * value) - } - } - let locations: [CGFloat] = (0 ..< numSteps).map { i in - if i < firstStep { - return 0.0 - } else { - let step: CGFloat = CGFloat(i - firstStep) / CGFloat(numSteps - firstStep - 1) - return (firstLocation + (1.0 - firstLocation) * step) - } - } - - self.maskView.image = generateGradientImage( - size: CGSize(width: 8.0, height: edge.size), - colors: colors, - locations: locations - )?.stretchableImage(withLeftCapWidth: 0, topCapHeight: Int(edge.size)) - } - - transition.updateFrame(view: self.maskView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: rect.size)) - - transition.updateFrame(node: self.overlayNode, frame: CGRect(origin: CGPoint(), size: containerSize)) - - if let gradientNode = self.gradientNode { - transition.updateFrame(node: gradientNode, frame: CGRect(origin: CGPoint(), size: containerSize)) - } - transition.updateFrame(layer: self.patternImageLayer, frame: CGRect(origin: CGPoint(), size: containerSize)) - } -} - private protocol WallpaperComponentView: AnyObject { var view: UIView { get } diff --git a/submodules/WallpaperBackgroundNode/Sources/WallpaperEdgeEffectNodeImpl.swift b/submodules/WallpaperBackgroundNode/Sources/WallpaperEdgeEffectNodeImpl.swift new file mode 100644 index 00000000..8898e7f8 --- /dev/null +++ b/submodules/WallpaperBackgroundNode/Sources/WallpaperEdgeEffectNodeImpl.swift @@ -0,0 +1,228 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import GradientBackground +import EdgeEffect +import SwiftSignalKit +import ComponentFlow +import ComponentDisplayAdapters + +final class WallpaperEdgeEffectNodeImpl: ASDisplayNode, WallpaperEdgeEffectNode { + private struct Params: Equatable { + let rect: CGRect + let edge: WallpaperEdgeEffectEdge + let blur: Bool + let containerSize: CGSize + + init(rect: CGRect, edge: WallpaperEdgeEffectEdge, blur: Bool, containerSize: CGSize) { + self.rect = rect + self.edge = edge + self.blur = blur + self.containerSize = containerSize + } + } + + private var gradientNode: GradientBackgroundNode.CloneNode? + private let patternImageLayer: EffectImageLayer.CloneLayer + private let contentNode: ASDisplayNode + + private let containerNode: ASDisplayNode + private let containerMaskingNode: ASDisplayNode + private let overlayNode: ASDisplayNode + private let maskView: UIImageView + + private var blurView: VariableBlurView? + + private weak var parentNode: WallpaperBackgroundNodeImpl? + private var index: Int? + private var params: Params? + + private var isInverted: Bool = false + + init(parentNode: WallpaperBackgroundNodeImpl) { + self.parentNode = parentNode + + if let gradientBackgroundNode = parentNode.gradientBackgroundNode { + self.gradientNode = GradientBackgroundNode.CloneNode(parentNode: gradientBackgroundNode, isDimmed: false) + } else { + self.gradientNode = nil + } + + self.patternImageLayer = EffectImageLayer.CloneLayer(parentLayer: parentNode.patternImageLayer) + + self.contentNode = ASDisplayNode() + + self.containerNode = ASDisplayNode() + self.containerNode.anchorPoint = CGPoint() + self.containerNode.clipsToBounds = true + + self.containerMaskingNode = ASDisplayNode() + self.containerMaskingNode.addSubnode(self.containerNode) + + self.overlayNode = ASDisplayNode() + + self.maskView = UIImageView() + + super.init() + + self.containerNode.addSubnode(self.contentNode) + if let gradientNode = self.gradientNode { + self.containerNode.addSubnode(gradientNode) + } + //self.containerMaskingNode.layer.addSublayer(self.patternImageLayer) + + self.addSubnode(self.containerMaskingNode) + self.containerMaskingNode.view.mask = self.maskView + + self.containerNode.addSubnode(self.overlayNode) + + self.index = parentNode.edgeEffectNodes.add(Weak(self)) + } + + deinit { + if let index = self.index, let parentNode = self.parentNode { + parentNode.edgeEffectNodes.remove(index) + } + } + + func updateGradientNode() { + if let gradientBackgroundNode = self.parentNode?.gradientBackgroundNode { + if self.gradientNode == nil { + let gradientNode = GradientBackgroundNode.CloneNode(parentNode: gradientBackgroundNode, isDimmed: false) + self.gradientNode = gradientNode + self.containerNode.insertSubnode(gradientNode, at: 0) + + if let params = self.params { + self.updateImpl(rect: params.rect, edge: params.edge, blur: params.blur, containerSize: params.containerSize, transition: .immediate) + } + } + } else { + if let gradientNode = self.gradientNode { + self.gradientNode = nil + gradientNode.removeFromSupernode() + } + } + } + + func updatePattern(isInverted: Bool) { + if self.isInverted != isInverted { + self.isInverted = isInverted + + self.overlayNode.backgroundColor = isInverted ? .black : .clear + } + } + + func updateContents() { + guard let parentNode = self.parentNode else { + return + } + self.contentNode.contents = parentNode.contentNode.contents + self.contentNode.backgroundColor = parentNode.contentNode.backgroundColor + self.contentNode.alpha = parentNode.contentNode.alpha + self.contentNode.isHidden = parentNode.contentNode.isHidden + } + + func update(rect: CGRect, edge: WallpaperEdgeEffectEdge, blur: Bool, containerSize: CGSize, transition: ContainedViewLayoutTransition) { + let params = Params(rect: rect, edge: edge, blur: blur, containerSize: containerSize) + if self.params != params { + self.params = params + self.updateImpl(rect: params.rect, edge: params.edge, blur: params.blur, containerSize: params.containerSize, transition: transition) + } + } + + private func updateImpl(rect: CGRect, edge: WallpaperEdgeEffectEdge, blur: Bool, containerSize: CGSize, transition: ContainedViewLayoutTransition) { + transition.updateFrame(node: self.containerMaskingNode, frame: CGRect(origin: CGPoint(), size: rect.size)) + transition.updateBounds(node: self.containerNode, bounds: CGRect(origin: CGPoint(x: rect.minX, y: rect.minY), size: rect.size)) + + if self.maskView.image?.size.height != edge.size { + let baseAlpha: CGFloat = 0.8 + let expSteps = 6 + let totalSteps = 18 + let expEndValue: CGFloat = 0.6 + + var colors: [UIColor] = [] + for i in 0 ..< expSteps { + let step = CGFloat(i) / CGFloat(expSteps - 1) + colors.append(UIColor(white: 1.0, alpha: bezierPoint(0.42, 0.0, 0.58, 1.0, step) * expEndValue)) + } + for i in 0 ..< (totalSteps - expSteps) { + let step = CGFloat(i) / CGFloat((totalSteps - expSteps) - 1) + colors.append(UIColor(white: 1.0, alpha: expEndValue * (1.0 - step) + 1.0 * step)) + } + + let locations: [CGFloat] = (0 ..< colors.count).map { i in + return CGFloat(i) / CGFloat(colors.count - 1) + } + + self.maskView.image = generateGradientImage( + size: CGSize(width: 8.0, height: edge.size), + colors: colors.map { $0.withMultipliedAlpha(baseAlpha) }, + locations: locations + )?.stretchableImage(withLeftCapWidth: 0, topCapHeight: Int(edge.size)) + } + + let maskFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: rect.size) + ComponentTransition(transition).setPosition(view: self.maskView, position: maskFrame.center) + ComponentTransition(transition).setBounds(view: self.maskView, bounds: CGRect(origin: CGPoint(), size: maskFrame.size)) + if case .top = edge.edge { + self.maskView.transform = CGAffineTransformMakeScale(1.0, -1.0) + } else { + self.maskView.transform = CGAffineTransformIdentity + } + + transition.updateFrame(node: self.overlayNode, frame: CGRect(origin: CGPoint(), size: containerSize)) + + if let gradientNode = self.gradientNode { + transition.updateFrame(node: gradientNode, frame: CGRect(origin: CGPoint(), size: containerSize)) + } + transition.updateFrame(layer: self.patternImageLayer, frame: CGRect(origin: CGPoint(x: -rect.minX, y: -rect.minY), size: containerSize)) + + transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: containerSize)) + + if blur { + let blurView: VariableBlurView + if let current = self.blurView { + blurView = current + } else { + let gradientMaskLayer = SimpleGradientLayer() + let baseGradientAlpha: CGFloat = 1.0 + let numSteps = 8 + let firstStep = 1 + let firstLocation = 0.8 + gradientMaskLayer.colors = (0 ..< numSteps).map { i in + if i < firstStep { + return UIColor(white: 1.0, alpha: 1.0).cgColor + } else { + let step: CGFloat = CGFloat(i - firstStep) / CGFloat(numSteps - firstStep - 1) + let value: CGFloat = 1.0 - bezierPoint(0.42, 0.0, 0.58, 1.0, step) + return UIColor(white: 1.0, alpha: baseGradientAlpha * value).cgColor + } + } + gradientMaskLayer.locations = (0 ..< numSteps).map { i -> NSNumber in + if i < firstStep { + return 0.0 as NSNumber + } else { + let step: CGFloat = CGFloat(i - firstStep) / CGFloat(numSteps - firstStep - 1) + return (firstLocation + (1.0 - firstLocation) * step) as NSNumber + } + } + + blurView = VariableBlurView(gradientMask: self.maskView.image ?? UIImage(), maxBlurRadius: 8.0) + blurView.layer.mask = gradientMaskLayer + self.view.insertSubview(blurView, at: 0) + self.blurView = blurView + } + blurView.update(size: bounds.size, transition: transition) + transition.updateFrame(view: blurView, frame: bounds) + if let maskLayer = blurView.layer.mask { + transition.updateFrame(layer: maskLayer, frame: bounds) + maskLayer.transform = CATransform3DMakeScale(1.0, -1.0, 1.0) + } + blurView.transform = self.maskView.transform + } else if let blurView = self.blurView { + self.blurView = nil + blurView.removeFromSuperview() + } + } +} diff --git a/submodules/WebSearchUI/Sources/WebSearchGalleryController.swift b/submodules/WebSearchUI/Sources/WebSearchGalleryController.swift index 28452c54..dba4d511 100644 --- a/submodules/WebSearchUI/Sources/WebSearchGalleryController.swift +++ b/submodules/WebSearchUI/Sources/WebSearchGalleryController.swift @@ -60,7 +60,7 @@ final class WebSearchGalleryControllerPresentationArguments { } class WebSearchGalleryController: ViewController { - private static let navigationTheme = NavigationBarTheme(buttonColor: .white, disabledButtonColor: UIColor(rgb: 0x525252), primaryTextColor: .white, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) + private static let navigationTheme = NavigationBarTheme(overallDarkAppearance: false, buttonColor: .white, disabledButtonColor: UIColor(rgb: 0x525252), primaryTextColor: .white, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) private var galleryNode: GalleryControllerNode { return self.displayNode as! GalleryControllerNode diff --git a/submodules/WebSearchUI/Sources/WebSearchNavigationContentNode.swift b/submodules/WebSearchUI/Sources/WebSearchNavigationContentNode.swift index 16649aa3..f7af0678 100644 --- a/submodules/WebSearchUI/Sources/WebSearchNavigationContentNode.swift +++ b/submodules/WebSearchUI/Sources/WebSearchNavigationContentNode.swift @@ -22,7 +22,7 @@ final class WebSearchNavigationContentNode: NavigationBarContentNode { self.theme = theme self.strings = strings - self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: theme, hasSeparator: false), strings: strings, fieldStyle: .modern, displayBackground: !attachment) + self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: theme, hasSeparator: false), presentationTheme: theme, strings: strings, fieldStyle: .modern, displayBackground: !attachment) self.searchBar.hasCancelButton = attachment self.searchBar.placeholderString = NSAttributedString(string: attachment ? strings.Attachment_SearchWeb : strings.Common_Search, font: searchBarFont, textColor: theme.rootController.navigationSearchBar.inputPlaceholderTextColor) @@ -54,10 +54,12 @@ final class WebSearchNavigationContentNode: NavigationBarContentNode { return 56.0 } - override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) { + override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { let searchBarFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - self.nominalHeight), size: CGSize(width: size.width, height: 56.0)) self.searchBar.frame = searchBarFrame self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition) + + return size } func activate(select: Bool = false) { diff --git a/submodules/WebSearchUI/Sources/WebSearchRecentQueryItem.swift b/submodules/WebSearchUI/Sources/WebSearchRecentQueryItem.swift index 071baf87..a6f0d79f 100644 --- a/submodules/WebSearchUI/Sources/WebSearchRecentQueryItem.swift +++ b/submodules/WebSearchUI/Sources/WebSearchRecentQueryItem.swift @@ -89,7 +89,7 @@ class WebSearchRecentQueryItemNode: ItemListRevealOptionsItemNode { self.highlightedBackgroundNode = ASDisplayNode() self.highlightedBackgroundNode.isLayerBacked = true - super.init(layerBacked: false, dynamicBounce: false, rotated: false, seeThrough: false) + super.init(layerBacked: false, rotated: false, seeThrough: false) self.addSubnode(self.backgroundNode) self.addSubnode(self.separatorNode) diff --git a/submodules/WebUI/BUILD b/submodules/WebUI/BUILD index a812278b..23c32514 100644 --- a/submodules/WebUI/BUILD +++ b/submodules/WebUI/BUILD @@ -57,6 +57,10 @@ swift_library( "//submodules/TelegramUI/Components/GlassBarButtonComponent", "//submodules/Components/BundleIconComponent", "//submodules/TelegramUI/Components/LottieComponent", + "//submodules/TelegramUI/Components/AlertComponent", + "//submodules/TelegramUI/Components/AvatarComponent", + "//submodules/TelegramUI/Components/AlertComponent/AlertCheckComponent", + "//submodules/TelegramUI/Components/AlertComponent/AlertTransferHeaderComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/WebUI/Sources/WebAppAddToAttachmentController.swift b/submodules/WebUI/Sources/WebAppAddToAttachmentController.swift new file mode 100644 index 00000000..5c96fd41 --- /dev/null +++ b/submodules/WebUI/Sources/WebAppAddToAttachmentController.swift @@ -0,0 +1,174 @@ +import Foundation +import UIKit +import SwiftSignalKit +import AsyncDisplayKit +import Display +import Postbox +import TelegramCore +import TelegramPresentationData +import TelegramUIPreferences +import AccountContext +import AppBundle +import PhotoResources +import ComponentFlow +import AlertComponent +import AlertCheckComponent +import BundleIconComponent + +public func addWebAppToAttachmentController(context: AccountContext, peerName: String, icons: [AttachMenuBots.Bot.IconName: TelegramMediaFile], requestWriteAccess: Bool, completion: @escaping (Bool) -> Void) -> ViewController { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let strings = presentationData.strings + + let checkState = AlertCheckComponent.ExternalState() + + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "header", + component: AnyComponent( + AlertWebAppAttachmentHeaderComponent(context: context, icons: icons) + ) + )) + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: strings.WebApp_AddToAttachmentTitle) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(strings.WebApp_AddToAttachmentText(peerName).string)) + ) + )) + if requestWriteAccess { + content.append(AnyComponentWithIdentity( + id: "check", + component: AnyComponent( + AlertCheckComponent(title: strings.WebApp_AddToAttachmentAllowMessages(peerName).string, initialValue: false, externalState: checkState) + ) + )) + } + + let alertController = AlertScreen( + context: context, + content: content, + actions: [ + .init(title: strings.Common_Cancel), + .init(title: strings.WebApp_AddToAttachmentAdd, type: .default, action: { + completion(requestWriteAccess && checkState.value) + }) + ] + ) + return alertController +} + +private final class AlertWebAppAttachmentHeaderComponent: Component { + public typealias EnvironmentType = AlertComponentEnvironment + + let context: AccountContext + let icons: [AttachMenuBots.Bot.IconName: TelegramMediaFile] + + public init( + context: AccountContext, + icons: [AttachMenuBots.Bot.IconName: TelegramMediaFile] + ) { + self.context = context + self.icons = icons + } + + public static func ==(lhs: AlertWebAppAttachmentHeaderComponent, rhs: AlertWebAppAttachmentHeaderComponent) -> Bool { + return true + } + + public final class View: UIView { + private let appIcon = ComponentView() + private let icon = ComponentView() + + private var appIconImage: UIImage? + private var appIconDisposable: Disposable? + + private var component: AlertWebAppAttachmentHeaderComponent? + private weak var state: EmptyComponentState? + + deinit { + self.appIconDisposable?.dispose() + } + + func update(component: AlertWebAppAttachmentHeaderComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + if self.component == nil { + var peerIcon: TelegramMediaFile? + if let icon = component.icons[.iOSStatic] { + peerIcon = icon + } else if let icon = component.icons[.default] { + peerIcon = icon + } + + if let peerIcon { + let _ = freeMediaFileInteractiveFetched(account: component.context.account, userLocation: .other, fileReference: .standalone(media: peerIcon)).start() + self.appIconDisposable = (svgIconImageFile(account: component.context.account, fileReference: .standalone(media: peerIcon)) + |> deliverOnMainQueue).start(next: { [weak self] transform in + if let self { + let availableSize = CGSize(width: 48.0, height: 48.0) + let arguments = TransformImageArguments(corners: ImageCorners(), imageSize: availableSize, boundingSize: availableSize, intrinsicInsets: UIEdgeInsets()) + let drawingContext = transform(arguments) + self.appIconImage = drawingContext?.generateImage()?.withRenderingMode(.alwaysTemplate) + + self.state?.updated() + } + }) + } + } + + self.component = component + self.state = state + + let environment = environment[AlertComponentEnvironment.self] + + let appIconSize = CGSize(width: 42.0, height: 42.0) + let _ = self.appIcon.update( + transition: .immediate, + component: AnyComponent( + Image(image: self.appIconImage, tintColor: environment.theme.actionSheet.controlAccentColor) + ), + environment: {}, + containerSize: appIconSize + ) + let iconSize = self.icon.update( + transition: .immediate, + component: AnyComponent( + BundleIconComponent(name: "Chat/Attach Menu/BotPlus", tintColor: environment.theme.actionSheet.controlAccentColor) + ), + environment: {}, + containerSize: availableSize + ) + + let totalWidth: CGFloat = 42.0 + iconSize.width + + let appIconFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - totalWidth) / 2.0) - 2.0, y: 3.0), size: appIconSize) + if let imageView = self.appIcon.view { + if imageView.superview == nil { + self.addSubview(imageView) + } + imageView.frame = appIconFrame + } + + let iconFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - totalWidth) / 2.0) + appIconSize.width, y: 0.0), size: iconSize) + if let imageView = self.icon.view { + if imageView.superview == nil { + self.addSubview(imageView) + } + imageView.frame = iconFrame + } + + return CGSize(width: availableSize.width, height: appIconSize.height + 17.0) + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index 4cee7d24..bc04472f 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -43,6 +43,7 @@ import GlassBarButtonComponent import BundleIconComponent import LottieComponent import CryptoKit +import AlertComponent private let durgerKingBotIds: [Int64] = [5104055776, 2200339955] @@ -685,7 +686,14 @@ public final class WebAppController: ViewController, AttachmentContainable { if let data = try? Data(contentsOf: url), let pass = try? PKPass(data: data) { let passLibrary = PKPassLibrary() if passLibrary.containsPass(pass) { - let alertController = textAlertController(context: self.context, updatedPresentationData: nil, title: nil, text: self.presentationData.strings.WebBrowser_PassExistsError, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_OK, action: {})]) + let alertController = AlertScreen( + context: self.context, + title: nil, + text: self.presentationData.strings.WebBrowser_PassExistsError, + actions: [ + .init(title: self.presentationData.strings.Common_OK, type: .default) + ] + ) self.controller?.present(alertController, in: .window(.root)) } else if let controller = PKAddPassesViewController(pass: pass) { self.controller?.view.window?.rootViewController?.present(controller, animated: true) @@ -761,12 +769,19 @@ public final class WebAppController: ViewController, AttachmentContainable { func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) { var completed = false - let alertController = textAlertController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, title: nil, text: message, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: { - if !completed { - completed = true - completionHandler() - } - })]) + let alertController = AlertScreen( + context: self.context, + title: nil, + text: message, + actions: [ + .init(title: self.presentationData.strings.Common_OK, action: { + if !completed { + completed = true + completionHandler() + } + }) + ] + ) alertController.dismissed = { byOutsideTap in if byOutsideTap { if !completed { @@ -780,17 +795,25 @@ public final class WebAppController: ViewController, AttachmentContainable { func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) { var completed = false - let alertController = textAlertController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, title: nil, text: message, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: { - if !completed { - completed = true - completionHandler(false) - } - }), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: { - if !completed { - completed = true - completionHandler(true) - } - })]) + let alertController = AlertScreen( + context: self.context, + title: nil, + text: message, + actions: [ + .init(title: self.presentationData.strings.Common_Cancel, action: { + if !completed { + completed = true + completionHandler(false) + } + }), + .init(title: self.presentationData.strings.Common_OK, type: .default, action: { + if !completed { + completed = true + completionHandler(true) + } + }) + ] + ) alertController.dismissed = { byOutsideTap in if byOutsideTap { if !completed { @@ -804,24 +827,28 @@ public final class WebAppController: ViewController, AttachmentContainable { func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) { var completed = false - let promptController = promptController(sharedContext: self.context.sharedContext, updatedPresentationData: self.controller?.updatedPresentationData, text: prompt, value: defaultText, apply: { value in - if !completed { - completed = true - if let value = value { - completionHandler(value) - } else { - completionHandler(nil) + let promptController = promptController( + context: self.context, + updatedPresentationData: self.controller?.updatedPresentationData, + text: prompt, + value: defaultText, + apply: { value in + if !completed { + completed = true + if let value = value { + completionHandler(value) + } else { + completionHandler(nil) + } } - } - }) - promptController.dismissed = { byOutsideTap in - if byOutsideTap { + }, + dismissed: { if !completed { completed = true completionHandler(nil) } } - } + ) self.controller?.present(promptController, in: .window(.root)) } @@ -1385,7 +1412,7 @@ public final class WebAppController: ViewController, AttachmentContainable { let presentationData = self.presentationData let title = json["title"] as? String - var alertButtons: [TextAlertAction] = [] + var actions: [AlertScreen.Action] = [] for buttonJson in buttons.reversed() { if let button = buttonJson as? [String: Any], let id = button["id"] as? String, let type = button["type"] as? String { @@ -1395,27 +1422,27 @@ public final class WebAppController: ViewController, AttachmentContainable { let text = button["text"] as? String switch type { case "default": - if let text = text { - alertButtons.append(TextAlertAction(type: .genericAction, title: text, action: { + if let text { + actions.append(AlertScreen.Action(title: text, action: { buttonAction() })) } case "destructive": - if let text = text { - alertButtons.append(TextAlertAction(type: .destructiveAction, title: text, action: { + if let text { + actions.append(AlertScreen.Action(title: text, type: .destructive, action: { buttonAction() })) } case "ok": - alertButtons.append(TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { + actions.append(AlertScreen.Action(title: presentationData.strings.Common_OK, type: .default, action: { buttonAction() })) case "cancel": - alertButtons.append(TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { + actions.append(AlertScreen.Action(title: presentationData.strings.Common_Cancel, action: { buttonAction() })) case "close": - alertButtons.append(TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Close, action: { + actions.append(AlertScreen.Action(title: presentationData.strings.Common_Close, action: { buttonAction() })) default: @@ -1424,12 +1451,18 @@ public final class WebAppController: ViewController, AttachmentContainable { } } - var actionLayout: TextAlertContentActionLayout = .horizontal - if alertButtons.count > 2 { + var actionLayout: AlertScreen.ActionAligmnent = .default + if actions.count > 2 { actionLayout = .vertical - alertButtons = Array(alertButtons.reversed()) + actions = Array(actions.reversed()) } - let alertController = textAlertController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, title: title, text: message, actions: alertButtons, actionLayout: actionLayout) + let alertController = AlertScreen( + context: self.context, + configuration: AlertScreen.Configuration(actionAlignment: actionLayout, dismissOnOutsideTap: true, allowInputInset: false), + title: title, + text: message, + actions: actions + ) alertController.dismissed = { byOutsideTap in if byOutsideTap { self.sendAlertButtonEvent(id: nil) @@ -2113,18 +2146,26 @@ public final class WebAppController: ViewController, AttachmentContainable { if result { sendEvent(true) } else { - let alertController = textAlertController(context: self.context, updatedPresentationData: controller.updatedPresentationData, title: self.presentationData.strings.WebApp_AllowWriteTitle, text: self.presentationData.strings.WebApp_AllowWriteConfirmation(controller.botName).string, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: { - sendEvent(false) - }), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: { [weak self] in - guard let self else { - return - } - - let _ = (self.context.engine.messages.allowBotSendMessages(botId: controller.botId) - |> deliverOnMainQueue).start(completed: { - sendEvent(true) - }) - })], parseMarkdown: true) + let alertController = AlertScreen( + context: self.context, + title: self.presentationData.strings.WebApp_AllowWriteTitle, + text: self.presentationData.strings.WebApp_AllowWriteConfirmation(controller.botName).string, + actions: [ + .init(title: self.presentationData.strings.Common_Cancel, action: { + sendEvent(false) + }), + .init(title: self.presentationData.strings.Common_OK, type: .default, action: { [weak self] in + guard let self else { + return + } + + let _ = (self.context.engine.messages.allowBotSendMessages(botId: controller.botId) + |> deliverOnMainQueue).start(completed: { + sendEvent(true) + }) + }) + ] + ) alertController.dismissed = { byOutsideTap in if byOutsideTap { sendEvent(false) @@ -2166,48 +2207,56 @@ public final class WebAppController: ViewController, AttachmentContainable { text = self.presentationData.strings.WebApp_SharePhoneConfirmation(botName).string } - let alertController = textAlertController(context: self.context, updatedPresentationData: controller.updatedPresentationData, title: self.presentationData.strings.WebApp_SharePhoneTitle, text: text, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: { - sendEvent(false) - }), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: { [weak self] in - guard let self, case let .user(user) = accountPeer, let phone = user.phone, !phone.isEmpty else { - return - } - - let sendMessageSignal = enqueueMessages(account: self.context.account, peerId: botId, messages: [ - .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaContact(firstName: user.firstName ?? "", lastName: user.lastName ?? "", phoneNumber: phone, peerId: user.id, vCardData: nil)), threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []) - ]) - |> mapToSignal { messageIds in - if let maybeMessageId = messageIds.first, let messageId = maybeMessageId { - return context.account.pendingMessageManager.pendingMessageStatus(messageId) - |> mapToSignal { status, _ -> Signal in - if status != nil { - return .never() + let alertController = AlertScreen( + context: self.context, + title: self.presentationData.strings.WebApp_SharePhoneTitle, + text: text, + actions: [ + .init(title: self.presentationData.strings.Common_Cancel, action: { + sendEvent(false) + }), + .init(title: self.presentationData.strings.Common_OK, type: .default, action: { [weak self] in + guard let self, case let .user(user) = accountPeer, let phone = user.phone, !phone.isEmpty else { + return + } + + let sendMessageSignal = enqueueMessages(account: self.context.account, peerId: botId, messages: [ + .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaContact(firstName: user.firstName ?? "", lastName: user.lastName ?? "", phoneNumber: phone, peerId: user.id, vCardData: nil)), threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []) + ]) + |> mapToSignal { messageIds in + if let maybeMessageId = messageIds.first, let messageId = maybeMessageId { + return context.account.pendingMessageManager.pendingMessageStatus(messageId) + |> mapToSignal { status, _ -> Signal in + if status != nil { + return .never() + } else { + return .single(true) + } + } + |> take(1) } else { - return .single(true) + return .complete() } } - |> take(1) - } else { - return .complete() - } - } - - let sendMessage = { - let _ = (sendMessageSignal - |> deliverOnMainQueue).start(completed: { - sendEvent(true) + + let sendMessage = { + let _ = (sendMessageSignal + |> deliverOnMainQueue).start(completed: { + sendEvent(true) + }) + } + + if requiresUnblock { + let _ = (context.engine.privacy.requestUpdatePeerIsBlocked(peerId: botId, isBlocked: false) + |> deliverOnMainQueue).start(completed: { + sendMessage() + }) + } else { + sendMessage() + } }) - } - - if requiresUnblock { - let _ = (context.engine.privacy.requestUpdatePeerIsBlocked(peerId: botId, isBlocked: false) - |> deliverOnMainQueue).start(completed: { - sendMessage() - }) - } else { - sendMessage() - } - })], parseMarkdown: true) + ] + ) alertController.dismissed = { byOutsideTap in if byOutsideTap { sendEvent(false) @@ -2328,14 +2377,21 @@ public final class WebAppController: ViewController, AttachmentContainable { alertText = self.presentationData.strings.WebApp_AlertBiometryAccessText(botPeer.compactDisplayTitle).string } } - controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: alertTitle, text: alertText, actions: [ - TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_No, action: { - updateAccessGranted(false) - }), - TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_Yes, action: { - updateAccessGranted(true) - }) - ], parseMarkdown: false), in: .window(.root)) + + let alertController = AlertScreen( + context: self.context, + title: alertTitle, + text: alertText, + actions: [ + .init(title: self.presentationData.strings.Common_No, action: { + updateAccessGranted(false) + }), + .init(title: self.presentationData.strings.Common_Yes, type: .default, action: { + updateAccessGranted(true) + }) + ] + ) + controller.present(alertController, in: .window(.root)) }) } @@ -2774,17 +2830,23 @@ public final class WebAppController: ViewController, AttachmentContainable { } let text: String = self.presentationData.strings.WebApp_Download_Text(controller.botName, fileName, fileSizeString).string - let alertController = standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: title, text: text, actions: [ - TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: { [weak self] in - let data: JSON = [ - "status": "cancelled" - ] - self?.webView?.sendEvent(name: "file_download_requested", data: data.string) - }), - TextAlertAction(type: .defaultAction, title: self.presentationData.strings.WebApp_Download_Download, action: { [weak self] in - self?.startDownload(url: url, fileName: fileName, fileSize: fileSize, isMedia: isMedia) - }) - ], parseMarkdown: true) + + let alertController = AlertScreen( + context: self.context, + title: title, + text: text, + actions: [ + .init(title: self.presentationData.strings.Common_Cancel, action: { [weak self] in + let data: JSON = [ + "status": "cancelled" + ] + self?.webView?.sendEvent(name: "file_download_requested", data: data.string) + }), + .init(title: self.presentationData.strings.WebApp_Download_Download, type: .default, action: { [weak self] in + self?.startDownload(url: url, fileName: fileName, fileSize: fileSize, isMedia: isMedia) + }) + ] + ) alertController.dismissed = { [weak self] byOutsideTap in let data: JSON = [ "status": "cancelled" @@ -2962,7 +3024,7 @@ public final class WebAppController: ViewController, AttachmentContainable { accountPeer: accountPeer, botName: controller.botName, icons: iconStatusEmoji, - completion: { [weak self] result in + completion: { [weak self] result, byOutsideTap in guard let self, let controller = self.controller else { return } @@ -3017,17 +3079,13 @@ public final class WebAppController: ViewController, AttachmentContainable { self.webView?.sendEvent(name: "emoji_status_access_requested", data: data.string) } - let _ = updateWebAppPermissionsStateInteractively(context: context, peerId: botId) { current in - return WebAppPermissionsState(location: current?.location, emojiStatus: WebAppPermissionsState.EmojiStatus(isRequested: true)) - }.startStandalone() + if !byOutsideTap { + let _ = updateWebAppPermissionsStateInteractively(context: context, peerId: botId) { current in + return WebAppPermissionsState(location: current?.location, emojiStatus: WebAppPermissionsState.EmojiStatus(isRequested: true)) + }.startStandalone() + } } ) - alertController.dismissed = { [weak self] byOutsideTap in - let data: JSON = [ - "status": "cancelled" - ] - self?.webView?.sendEvent(name: "emoji_status_access_requested", data: data.string) - } controller.present(alertController, in: .window(.root)) }) } @@ -3510,7 +3568,7 @@ public final class WebAppController: ViewController, AttachmentContainable { private func updateNavigationButtons() { if case .attachMenu = self.source { - let barButtonSize = CGSize(width: 40.0, height: 40.0) + let barButtonSize = CGSize(width: 44.0, height: 44.0) let closeComponent: AnyComponentWithIdentity = AnyComponentWithIdentity( id: "close", component: AnyComponent(GlassBarButtonComponent( @@ -3521,7 +3579,7 @@ public final class WebAppController: ViewController, AttachmentContainable { component: AnyComponentWithIdentity(id: self.controllerNode.hasBackButton ? "back" : "close", component: AnyComponent( BundleIconComponent( name: self.controllerNode.hasBackButton ? "Navigation/Back" : "Navigation/Close", - tintColor: self.presentationData.theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: self.presentationData.theme.chat.inputPanel.panelControlColor ) )), action: { [weak self] _ in @@ -3542,7 +3600,7 @@ public final class WebAppController: ViewController, AttachmentContainable { content: LottieComponent.AppBundleContent( name: "anim_morewide" ), - color: self.presentationData.theme.rootController.navigationBar.glassBarButtonForegroundColor, + color: self.presentationData.theme.chat.inputPanel.panelControlColor, size: CGSize(width: 34.0, height: 34.0), playOnce: self.moreButtonPlayOnce ) @@ -3621,6 +3679,7 @@ public final class WebAppController: ViewController, AttachmentContainable { if let backgroundColor = self.controllerNode.headerColor, let textColor = self.controllerNode.headerPrimaryTextColor { navigationBarPresentationData = NavigationBarPresentationData( theme: NavigationBarTheme( + overallDarkAppearance: false, buttonColor: textColor, disabledButtonColor: textColor, primaryTextColor: textColor, @@ -3639,7 +3698,7 @@ public final class WebAppController: ViewController, AttachmentContainable { strings: NavigationBarStrings(back: "", close: "") ) } - self.navigationBar?.updatePresentationData(navigationBarPresentationData) + self.navigationBar?.updatePresentationData(navigationBarPresentationData, transition: .immediate) } @objc fileprivate func cancelPressed() { @@ -3857,13 +3916,22 @@ public final class WebAppController: ViewController, AttachmentContainable { private func removeAttachBot() { let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } - self.present(textAlertController(context: context, title: presentationData.strings.WebApp_RemoveConfirmationTitle, text: presentationData.strings.WebApp_RemoveAllConfirmationText(self.botName).string, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { [weak self] in - guard let self else { - return - } - let _ = self.context.engine.messages.removeBotFromAttachMenu(botId: self.botId).start() - self.dismiss() - })], parseMarkdown: true), in: .window(.root)) + let alertController = AlertScreen( + context: self.context, + title: presentationData.strings.WebApp_RemoveConfirmationTitle, + text: presentationData.strings.WebApp_RemoveAllConfirmationText(self.botName).string, + actions: [ + .init(title: presentationData.strings.Common_Cancel), + .init(title: presentationData.strings.Common_OK, type: .default, action: { [weak self] in + guard let self else { + return + } + let _ = self.context.engine.messages.removeBotFromAttachMenu(botId: self.botId).start() + self.dismiss() + }) + ] + ) + self.present(alertController, in: .window(.root)) } override public func loadDisplayNode() { diff --git a/submodules/WebUI/Sources/WebAppEmojiStatusAlertController.swift b/submodules/WebUI/Sources/WebAppEmojiStatusAlertController.swift index 316962fb..ea8dc4ed 100644 --- a/submodules/WebUI/Sources/WebAppEmojiStatusAlertController.swift +++ b/submodules/WebUI/Sources/WebAppEmojiStatusAlertController.swift @@ -4,6 +4,7 @@ import SwiftSignalKit import AsyncDisplayKit import Display import Postbox +import ComponentFlow import TelegramCore import TelegramPresentationData import TelegramUIPreferences @@ -13,352 +14,236 @@ import AvatarNode import EmojiTextAttachmentView import TextFormat import Markdown - -private final class IconsNode: ASDisplayNode { - private let context: AccountContext - private var animationLayer: InlineStickerItemLayer? - - private var files: [TelegramMediaFile.Accessor] - private var currentIndex = 0 - private var switchingToNext = false - - private var timer: SwiftSignalKit.Timer? - - private var currentParams: (size: CGSize, theme: PresentationTheme)? - - init(context: AccountContext, files: [TelegramMediaFile.Accessor]) { - self.context = context - self.files = files - - super.init() - } - - deinit { - self.timer?.invalidate() - } - - func updateLayout(size: CGSize, theme: PresentationTheme) { - self.currentParams = (size, theme) - - if self.timer == nil { - self.timer = SwiftSignalKit.Timer(timeout: 2.5, repeat: true, completion: { [weak self] in - guard let self else { - return - } - self.switchingToNext = true - if let (size, theme) = self.currentParams { - self.updateLayout(size: size, theme: theme) - } - }, queue: Queue.mainQueue()) - self.timer?.start() - } - - let animationLayer: InlineStickerItemLayer - var disappearingAnimationLayer: InlineStickerItemLayer? - if let current = self.animationLayer, !self.switchingToNext { - animationLayer = current - } else { - if self.switchingToNext { - self.currentIndex = (self.currentIndex + 1) % self.files.count - disappearingAnimationLayer = self.animationLayer - self.switchingToNext = false - } - let file = self.files[self.currentIndex]._parse() - let emoji = ChatTextInputTextCustomEmojiAttribute( - interactivelySelectedFromPackId: nil, - fileId: file.fileId.id, - file: file - ) - animationLayer = InlineStickerItemLayer( - context: .account(self.context), - userLocation: .other, - attemptSynchronousLoad: false, - emoji: emoji, - file: file, - cache: self.context.animationCache, - renderer: self.context.animationRenderer, - unique: true, - placeholderColor: theme.list.mediaPlaceholderColor, - pointSize: CGSize(width: 20.0, height: 20.0), - loopCount: 1 - ) - animationLayer.isVisibleForAnimations = true - animationLayer.dynamicColor = theme.actionSheet.controlAccentColor - self.view.layer.addSublayer(animationLayer) - self.animationLayer = animationLayer - - animationLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) - animationLayer.animatePosition(from: CGPoint(x: 0.0, y: 10.0), to: .zero, duration: 0.2, additive: true) - animationLayer.animateScale(from: 0.01, to: 1.0, duration: 0.2) - } - - animationLayer.frame = CGRect(origin: .zero, size: CGSize(width: 20.0, height: 20.0)) - - if let disappearingAnimationLayer { - disappearingAnimationLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in - disappearingAnimationLayer.removeFromSuperlayer() - }) - disappearingAnimationLayer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: -10.0), duration: 0.2, removeOnCompletion: false, additive: true) - disappearingAnimationLayer.animateScale(from: 1.0, to: 0.01, duration: 0.2, removeOnCompletion: false) - } - } -} - -private final class WebAppEmojiStatusAlertContentNode: AlertContentNode { - private let strings: PresentationStrings - private let presentationTheme: PresentationTheme - private let botName: String - - private let textNode: ASTextNode - private let iconBackgroundNode: ASImageNode - private let iconAvatarNode: AvatarNode - private let iconNameNode: ASTextNode - private let iconAnimationNode: IconsNode - - private let actionNodesSeparator: ASDisplayNode - private let actionNodes: [TextAlertContentActionNode] - private let actionVerticalSeparators: [ASDisplayNode] - - private var validLayout: CGSize? - - override var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled - } - - init( - context: AccountContext, - theme: AlertControllerTheme, - ptheme: PresentationTheme, - strings: PresentationStrings, - accountPeer: EnginePeer, - botName: String, - icons: [TelegramMediaFile.Accessor], - actions: [TextAlertAction] - ) { - self.strings = strings - self.presentationTheme = ptheme - self.botName = botName - - self.textNode = ASTextNode() - self.textNode.maximumNumberOfLines = 0 - - self.iconBackgroundNode = ASImageNode() - self.iconBackgroundNode.displaysAsynchronously = false - self.iconBackgroundNode.image = generateStretchableFilledCircleImage(radius: 16.0, color: theme.separatorColor) - - self.iconAvatarNode = AvatarNode(font: avatarPlaceholderFont(size: 14.0)) - self.iconAvatarNode.setPeer(context: context, theme: ptheme, peer: accountPeer) - - self.iconNameNode = ASTextNode() - self.iconNameNode.attributedText = NSAttributedString(string: accountPeer.compactDisplayTitle, font: Font.medium(15.0), textColor: theme.primaryColor) - - self.iconAnimationNode = IconsNode(context: context, files: icons) - - self.actionNodesSeparator = ASDisplayNode() - self.actionNodesSeparator.isLayerBacked = true - - self.actionNodes = actions.map { action -> TextAlertContentActionNode in - return TextAlertContentActionNode(theme: theme, action: action) - } - - var actionVerticalSeparators: [ASDisplayNode] = [] - if actions.count > 1 { - for _ in 0 ..< actions.count - 1 { - let separatorNode = ASDisplayNode() - separatorNode.isLayerBacked = true - actionVerticalSeparators.append(separatorNode) - } - } - self.actionVerticalSeparators = actionVerticalSeparators - - super.init() - - self.addSubnode(self.textNode) - self.addSubnode(self.iconBackgroundNode) - self.addSubnode(self.iconAvatarNode) - self.addSubnode(self.iconNameNode) - self.addSubnode(self.iconAnimationNode) - - self.addSubnode(self.actionNodesSeparator) - - for actionNode in self.actionNodes { - self.addSubnode(actionNode) - } - - for separatorNode in self.actionVerticalSeparators { - self.addSubnode(separatorNode) - } - - self.updateTheme(theme) - } - - override func updateTheme(_ theme: AlertControllerTheme) { - let string = self.strings.WebApp_EmojiPermission_Text(self.botName, self.botName).string - let attributedText = parseMarkdownIntoAttributedString(string, attributes: MarkdownAttributes( - body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor), - bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: theme.primaryColor), - link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor), - linkAttribute: { url in - return ("URL", url) - } - ), textAlignment: .center) - self.textNode.attributedText = attributedText - - self.actionNodesSeparator.backgroundColor = theme.separatorColor - for actionNode in self.actionNodes { - actionNode.updateTheme(theme) - } - for separatorNode in self.actionVerticalSeparators { - separatorNode.backgroundColor = theme.separatorColor - } - - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) - } - } - - override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - var size = size - size.width = min(size.width , 270.0) - - self.validLayout = size - - var origin: CGPoint = CGPoint(x: 0.0, y: 20.0) - - let iconSpacing: CGFloat = 6.0 - let iconSize = CGSize(width: 32.0, height: 32.0) - let nameSize = self.iconNameNode.measure(size) - let totalIconWidth = iconSize.width + iconSpacing + nameSize.width + 4.0 + iconSize.width - - let iconBackgroundFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - totalIconWidth) / 2.0), y: origin.y), size: CGSize(width: totalIconWidth, height: iconSize.height)) - transition.updateFrame(node: self.iconBackgroundNode, frame: iconBackgroundFrame) - transition.updateFrame(node: self.iconAvatarNode, frame: CGRect(origin: iconBackgroundFrame.origin, size: iconSize).insetBy(dx: 1.0, dy: 1.0)) - transition.updateFrame(node: self.iconNameNode, frame: CGRect(origin: CGPoint(x: iconBackgroundFrame.minX + iconSize.width + iconSpacing, y: iconBackgroundFrame.minY + floorToScreenPixels((iconBackgroundFrame.height - nameSize.height) / 2.0)), size: nameSize)) - - self.iconAnimationNode.updateLayout(size: CGSize(width: 20.0, height: 20.0), theme: self.presentationTheme) - self.iconAnimationNode.frame = CGRect(origin: CGPoint(x: iconBackgroundFrame.maxX - iconSize.width - 3.0, y: iconBackgroundFrame.minY), size: iconSize).insetBy(dx: 6.0, dy: 6.0) - - origin.y += iconSize.height + 16.0 - - let textSize = self.textNode.measure(CGSize(width: size.width - 32.0, height: size.height)) - transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize)) - - let actionButtonHeight: CGFloat = 44.0 - var minActionsWidth: CGFloat = 0.0 - let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) - let actionTitleInsets: CGFloat = 8.0 - - var effectiveActionLayout = TextAlertContentActionLayout.horizontal - for actionNode in self.actionNodes { - let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) - if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 { - effectiveActionLayout = .vertical - } - switch effectiveActionLayout { - case .horizontal: - minActionsWidth += actionTitleSize.width + actionTitleInsets - case .vertical: - minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) - } - } - - let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0) - - var contentWidth = minActionsWidth - contentWidth = max(contentWidth, 234.0) - - var actionsHeight: CGFloat = 0.0 - switch effectiveActionLayout { - case .horizontal: - actionsHeight = actionButtonHeight - case .vertical: - actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) - } - - let resultWidth = contentWidth + insets.left + insets.right - let resultSize = CGSize(width: resultWidth, height: iconSize.height + textSize.height + actionsHeight + 16.0 + insets.top + insets.bottom) - - transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - - var actionOffset: CGFloat = 0.0 - let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) - var separatorIndex = -1 - var nodeIndex = 0 - for actionNode in self.actionNodes { - if separatorIndex >= 0 { - let separatorNode = self.actionVerticalSeparators[separatorIndex] - switch effectiveActionLayout { - case .horizontal: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) - case .vertical: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - } - } - separatorIndex += 1 - - let currentActionWidth: CGFloat - switch effectiveActionLayout { - case .horizontal: - if nodeIndex == self.actionNodes.count - 1 { - currentActionWidth = resultSize.width - actionOffset - } else { - currentActionWidth = actionWidth - } - case .vertical: - currentActionWidth = resultSize.width - } - - let actionNodeFrame: CGRect - switch effectiveActionLayout { - case .horizontal: - actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += currentActionWidth - case .vertical: - actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += actionButtonHeight - } - - transition.updateFrame(node: actionNode, frame: actionNodeFrame) - - nodeIndex += 1 - } - - return resultSize - } -} +import AlertComponent +import AvatarComponent +import MultilineTextComponent func webAppEmojiStatusAlertController( context: AccountContext, accountPeer: EnginePeer, botName: String, icons: [TelegramMediaFile.Accessor], - completion: @escaping (Bool) -> Void -) -> AlertController { - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let theme = presentationData.theme - let strings = presentationData.strings - - var dismissImpl: ((Bool) -> Void)? - var contentNode: WebAppEmojiStatusAlertContentNode? - let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: strings.WebApp_EmojiPermission_Decline, action: { - dismissImpl?(true) + completion: @escaping (Bool, Bool) -> Void +) -> ViewController { + let strings = context.sharedContext.currentPresentationData.with { $0 }.strings - completion(false) - }), TextAlertAction(type: .defaultAction, title: strings.WebApp_EmojiPermission_Allow, action: { - dismissImpl?(true) - - completion(true) - })] + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "status", + component: AnyComponent( + AlertEmojiStatusComponent(context: context, peer: accountPeer, files: icons) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(strings.WebApp_EmojiPermission_Text(botName, botName).string)) + ) + )) - contentNode = WebAppEmojiStatusAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: theme, strings: strings, accountPeer: accountPeer, botName: botName, icons: icons, actions: actions) - - let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode!) - dismissImpl = { [weak controller] animated in - if animated { - controller?.dismissAnimated() - } else { - controller?.dismiss() + let alertController = AlertScreen( + context: context, + content: content, + actions: [ + .init(title: strings.WebApp_EmojiPermission_Decline, action: { + completion(false, false) + }), + .init(title: strings.WebApp_EmojiPermission_Allow, type: .default, action: { + completion(true, false) + }) + ] + ) + alertController.dismissed = { byOutsideTap in + if byOutsideTap { + completion(false, true) } } - return controller + return alertController +} + +private final class AlertEmojiStatusComponent: Component { + public typealias EnvironmentType = AlertComponentEnvironment + + let context: AccountContext + let peer: EnginePeer + let files: [TelegramMediaFile.Accessor] + + public init( + context: AccountContext, + peer: EnginePeer, + files: [TelegramMediaFile.Accessor] + ) { + self.context = context + self.peer = peer + self.files = files + } + + public static func ==(lhs: AlertEmojiStatusComponent, rhs: AlertEmojiStatusComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.peer != rhs.peer { + return false + } + if lhs.files != rhs.files { + return false + } + return true + } + + final class View: UIView { + private let background = ComponentView() + private let title = ComponentView() + private let avatar = ComponentView() + + private var animationLayer: InlineStickerItemLayer? + + private var currentIndex = 0 + private var switchingToNext = false + + private var timer: SwiftSignalKit.Timer? + + private var component: AlertEmojiStatusComponent? + private weak var state: EmptyComponentState? + + func update(component: AlertEmojiStatusComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + self.state = state + + let environment = environment[AlertComponentEnvironment.self] + + let titleSize = self.title.update( + transition: transition, + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString( + string: component.peer.compactDisplayTitle, + font: Font.medium(15.0), + textColor: environment.theme.actionSheet.primaryTextColor + )), + maximumNumberOfLines: 0 + )), + environment: {}, + containerSize: availableSize + ) + + let avatarSize = CGSize(width: 30.0, height: 30.0) + let iconSize = CGSize(width: 20.0, height: 20.0) + let avatarMargin: CGFloat = 1.0 + let avatarSpacing: CGFloat = 7.0 + let titleSpacing: CGFloat = 4.0 + let statusMargin: CGFloat = 12.0 + + let backgroundSize = CGSize(width: avatarMargin + avatarSize.width + avatarSpacing + titleSize.width + titleSpacing + iconSize.width + statusMargin, height: 32.0) + + let _ = self.background.update( + transition: transition, + component: AnyComponent(FilledRoundedRectangleComponent(color: environment.theme.actionSheet.primaryTextColor.withMultipliedAlpha(0.1), cornerRadius: .minEdge, smoothCorners: false)), + environment: {}, + containerSize: backgroundSize + ) + let backgroundFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - backgroundSize.width) / 2.0), y: 0.0), size: backgroundSize) + if let backgroundView = self.background.view { + if backgroundView.superview == nil { + self.addSubview(backgroundView) + } + transition.setFrame(view: backgroundView, frame: backgroundFrame) + } + + let _ = self.avatar.update( + transition: transition, + component: AnyComponent(AvatarComponent( + context: component.context, + theme: environment.theme, + peer: component.peer + )), + environment: {}, + containerSize: avatarSize + ) + let avatarFrame = CGRect(origin: CGPoint(x: backgroundFrame.minX + avatarMargin, y: backgroundFrame.minY + avatarMargin), size: avatarSize) + if let avatarView = self.avatar.view { + if avatarView.superview == nil { + self.addSubview(avatarView) + } + transition.setFrame(view: avatarView, frame: avatarFrame) + } + + let titleFrame = CGRect(origin: CGPoint(x: backgroundFrame.minX + avatarMargin + avatarSize.width + avatarSpacing, y: backgroundFrame.minY + floorToScreenPixels((backgroundSize.height - titleSize.height) / 2.0)), size: titleSize) + if let titleView = self.title.view { + if titleView.superview == nil { + self.addSubview(titleView) + } + transition.setFrame(view: titleView, frame: titleFrame) + } + + if self.timer == nil { + self.timer = SwiftSignalKit.Timer(timeout: 2.5, repeat: true, completion: { [weak self] in + guard let self else { + return + } + self.switchingToNext = true + self.state?.updated() + }, queue: Queue.mainQueue()) + self.timer?.start() + } + + let animationLayer: InlineStickerItemLayer + var disappearingAnimationLayer: InlineStickerItemLayer? + if let current = self.animationLayer, !self.switchingToNext { + animationLayer = current + } else { + if self.switchingToNext { + self.currentIndex = (self.currentIndex + 1) % component.files.count + disappearingAnimationLayer = self.animationLayer + self.switchingToNext = false + } + let file = component.files[self.currentIndex]._parse() + let emoji = ChatTextInputTextCustomEmojiAttribute( + interactivelySelectedFromPackId: nil, + fileId: file.fileId.id, + file: file + ) + animationLayer = InlineStickerItemLayer( + context: .account(component.context), + userLocation: .other, + attemptSynchronousLoad: false, + emoji: emoji, + file: file, + cache: component.context.animationCache, + renderer: component.context.animationRenderer, + unique: true, + placeholderColor: environment.theme.list.mediaPlaceholderColor, + pointSize: iconSize, + loopCount: 1 + ) + animationLayer.isVisibleForAnimations = true + animationLayer.dynamicColor = environment.theme.actionSheet.controlAccentColor + self.layer.addSublayer(animationLayer) + self.animationLayer = animationLayer + + animationLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + animationLayer.animatePosition(from: CGPoint(x: 0.0, y: 10.0), to: .zero, duration: 0.2, additive: true) + animationLayer.animateScale(from: 0.01, to: 1.0, duration: 0.2) + } + + animationLayer.frame = CGRect(origin: CGPoint(x: backgroundFrame.maxX - iconSize.width - statusMargin, y: backgroundFrame.minY + floorToScreenPixels((backgroundFrame.height - iconSize.height) / 2.0)), size: iconSize) + + if let disappearingAnimationLayer { + disappearingAnimationLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in + disappearingAnimationLayer.removeFromSuperlayer() + }) + disappearingAnimationLayer.animatePosition(from: .zero, to: CGPoint(x: 0.0, y: -10.0), duration: 0.2, removeOnCompletion: false, additive: true) + disappearingAnimationLayer.animateScale(from: 1.0, to: 0.01, duration: 0.2, removeOnCompletion: false) + } + + return CGSize(width: availableSize.width, height: backgroundSize.height + 12.0) + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } } diff --git a/submodules/WebUI/Sources/WebAppLaunchConfirmationController.swift b/submodules/WebUI/Sources/WebAppLaunchConfirmationController.swift index ba371f96..2fa118db 100644 --- a/submodules/WebUI/Sources/WebAppLaunchConfirmationController.swift +++ b/submodules/WebUI/Sources/WebAppLaunchConfirmationController.swift @@ -10,365 +10,13 @@ import TelegramPresentationData import TelegramUIPreferences import AccountContext import AppBundle -import AvatarNode -import CheckNode -import Markdown import EmojiStatusComponent - -private let textFont = Font.regular(13.0) -private let boldTextFont = Font.semibold(13.0) - -private func formattedText(_ text: String, color: UIColor, linkColor: UIColor, textAlignment: NSTextAlignment = .natural) -> NSAttributedString { - return parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: color), bold: MarkdownAttributeSet(font: boldTextFont, textColor: color), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { _ in return nil}), textAlignment: textAlignment) -} - -private final class WebAppLaunchConfirmationAlertContentNode: AlertContentNode { - private let context: AccountContext - private let presentationTheme: PresentationTheme - private let strings: PresentationStrings - private let peer: EnginePeer - private let title: String - private let text: String - private let showMore: Bool - - private let titleNode: ImmediateTextNode - private var titleCredibilityIconView: ComponentHostView? - private let textNode: ASTextNode - private let avatarNode: AvatarNode - - private let moreButton: HighlightableButtonNode - private let arrowNode: ASImageNode - - private let allowWriteCheckNode: InteractiveCheckNode - private let allowWriteLabelNode: ASTextNode - - private let actionNodesSeparator: ASDisplayNode - private let actionNodes: [TextAlertContentActionNode] - private let actionVerticalSeparators: [ASDisplayNode] - - private var validLayout: CGSize? - - private let morePressed: () -> Void - private let termsPressed: () -> Void - - override var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled - } - - var allowWriteAccess: Bool = true { - didSet { - self.allowWriteCheckNode.setSelected(self.allowWriteAccess, animated: true) - } - } - - init(context: AccountContext, theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, peer: EnginePeer, title: String, text: String, showMore: Bool, requestWriteAccess: Bool, actions: [TextAlertAction], morePressed: @escaping () -> Void, termsPressed: @escaping () -> Void) { - self.context = context - self.strings = strings - self.presentationTheme = ptheme - self.peer = peer - self.title = title - self.text = text - self.showMore = showMore - self.morePressed = morePressed - self.termsPressed = termsPressed - - self.titleNode = ImmediateTextNode() - self.titleNode.displaysAsynchronously = false - self.titleNode.maximumNumberOfLines = 1 - self.titleNode.textAlignment = .center - - self.textNode = ASTextNode() - self.textNode.displaysAsynchronously = false - self.textNode.maximumNumberOfLines = 0 - - self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0)) - - self.moreButton = HighlightableButtonNode() - - self.arrowNode = ASImageNode() - self.arrowNode.displaysAsynchronously = false - self.arrowNode.displayWithoutProcessing = true - self.arrowNode.isHidden = !showMore - self.arrowNode.contentMode = .scaleAspectFit - - self.allowWriteCheckNode = InteractiveCheckNode(theme: CheckNodeTheme(backgroundColor: theme.accentColor, strokeColor: theme.contrastColor, borderColor: theme.controlBorderColor, overlayBorder: false, hasInset: false, hasShadow: false)) - self.allowWriteCheckNode.setSelected(true, animated: false) - self.allowWriteLabelNode = ASTextNode() - self.allowWriteLabelNode.maximumNumberOfLines = 4 - self.allowWriteLabelNode.isUserInteractionEnabled = true - - self.actionNodesSeparator = ASDisplayNode() - self.actionNodesSeparator.isLayerBacked = true - - self.actionNodes = actions.map { action -> TextAlertContentActionNode in - return TextAlertContentActionNode(theme: theme, action: action) - } - - var actionVerticalSeparators: [ASDisplayNode] = [] - if actions.count > 1 { - for _ in 0 ..< actions.count - 1 { - let separatorNode = ASDisplayNode() - separatorNode.isLayerBacked = true - actionVerticalSeparators.append(separatorNode) - } - } - self.actionVerticalSeparators = actionVerticalSeparators - - super.init() - - self.addSubnode(self.titleNode) - self.addSubnode(self.textNode) - self.addSubnode(self.avatarNode) - self.addSubnode(self.moreButton) - self.moreButton.addSubnode(self.arrowNode) - - if requestWriteAccess { - self.addSubnode(self.allowWriteCheckNode) - self.addSubnode(self.allowWriteLabelNode) - } - - self.addSubnode(self.actionNodesSeparator) - - for actionNode in self.actionNodes { - self.addSubnode(actionNode) - } - - for separatorNode in self.actionVerticalSeparators { - self.addSubnode(separatorNode) - } - - self.allowWriteCheckNode.valueChanged = { [weak self] value in - if let strongSelf = self { - strongSelf.allowWriteAccess = !strongSelf.allowWriteAccess - } - } - - self.updateTheme(theme) - - self.avatarNode.setPeer(context: context, theme: ptheme, peer: peer) - - self.moreButton.addTarget(self, action: #selector(self.moreButtonPressed), forControlEvents: .touchUpInside) - } - - override func didLoad() { - super.didLoad() - - self.allowWriteLabelNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.allowWriteTap(_:)))) - - self.textNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.termsTap(_:)))) - } - - @objc private func allowWriteTap(_ gestureRecognizer: UITapGestureRecognizer) { - if self.allowWriteCheckNode.isUserInteractionEnabled { - self.allowWriteAccess = !self.allowWriteAccess - } - } - - @objc private func termsTap(_ gestureRecognizer: UITapGestureRecognizer) { - self.termsPressed() - } - - @objc private func moreButtonPressed() { - self.morePressed() - } - - override func updateTheme(_ theme: AlertControllerTheme) { - self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.semibold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center) - self.textNode.attributedText = formattedText(self.text, color: theme.primaryColor, linkColor: theme.accentColor, textAlignment: .center) - - self.moreButton.setAttributedTitle(NSAttributedString(string: self.strings.WebApp_LaunchMoreInfo, font: Font.regular(13.0), textColor: theme.accentColor), for: .normal) - self.arrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Peer Info/AlertArrow"), color: theme.accentColor) - - self.allowWriteLabelNode.attributedText = formattedText(strings.WebApp_AddToAttachmentAllowMessages(self.peer.compactDisplayTitle).string, color: theme.primaryColor, linkColor: theme.primaryColor) - - self.actionNodesSeparator.backgroundColor = theme.separatorColor - for actionNode in self.actionNodes { - actionNode.updateTheme(theme) - } - for separatorNode in self.actionVerticalSeparators { - separatorNode.backgroundColor = theme.separatorColor - } - - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) - } - } - - override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - var size = size - size.width = min(size.width, 270.0) - - self.validLayout = size - - var origin: CGPoint = CGPoint(x: 0.0, y: 20.0) - - let avatarSize = CGSize(width: 60.0, height: 60.0) - self.avatarNode.updateSize(size: avatarSize) - - let avatarFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0), y: origin.y), size: avatarSize) - transition.updateFrame(node: self.avatarNode, frame: avatarFrame) - - origin.y += avatarSize.height + 17.0 - - if let arrowImage = self.arrowNode.image { - let arrowFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - arrowImage.size.width) / 2.0), y: origin.y + floorToScreenPixels((avatarSize.height - arrowImage.size.height) / 2.0)), size: arrowImage.size) - transition.updateFrame(node: self.arrowNode, frame: arrowFrame) - } - - let titleSize = self.titleNode.updateLayout(CGSize(width: size.width - 32.0, height: size.height)) - var totalWidth = titleSize.width - - var statusContent: EmojiStatusComponent.Content? - if self.peer.isScam { - statusContent = .text(color: self.presentationTheme.list.itemDestructiveColor, string: self.strings.Message_ScamAccount.uppercased()) - } else if self.peer.isFake { - statusContent = .text(color: self.presentationTheme.list.itemDestructiveColor, string: self.strings.Message_FakeAccount.uppercased()) - } else if self.peer.isVerified { - statusContent = .verified(fillColor: self.presentationTheme.list.itemCheckColors.fillColor, foregroundColor: self.presentationTheme.list.itemCheckColors.foregroundColor, sizeType: .large) - } - - if let statusContent { - let titleCredibilityIconTransition: ComponentTransition = .immediate - - let titleCredibilityIconView: ComponentHostView - if let current = self.titleCredibilityIconView { - titleCredibilityIconView = current - } else { - titleCredibilityIconView = ComponentHostView() - self.titleCredibilityIconView = titleCredibilityIconView - self.view.addSubview(titleCredibilityIconView) - } - - let titleIconSize = titleCredibilityIconView.update( - transition: titleCredibilityIconTransition, - component: AnyComponent(EmojiStatusComponent( - context: self.context, - animationCache: self.context.animationCache, - animationRenderer: self.context.animationRenderer, - content: statusContent, - isVisibleForAnimations: true, - action: { - } - )), - environment: {}, - containerSize: CGSize(width: 20.0, height: 20.0) - ) - - totalWidth += titleIconSize.width + 2.0 - titleCredibilityIconTransition.setFrame(view: titleCredibilityIconView, frame: CGRect(origin: CGPoint(x:floorToScreenPixels((size.width - totalWidth) / 2.0) + titleSize.width + 2.0, y: origin.y), size: titleIconSize)) - } - - transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - totalWidth) / 2.0), y: origin.y), size: titleSize)) - origin.y += titleSize.height + 6.0 - - var entriesHeight: CGFloat = 0.0 - if self.showMore { - let moreButtonSize = self.moreButton.measure(CGSize(width: size.width - 32.0, height: size.height)) - transition.updateFrame(node: self.moreButton, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - moreButtonSize.width) / 2.0) - 5.0, y: origin.y), size: moreButtonSize)) - transition.updateFrame(node: self.arrowNode, frame: CGRect(origin: CGPoint(x: moreButtonSize.width + 3.0, y: 4.0), size: CGSize(width: 9.0, height: 9.0))) - origin.y += moreButtonSize.height + 22.0 - entriesHeight += 37.0 - } - - let textSize = self.textNode.measure(CGSize(width: size.width - 32.0, height: size.height)) - transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize)) - origin.y += textSize.height - - if self.allowWriteLabelNode.supernode != nil { - origin.y += 16.0 - entriesHeight += 16.0 - - let checkSize = CGSize(width: 22.0, height: 22.0) - let condensedSize = CGSize(width: size.width - 76.0, height: size.height) - - let allowWriteSize = self.allowWriteLabelNode.measure(condensedSize) - transition.updateFrame(node: self.allowWriteLabelNode, frame: CGRect(origin: CGPoint(x: 46.0, y: origin.y), size: allowWriteSize)) - transition.updateFrame(node: self.allowWriteCheckNode, frame: CGRect(origin: CGPoint(x: 12.0, y: origin.y - 2.0), size: checkSize)) - origin.y += allowWriteSize.height - entriesHeight += allowWriteSize.height - } - - let actionButtonHeight: CGFloat = 44.0 - var minActionsWidth: CGFloat = 0.0 - let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) - let actionTitleInsets: CGFloat = 8.0 - - var effectiveActionLayout = TextAlertContentActionLayout.vertical - for actionNode in self.actionNodes { - let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) - if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 { - effectiveActionLayout = .vertical - } - switch effectiveActionLayout { - case .horizontal: - minActionsWidth += actionTitleSize.width + actionTitleInsets - case .vertical: - minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) - } - } - - let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0) - - let contentWidth = max(size.width, minActionsWidth) - - var actionsHeight: CGFloat = 0.0 - switch effectiveActionLayout { - case .horizontal: - actionsHeight = actionButtonHeight - case .vertical: - actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) - } - - let resultSize = CGSize(width: contentWidth, height: avatarSize.height + titleSize.height + textSize.height + entriesHeight + actionsHeight + 25.0 + insets.top + insets.bottom) - - transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - - var actionOffset: CGFloat = 0.0 - let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) - var separatorIndex = -1 - var nodeIndex = 0 - for actionNode in self.actionNodes { - if separatorIndex >= 0 { - let separatorNode = self.actionVerticalSeparators[separatorIndex] - switch effectiveActionLayout { - case .horizontal: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) - case .vertical: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - } - } - separatorIndex += 1 - - let currentActionWidth: CGFloat - switch effectiveActionLayout { - case .horizontal: - if nodeIndex == self.actionNodes.count - 1 { - currentActionWidth = resultSize.width - actionOffset - } else { - currentActionWidth = actionWidth - } - case .vertical: - currentActionWidth = resultSize.width - } - - let actionNodeFrame: CGRect - switch effectiveActionLayout { - case .horizontal: - actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += currentActionWidth - case .vertical: - actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += actionButtonHeight - } - - transition.updateFrame(node: actionNode, frame: actionNodeFrame) - - nodeIndex += 1 - } - - return resultSize - } -} +import AlertComponent +import AlertCheckComponent +import AvatarComponent +import MultilineTextComponent +import BundleIconComponent +import PlainButtonComponent public func webAppLaunchConfirmationController( context: AccountContext, @@ -378,50 +26,209 @@ public func webAppLaunchConfirmationController( completion: @escaping (Bool) -> Void, showMore: (() -> Void)?, openTerms: @escaping () -> Void -) -> AlertController { - let theme = defaultDarkColorPresentationTheme - let presentationData: PresentationData - if let updatedPresentationData { - presentationData = updatedPresentationData.initial - } else { - presentationData = context.sharedContext.currentPresentationData.with { $0 } - } +) -> ViewController { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } let strings = presentationData.strings + + let checkState = AlertCheckComponent.ExternalState() - var dismissImpl: ((Bool) -> Void)? - var getContentNodeImpl: (() -> WebAppLaunchConfirmationAlertContentNode?)? - let actions: [TextAlertAction] = [TextAlertAction(type: .defaultAction, title: presentationData.strings.WebApp_LaunchOpenApp, action: { - if requestWriteAccess, let allowWriteAccess = getContentNodeImpl?()?.allowWriteAccess { - completion(allowWriteAccess) - } else { - completion(false) - } - dismissImpl?(true) - }), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - dismissImpl?(true) - })] - - let title = peer.compactDisplayTitle - let text = presentationData.strings.WebApp_LaunchTermsConfirmation - - let contentNode = WebAppLaunchConfirmationAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: theme, strings: strings, peer: peer, title: title, text: text, showMore: showMore != nil, requestWriteAccess: requestWriteAccess, actions: actions, morePressed: { - dismissImpl?(true) - showMore?() - }, termsPressed: { - dismissImpl?(true) - openTerms() - }) - getContentNodeImpl = { [weak contentNode] in - return contentNode + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "header", + component: AnyComponent( + AlertWebAppHeaderComponent(context: context, peer: peer, showMore: showMore) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(strings.WebApp_LaunchTermsConfirmation)) + ) + )) + if requestWriteAccess { + content.append(AnyComponentWithIdentity( + id: "check", + component: AnyComponent( + AlertCheckComponent(title: strings.WebApp_AddToAttachmentAllowMessages(peer.compactDisplayTitle).string, initialValue: false, externalState: checkState) + ) + )) } - let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode) - dismissImpl = { [weak controller] animated in - if animated { - controller?.dismissAnimated() - } else { - controller?.dismiss() - } - } - return controller + let alertController = AlertScreen( + context: context, + configuration: AlertScreen.Configuration(actionAlignment: .vertical), + content: content, + actions: [ + .init(title: strings.WebApp_LaunchOpenApp, type: .default, action: { + completion(requestWriteAccess && checkState.value) + }), + .init(title: strings.Common_Cancel) + ] + ) + return alertController +} + +private final class AlertWebAppHeaderComponent: Component { + public typealias EnvironmentType = AlertComponentEnvironment + + let context: AccountContext + let peer: EnginePeer + let showMore: (() -> Void)? + + public init( + context: AccountContext, + peer: EnginePeer, + showMore: (() -> Void)? + ) { + self.context = context + self.peer = peer + self.showMore = showMore + } + + public static func ==(lhs: AlertWebAppHeaderComponent, rhs: AlertWebAppHeaderComponent) -> Bool { + return true + } + + public final class View: UIView { + private let avatar = ComponentView() + private let title = ComponentView() + private let titleIcon = ComponentView() + private let showMore = ComponentView() + + private var component: AlertWebAppHeaderComponent? + private weak var state: EmptyComponentState? + + func update(component: AlertWebAppHeaderComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + self.state = state + + let environment = environment[AlertComponentEnvironment.self] + + var contentHeight: CGFloat = 0.0 + let avatarSize = self.avatar.update( + transition: .immediate, + component: AnyComponent( + AvatarComponent( + context: component.context, + theme: environment.theme, + peer: component.peer + ) + ), + environment: {}, + containerSize: CGSize(width: 60.0, height: 60.0) + ) + let avatarFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - avatarSize.width) / 2.0), y: contentHeight), size: avatarSize) + if let avatarView = self.avatar.view { + if avatarView.superview == nil { + self.addSubview(avatarView) + } + avatarView.frame = avatarFrame + } + contentHeight += avatarSize.height + contentHeight += 17.0 + + let titleSize = self.title.update( + transition: .immediate, + component: AnyComponent( + MultilineTextComponent( + text: .plain(NSAttributedString( + string: component.peer.compactDisplayTitle, + font: Font.bold(17.0), + textColor: environment.theme.actionSheet.primaryTextColor + )), + horizontalAlignment: .natural, + maximumNumberOfLines: 0 + ) + ), + environment: {}, + containerSize: CGSize(width: availableSize.width - 32.0, height: availableSize.height) + ) + + var totalWidth = titleSize.width + + var statusContent: EmojiStatusComponent.Content? + if component.peer.isScam { + statusContent = .text(color: environment.theme.list.itemDestructiveColor, string: environment.strings.Message_ScamAccount.uppercased()) + } else if component.peer.isFake { + statusContent = .text(color: environment.theme.list.itemDestructiveColor, string: environment.strings.Message_FakeAccount.uppercased()) + } else if component.peer.isVerified { + statusContent = .verified(fillColor: environment.theme.list.itemCheckColors.fillColor, foregroundColor: environment.theme.list.itemCheckColors.foregroundColor, sizeType: .large) + } + if let statusContent { + let titleIconSize = self.titleIcon.update( + transition: .immediate, + component: AnyComponent(EmojiStatusComponent( + context: component.context, + animationCache: component.context.animationCache, + animationRenderer: component.context.animationRenderer, + content: statusContent, + isVisibleForAnimations: true, + action: { + } + )), + environment: {}, + containerSize: CGSize(width: 20.0, height: 20.0) + ) + totalWidth += titleIconSize.width + 2.0 + if let titleIconView = self.titleIcon.view { + if titleIconView.superview == nil { + self.addSubview(titleIconView) + } + transition.setFrame(view: titleIconView, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - totalWidth) / 2.0) + titleSize.width + 2.0, y: contentHeight), size: titleIconSize)) + } + } + + let titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - totalWidth) / 2.0), y: contentHeight), size: titleSize) + if let titleView = self.title.view { + if titleView.superview == nil { + self.addSubview(titleView) + } + titleView.frame = titleFrame + } + contentHeight += titleSize.height + + if let showMore = component.showMore { + contentHeight += 6.0 + + let showMoreSize = self.showMore.update( + transition: .immediate, + component: AnyComponent( + PlainButtonComponent( + content: AnyComponent( + HStack([ + AnyComponentWithIdentity(id: "label", component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: environment.strings.WebApp_LaunchMoreInfo, font: Font.regular(14.0), textColor: environment.theme.actionSheet.controlAccentColor))))), + AnyComponentWithIdentity(id: "arrow", component: AnyComponent(BundleIconComponent(name: "Item List/InlineTextRightArrow", tintColor: environment.theme.actionSheet.controlAccentColor))) + ], spacing: 3.0) + ), + action: { + showMore() + }, + animateScale: false + ) + ), + environment: {}, + containerSize: availableSize + ) + let showMoreFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - showMoreSize.width) / 2.0), y: contentHeight), size: showMoreSize) + if let showMoreView = self.showMore.view { + if showMoreView.superview == nil { + self.addSubview(showMoreView) + } + showMoreView.frame = showMoreFrame + } + contentHeight += showMoreSize.height + } + contentHeight += 12.0 + + return CGSize(width: availableSize.width, height: contentHeight) + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } } diff --git a/submodules/WebUI/Sources/WebAppLocationAlertController.swift b/submodules/WebUI/Sources/WebAppLocationAlertController.swift index 867841fc..1fe62d60 100644 --- a/submodules/WebUI/Sources/WebAppLocationAlertController.swift +++ b/submodules/WebUI/Sources/WebAppLocationAlertController.swift @@ -10,17 +10,18 @@ import TelegramPresentationData import TelegramUIPreferences import AccountContext import AppBundle -import AvatarNode -import Markdown -import CheckNode +import ComponentFlow +import AlertComponent +import AvatarComponent +import AlertTransferHeaderComponent -private func generateBoostIcon(theme: PresentationTheme) -> UIImage? { - let size = CGSize(width: 28.0, height: 28.0) +private func generateLocationIcon() -> UIImage? { + let size = CGSize(width: 24.0, height: 24.0) return generateImage(size, contextGenerator: { size, context in let bounds = CGRect(origin: .zero, size: size) context.clear(bounds) - context.addEllipse(in: bounds.insetBy(dx: 1.0, dy: 1.0)) + context.addEllipse(in: bounds) context.clip() var locations: [CGFloat] = [1.0, 0.0] @@ -32,261 +33,61 @@ private func generateBoostIcon(theme: PresentationTheme) -> UIImage? { context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: size.height), options: CGGradientDrawingOptions()) if let image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Attach Menu/Location"), color: .white), let cgImage = image.cgImage { - context.draw(cgImage, in: bounds.insetBy(dx: 6.0, dy: 6.0)) + context.draw(cgImage, in: bounds.insetBy(dx: 4.0, dy: 4.0)) } context.resetClip() - - let lineWidth = 2.0 - UIScreenPixel - context.setLineWidth(lineWidth) - context.setStrokeColor(theme.actionSheet.opaqueItemBackgroundColor.cgColor) - context.strokeEllipse(in: bounds.insetBy(dx: lineWidth / 2.0 + UIScreenPixel, dy: lineWidth / 2.0 + UIScreenPixel)) }, opaque: false) } -private final class WebAppLocationAlertContentNode: AlertContentNode { - private let strings: PresentationStrings - private let text: String - - private let textNode: ASTextNode - private let avatarNode: AvatarNode - private let arrowNode: ASImageNode - private let secondAvatarNode: AvatarNode - private let iconNode: ASImageNode - - private let actionNodesSeparator: ASDisplayNode - private let actionNodes: [TextAlertContentActionNode] - private let actionVerticalSeparators: [ASDisplayNode] - - private var validLayout: CGSize? - - override var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled - } - - init(context: AccountContext, theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, accountPeer: EnginePeer, botPeer: EnginePeer, text: String, actions: [TextAlertAction]) { - self.strings = strings - self.text = text - - self.textNode = ASTextNode() - self.textNode.maximumNumberOfLines = 0 - - self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0)) - - self.arrowNode = ASImageNode() - self.arrowNode.displaysAsynchronously = false - self.arrowNode.displayWithoutProcessing = true - - self.secondAvatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0)) - - self.iconNode = ASImageNode() - self.iconNode.displaysAsynchronously = false - self.iconNode.image = generateBoostIcon(theme: ptheme) - - self.actionNodesSeparator = ASDisplayNode() - self.actionNodesSeparator.isLayerBacked = true - - self.actionNodes = actions.map { action -> TextAlertContentActionNode in - return TextAlertContentActionNode(theme: theme, action: action) - } - - var actionVerticalSeparators: [ASDisplayNode] = [] - if actions.count > 1 { - for _ in 0 ..< actions.count - 1 { - let separatorNode = ASDisplayNode() - separatorNode.isLayerBacked = true - actionVerticalSeparators.append(separatorNode) - } - } - self.actionVerticalSeparators = actionVerticalSeparators - - super.init() - - self.addSubnode(self.textNode) - self.addSubnode(self.avatarNode) - self.addSubnode(self.arrowNode) - self.addSubnode(self.secondAvatarNode) - self.addSubnode(self.iconNode) - - self.addSubnode(self.actionNodesSeparator) - - for actionNode in self.actionNodes { - self.addSubnode(actionNode) - } - - for separatorNode in self.actionVerticalSeparators { - self.addSubnode(separatorNode) - } - - self.updateTheme(theme) - - self.avatarNode.setPeer(context: context, theme: ptheme, peer: accountPeer) - self.secondAvatarNode.setPeer(context: context, theme: ptheme, peer: botPeer) - } - - override func updateTheme(_ theme: AlertControllerTheme) { - self.textNode.attributedText = parseMarkdownIntoAttributedString(self.text, attributes: MarkdownAttributes( - body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor), - bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: theme.primaryColor), - link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor), - linkAttribute: { url in - return ("URL", url) - } - ), textAlignment: .center) - self.arrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Peer Info/AlertArrow"), color: theme.secondaryColor) - - self.actionNodesSeparator.backgroundColor = theme.separatorColor - for actionNode in self.actionNodes { - actionNode.updateTheme(theme) - } - for separatorNode in self.actionVerticalSeparators { - separatorNode.backgroundColor = theme.separatorColor - } - - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) - } - } - - override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - var size = size - size.width = min(size.width, 270.0) - - self.validLayout = size - - var origin: CGPoint = CGPoint(x: 0.0, y: 20.0) - - let avatarSize = CGSize(width: 60.0, height: 60.0) - self.avatarNode.updateSize(size: avatarSize) - - let avatarFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) - 44.0, y: origin.y), size: avatarSize) - transition.updateFrame(node: self.avatarNode, frame: avatarFrame) - - if let arrowImage = self.arrowNode.image { - let arrowFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - arrowImage.size.width) / 2.0), y: origin.y + floorToScreenPixels((avatarSize.height - arrowImage.size.height) / 2.0)), size: arrowImage.size) - transition.updateFrame(node: self.arrowNode, frame: arrowFrame) - } - - let secondAvatarFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) + 44.0, y: origin.y), size: avatarSize) - transition.updateFrame(node: self.secondAvatarNode, frame: secondAvatarFrame) - - if let icon = self.iconNode.image { - let iconFrame = CGRect(origin: CGPoint(x: avatarFrame.maxX + 4.0 - icon.size.width, y: avatarFrame.maxY + 4.0 - icon.size.height), size: icon.size) - transition.updateFrame(node: self.iconNode, frame: iconFrame) - } - - origin.y += avatarSize.height + 10.0 - - let textSize = self.textNode.measure(CGSize(width: size.width - 32.0, height: size.height)) - transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize)) - origin.y += textSize.height + 10.0 - - let actionButtonHeight: CGFloat = 44.0 - var minActionsWidth: CGFloat = 0.0 - let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) - let actionTitleInsets: CGFloat = 8.0 - - var effectiveActionLayout = TextAlertContentActionLayout.horizontal - for actionNode in self.actionNodes { - let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) - if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 { - effectiveActionLayout = .vertical - } - switch effectiveActionLayout { - case .horizontal: - minActionsWidth += actionTitleSize.width + actionTitleInsets - case .vertical: - minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) - } - } - - let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0) - - let contentWidth = max(size.width, minActionsWidth) - - var actionsHeight: CGFloat = 0.0 - switch effectiveActionLayout { - case .horizontal: - actionsHeight = actionButtonHeight - case .vertical: - actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) - } - - let resultSize = CGSize(width: contentWidth, height: avatarSize.height + textSize.height + actionsHeight + 16.0 + insets.top + insets.bottom) - transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - - var actionOffset: CGFloat = 0.0 - let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) - var separatorIndex = -1 - var nodeIndex = 0 - for actionNode in self.actionNodes { - if separatorIndex >= 0 { - let separatorNode = self.actionVerticalSeparators[separatorIndex] - switch effectiveActionLayout { - case .horizontal: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) - case .vertical: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - } - } - separatorIndex += 1 - - let currentActionWidth: CGFloat - switch effectiveActionLayout { - case .horizontal: - if nodeIndex == self.actionNodes.count - 1 { - currentActionWidth = resultSize.width - actionOffset - } else { - currentActionWidth = actionWidth - } - case .vertical: - currentActionWidth = resultSize.width - } - - let actionNodeFrame: CGRect - switch effectiveActionLayout { - case .horizontal: - actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += currentActionWidth - case .vertical: - actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += actionButtonHeight - } - - transition.updateFrame(node: actionNode, frame: actionNodeFrame) - - nodeIndex += 1 - } - - return resultSize - } -} - -func webAppLocationAlertController(context: AccountContext, accountPeer: EnginePeer, botPeer: EnginePeer, completion: @escaping (Bool) -> Void) -> AlertController { +func webAppLocationAlertController(context: AccountContext, accountPeer: EnginePeer, botPeer: EnginePeer, completion: @escaping (Bool) -> Void) -> ViewController { let presentationData = context.sharedContext.currentPresentationData.with { $0 } let strings = presentationData.strings - - let text = strings.WebApp_LocationPermission_Text(botPeer.compactDisplayTitle, botPeer.compactDisplayTitle).string - - var dismissImpl: ((Bool) -> Void)? - var contentNode: WebAppLocationAlertContentNode? - let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: strings.WebApp_LocationPermission_Decline, action: { - dismissImpl?(true) - completion(false) - }), TextAlertAction(type: .defaultAction, title: strings.WebApp_LocationPermission_Allow, action: { - dismissImpl?(true) - completion(true) - })] - contentNode = WebAppLocationAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: strings, accountPeer: accountPeer, botPeer: botPeer, text: text, actions: actions) + let locationIcon = generateLocationIcon() - let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode!) - dismissImpl = { [weak controller] animated in - if animated { - controller?.dismissAnimated() - } else { - controller?.dismiss() - } - } - return controller + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "header", + component: AnyComponent( + AlertTransferHeaderComponent( + fromComponent: AnyComponentWithIdentity(id: "user", component: AnyComponent( + AvatarComponent( + context: context, + theme: presentationData.theme, + peer: accountPeer, + icon: AnyComponent(Image(image: locationIcon, contentMode: .center)) + ) + )), + toComponent: AnyComponentWithIdentity(id: "bot", component: AnyComponent( + AvatarComponent( + context: context, + theme: presentationData.theme, + peer: botPeer + ) + )), + type: .transfer + ) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(strings.WebApp_LocationPermission_Text(botPeer.compactDisplayTitle, botPeer.compactDisplayTitle).string)) + ) + )) + + let alertController = AlertScreen( + context: context, + content: content, + actions: [ + .init(title: strings.WebApp_LocationPermission_Decline, action: { + completion(false) + }), + .init(title: strings.WebApp_LocationPermission_Allow, type: .default, action: { + completion(true) + }) + ] + ) + return alertController } diff --git a/submodules/WebUI/Sources/WebAppMessageChatPreviewItem.swift b/submodules/WebUI/Sources/WebAppMessageChatPreviewItem.swift index 9f6133b1..68f30500 100644 --- a/submodules/WebUI/Sources/WebAppMessageChatPreviewItem.swift +++ b/submodules/WebUI/Sources/WebAppMessageChatPreviewItem.swift @@ -160,7 +160,7 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode { self.containerNode = ASDisplayNode() self.containerNode.subnodeTransform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) - super.init(layerBacked: false, dynamicBounce: false) + super.init(layerBacked: false) self.clipsToBounds = true self.isUserInteractionEnabled = false diff --git a/submodules/WebUI/Sources/WebAppMessagePreviewScreen.swift b/submodules/WebUI/Sources/WebAppMessagePreviewScreen.swift index a9090e04..3d65b1f3 100644 --- a/submodules/WebUI/Sources/WebAppMessagePreviewScreen.swift +++ b/submodules/WebUI/Sources/WebAppMessagePreviewScreen.swift @@ -23,6 +23,7 @@ import ListItemComponentAdaptor import TelegramStringFormatting import UndoUI import ChatMessagePaymentAlertController +import GlassBarButtonComponent private final class SheetContent: CombinedComponent { typealias EnvironmentType = ViewControllerComponentContainer.Environment @@ -55,7 +56,7 @@ private final class SheetContent: CombinedComponent { } static var body: Body { - let closeButton = Child(Button.self) + let closeButton = Child(GlassBarButtonComponent.self) let title = Child(Text.self) let amountSection = Child(ListSectionComponent.self) let button = Child(ButtonComponent.self) @@ -71,22 +72,31 @@ private final class SheetContent: CombinedComponent { let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } let sideInset: CGFloat = 16.0 - var contentSize = CGSize(width: context.availableSize.width, height: 18.0) + var contentSize = CGSize(width: context.availableSize.width, height: 36.0) let constrainedTitleWidth = context.availableSize.width - 16.0 * 2.0 let closeButton = closeButton.update( - component: Button( - content: AnyComponent(Text(text: environment.strings.Common_Cancel, font: Font.regular(17.0), color: theme.actionSheet.controlAccentColor)), - action: { + component: GlassBarButtonComponent( + size: CGSize(width: 40.0, height: 40.0), + backgroundColor: theme.rootController.navigationBar.glassBarButtonBackgroundColor, + isDark: theme.overallDarkAppearance, + state: .generic, + component: AnyComponentWithIdentity(id: "close", component: AnyComponent( + BundleIconComponent( + name: "Navigation/Close", + tintColor: theme.rootController.navigationBar.glassBarButtonForegroundColor + ) + )), + action: { _ in component.dismiss() } ), - availableSize: CGSize(width: 120.0, height: 30.0), + availableSize: CGSize(width: 40.0, height: 40.0), transition: .immediate ) context.add(closeButton - .position(CGPoint(x: closeButton.size.width / 2.0 + sideInset, y: 28.0)) + .position(CGPoint(x: 16.0 + closeButton.size.width / 2.0, y: 16.0 + closeButton.size.height / 2.0)) ) let title = title.update( @@ -95,7 +105,7 @@ private final class SheetContent: CombinedComponent { transition: .immediate ) context.add(title - .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + title.size.height / 2.0)) + .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height)) ) contentSize.height += title.size.height contentSize.height += 40.0 @@ -246,13 +256,14 @@ private final class SheetContent: CombinedComponent { let buttonString: String = environment.strings.WebApp_ShareMessage_Share let buttonAttributedString = NSMutableAttributedString(string: buttonString, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center) + let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: environment.safeInsets.bottom, innerDiameter: 52.0, sideInset: 30.0) let button = button.update( component: ButtonComponent( background: ButtonComponent.Background( + style: .glass, color: theme.list.itemCheckColors.fillColor, foreground: theme.list.itemCheckColors.foregroundColor, pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9), - cornerRadius: 10.0 ), content: AnyComponentWithIdentity( id: AnyHashable(0), @@ -266,7 +277,7 @@ private final class SheetContent: CombinedComponent { } } ), - availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50), + availableSize: CGSize(width: context.availableSize.width - buttonInsets.left - buttonInsets.right, height: 52.0), transition: .immediate ) context.add(button @@ -275,10 +286,8 @@ private final class SheetContent: CombinedComponent { .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + button.size.height / 2.0)) ) contentSize.height += button.size.height - contentSize.height += 15.0 + contentSize.height += buttonInsets.bottom - contentSize.height += max(environment.inputHeight, environment.safeInsets.bottom) - return contentSize } } @@ -328,6 +337,7 @@ private final class WebAppMessagePreviewSheetComponent: CombinedComponent { let controller = environment.controller + let theme = environment.theme.withModalBlocksBackground() let sheet = sheet.update( component: SheetComponent( content: AnyComponent(SheetContent( @@ -344,7 +354,8 @@ private final class WebAppMessagePreviewSheetComponent: CombinedComponent { }) } )), - backgroundColor: .color(environment.theme.list.blocksBackgroundColor), + style: .glass, + backgroundColor: .color(theme.list.blocksBackgroundColor), followContentSizeChanges: false, clipsContent: true, isScrollEnabled: false, diff --git a/submodules/WebUI/Sources/WebAppSetEmojiStatusScreen.swift b/submodules/WebUI/Sources/WebAppSetEmojiStatusScreen.swift index bde5fba5..55cd331d 100644 --- a/submodules/WebUI/Sources/WebAppSetEmojiStatusScreen.swift +++ b/submodules/WebUI/Sources/WebAppSetEmojiStatusScreen.swift @@ -116,7 +116,7 @@ private final class SheetContent: CombinedComponent { component: AnyComponentWithIdentity(id: "close", component: AnyComponent( BundleIconComponent( name: "Navigation/Close", - tintColor: theme.rootController.navigationBar.glassBarButtonForegroundColor + tintColor: theme.chat.inputPanel.panelControlColor ) )), action: { _ in @@ -198,6 +198,7 @@ private final class SheetContent: CombinedComponent { let controller = environment.controller() as? WebAppSetEmojiStatusScreen + let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: environment.safeInsets.bottom, innerDiameter: 52.0, sideInset: 30.0) let button = button.update( component: ButtonComponent( background: ButtonComponent.Background( @@ -217,7 +218,7 @@ private final class SheetContent: CombinedComponent { controller?.dismissAnimated() } ), - availableSize: CGSize(width: context.availableSize.width - 30.0 * 2.0, height: 52.0), + availableSize: CGSize(width: context.availableSize.width - buttonInsets.left - buttonInsets.right, height: 52.0), transition: .immediate ) context.add(button @@ -226,8 +227,7 @@ private final class SheetContent: CombinedComponent { .position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + button.size.height / 2.0)) ) contentSize.height += button.size.height - - contentSize.height += 48.0 + contentSize.height += buttonInsets.bottom return contentSize } diff --git a/submodules/WebUI/Sources/WebAppTermsAlertController.swift b/submodules/WebUI/Sources/WebAppTermsAlertController.swift index 88f3a717..ba305d3b 100644 --- a/submodules/WebUI/Sources/WebAppTermsAlertController.swift +++ b/submodules/WebUI/Sources/WebAppTermsAlertController.swift @@ -9,345 +9,9 @@ import TelegramPresentationData import TelegramUIPreferences import AccountContext import AppBundle -import AvatarNode -import CheckNode -import Markdown -import TextFormat - -private let textFont = Font.regular(13.0) -private let boldTextFont = Font.semibold(13.0) - -private func formattedText(_ text: String, fontSize: CGFloat, color: UIColor, linkColor: UIColor, textAlignment: NSTextAlignment = .natural) -> NSAttributedString { - return parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: Font.regular(fontSize), textColor: color), bold: MarkdownAttributeSet(font: Font.semibold(fontSize), textColor: color), link: MarkdownAttributeSet(font: Font.regular(fontSize), textColor: linkColor), linkAttribute: { _ in return (TelegramTextAttributes.URL, "") }), textAlignment: textAlignment) -} - -private final class WebAppTermsAlertContentNode: AlertContentNode, ASGestureRecognizerDelegate { - private let strings: PresentationStrings - private let title: String - private let text: String - private let additionalText: String? - - private let titleNode: ImmediateTextNode - private let textNode: ImmediateTextNode - private let additionalTextNode: ImmediateTextNode - - private let acceptTermsCheckNode: InteractiveCheckNode - private let acceptTermsLabelNode: ImmediateTextNode - - private let actionNodesSeparator: ASDisplayNode - private let actionNodes: [TextAlertContentActionNode] - private let actionVerticalSeparators: [ASDisplayNode] - - private var validLayout: CGSize? - - override var dismissOnOutsideTap: Bool { - return self.isUserInteractionEnabled - } - - var acceptedTerms: Bool = false { - didSet { - self.acceptTermsCheckNode.setSelected(self.acceptedTerms, animated: true) - if let firstAction = self.actionNodes.first { - firstAction.actionEnabled = self.acceptedTerms - } - } - } - - var openTerms: () -> Void = {} - - init(context: AccountContext, theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, title: String, text: String, additionalText: String?, actions: [TextAlertAction]) { - self.strings = strings - self.title = title - self.text = text - self.additionalText = additionalText - - self.titleNode = ImmediateTextNode() - self.titleNode.displaysAsynchronously = false - self.titleNode.maximumNumberOfLines = 1 - self.titleNode.textAlignment = .center - - self.textNode = ImmediateTextNode() - self.textNode.maximumNumberOfLines = 0 - self.textNode.displaysAsynchronously = false - self.textNode.lineSpacing = 0.1 - self.textNode.textAlignment = .center - - self.additionalTextNode = ImmediateTextNode() - self.additionalTextNode.maximumNumberOfLines = 0 - self.additionalTextNode.displaysAsynchronously = false - self.additionalTextNode.lineSpacing = 0.1 - self.additionalTextNode.textAlignment = .center - - self.acceptTermsCheckNode = InteractiveCheckNode(theme: CheckNodeTheme(backgroundColor: theme.accentColor, strokeColor: theme.contrastColor, borderColor: theme.controlBorderColor, overlayBorder: false, hasInset: false, hasShadow: false)) - self.acceptTermsLabelNode = ImmediateTextNode() - self.acceptTermsLabelNode.maximumNumberOfLines = 4 - - self.actionNodesSeparator = ASDisplayNode() - self.actionNodesSeparator.isLayerBacked = true - - self.actionNodes = actions.map { action -> TextAlertContentActionNode in - return TextAlertContentActionNode(theme: theme, action: action) - } - - var actionVerticalSeparators: [ASDisplayNode] = [] - if actions.count > 1 { - for _ in 0 ..< actions.count - 1 { - let separatorNode = ASDisplayNode() - separatorNode.isLayerBacked = true - actionVerticalSeparators.append(separatorNode) - } - } - self.actionVerticalSeparators = actionVerticalSeparators - - super.init() - - self.addSubnode(self.titleNode) - self.addSubnode(self.textNode) - self.addSubnode(self.additionalTextNode) - - self.addSubnode(self.acceptTermsCheckNode) - self.addSubnode(self.acceptTermsLabelNode) - - self.addSubnode(self.actionNodesSeparator) - - for actionNode in self.actionNodes { - self.addSubnode(actionNode) - } - - for separatorNode in self.actionVerticalSeparators { - self.addSubnode(separatorNode) - } - - self.acceptTermsCheckNode.valueChanged = { [weak self] value in - if let strongSelf = self { - strongSelf.acceptedTerms = !strongSelf.acceptedTerms - } - } - - self.acceptTermsLabelNode.highlightAttributeAction = { attributes in - if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] { - return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL) - } else { - return nil - } - } - self.acceptTermsLabelNode.tapAttributeAction = { [weak self] attributes, _ in - if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] { - self?.openTerms() - } - } - - self.updateTheme(theme) - } - - override func didLoad() { - super.didLoad() - - let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.acceptTap(_:))) - tapGesture.delegate = self.wrappedGestureRecognizerDelegate - self.view.addGestureRecognizer(tapGesture) - - if let firstAction = self.actionNodes.first { - firstAction.actionEnabled = false - } - } - - override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { - let location = gestureRecognizer.location(in: self.acceptTermsLabelNode.view) - if self.acceptTermsLabelNode.bounds.contains(location) { - return true - } - return false - } - - override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - if !self.bounds.contains(point) { - return nil - } - - if let (_, attributes) = self.acceptTermsLabelNode.attributesAtPoint(self.view.convert(point, to: self.acceptTermsLabelNode.view)) { - if attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] == nil { - return self.view - } - } - - return super.hitTest(point, with: event) - } - - @objc private func acceptTap(_ gestureRecognizer: UITapGestureRecognizer) { - let location = gestureRecognizer.location(in: self.acceptTermsLabelNode.view) - if self.acceptTermsCheckNode.isUserInteractionEnabled { - if let attributes = self.acceptTermsLabelNode.attributesAtPoint(location)?.1 { - if attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] != nil { - return - } - } - self.acceptedTerms = !self.acceptedTerms - } - } - - override func updateTheme(_ theme: AlertControllerTheme) { - self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.semibold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center) - self.textNode.attributedText = formattedText(self.text, fontSize: 13.0, color: theme.primaryColor, linkColor: theme.accentColor, textAlignment: .center) - if let additionalText = self.additionalText { - self.additionalTextNode.attributedText = formattedText(additionalText, fontSize: 13.0, color: theme.primaryColor, linkColor: theme.accentColor, textAlignment: .center) - } else { - self.additionalTextNode.attributedText = nil - } - - let attributedAgreeText = parseMarkdownIntoAttributedString( - self.strings.WebApp_DisclaimerAgree, - attributes: MarkdownAttributes( - body: MarkdownAttributeSet(font: textFont, textColor: theme.primaryColor), - bold: MarkdownAttributeSet(font: boldTextFont, textColor: theme.primaryColor), - link: MarkdownAttributeSet(font: textFont, textColor: theme.accentColor), - linkAttribute: { contents in - return (TelegramTextAttributes.URL, contents) - } - ) - ) - - self.acceptTermsLabelNode.attributedText = attributedAgreeText - self.acceptTermsLabelNode.linkHighlightColor = theme.accentColor.withAlphaComponent(0.2) - - self.actionNodesSeparator.backgroundColor = theme.separatorColor - for actionNode in self.actionNodes { - actionNode.updateTheme(theme) - } - for separatorNode in self.actionVerticalSeparators { - separatorNode.backgroundColor = theme.separatorColor - } - - if let size = self.validLayout { - _ = self.updateLayout(size: size, transition: .immediate) - } - } - - override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { - var size = size - size.width = min(size.width, 270.0) - - self.validLayout = size - - var origin: CGPoint = CGPoint(x: 0.0, y: 17.0) - - let titleSize = self.titleNode.updateLayout(CGSize(width: size.width - 32.0, height: size.height)) - transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize)) - origin.y += titleSize.height + 4.0 - - var entriesHeight: CGFloat = 0.0 - - let textSize = self.textNode.updateLayout(CGSize(width: size.width - 48.0, height: size.height)) - transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize)) - origin.y += textSize.height - - if self.acceptTermsLabelNode.supernode != nil { - origin.y += 21.0 - entriesHeight += 21.0 - - let checkSize = CGSize(width: 22.0, height: 22.0) - let condensedSize = CGSize(width: size.width - 76.0, height: size.height) - - let spacing: CGFloat = 12.0 - let acceptTermsSize = self.acceptTermsLabelNode.updateLayout(condensedSize) - let acceptTermsTotalWidth = checkSize.width + spacing + acceptTermsSize.width - let acceptTermsOriginX = floorToScreenPixels((size.width - acceptTermsTotalWidth) / 2.0) - - transition.updateFrame(node: self.acceptTermsCheckNode, frame: CGRect(origin: CGPoint(x: acceptTermsOriginX, y: origin.y - 3.0), size: checkSize)) - transition.updateFrame(node: self.acceptTermsLabelNode, frame: CGRect(origin: CGPoint(x: acceptTermsOriginX + checkSize.width + spacing, y: origin.y), size: acceptTermsSize)) - origin.y += acceptTermsSize.height - entriesHeight += acceptTermsSize.height - origin.y += 21.0 - } - - let additionalTextSize = self.additionalTextNode.updateLayout(CGSize(width: size.width - 48.0, height: size.height)) - transition.updateFrame(node: self.additionalTextNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - additionalTextSize.width) / 2.0), y: origin.y), size: additionalTextSize)) - origin.y += additionalTextSize.height - if additionalTextSize.height > 0.0 { - entriesHeight += 20.0 - } - - let actionButtonHeight: CGFloat = 44.0 - var minActionsWidth: CGFloat = 0.0 - let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count)) - let actionTitleInsets: CGFloat = 8.0 - - var effectiveActionLayout = TextAlertContentActionLayout.vertical - for actionNode in self.actionNodes { - let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight)) - if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 { - effectiveActionLayout = .vertical - } - switch effectiveActionLayout { - case .horizontal: - minActionsWidth += actionTitleSize.width + actionTitleInsets - case .vertical: - minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets) - } - } - - let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0) - - let contentWidth = max(size.width, minActionsWidth) - - var actionsHeight: CGFloat = 0.0 - switch effectiveActionLayout { - case .horizontal: - actionsHeight = actionButtonHeight - case .vertical: - actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count) - } - - let resultSize = CGSize(width: contentWidth, height: titleSize.height + textSize.height + additionalTextSize.height + entriesHeight + actionsHeight + 3.0 + insets.top + insets.bottom) - - transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - - var actionOffset: CGFloat = 0.0 - let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count)) - var separatorIndex = -1 - var nodeIndex = 0 - for actionNode in self.actionNodes { - if separatorIndex >= 0 { - let separatorNode = self.actionVerticalSeparators[separatorIndex] - switch effectiveActionLayout { - case .horizontal: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel))) - case .vertical: - transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))) - } - } - separatorIndex += 1 - - let currentActionWidth: CGFloat - switch effectiveActionLayout { - case .horizontal: - if nodeIndex == self.actionNodes.count - 1 { - currentActionWidth = resultSize.width - actionOffset - } else { - currentActionWidth = actionWidth - } - case .vertical: - currentActionWidth = resultSize.width - } - - let actionNodeFrame: CGRect - switch effectiveActionLayout { - case .horizontal: - actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += currentActionWidth - case .vertical: - actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight)) - actionOffset += actionButtonHeight - } - - transition.updateFrame(node: actionNode, frame: actionNodeFrame) - - nodeIndex += 1 - } - - return resultSize - } -} +import ComponentFlow +import AlertComponent +import AlertCheckComponent public func webAppTermsAlertController( context: AccountContext, @@ -355,46 +19,51 @@ public func webAppTermsAlertController( bot: AttachMenuBot, completion: @escaping (Bool) -> Void, dismissed: @escaping () -> Void = {} -) -> AlertController { - let theme = defaultDarkColorPresentationTheme - let presentationData: PresentationData - if let updatedPresentationData { - presentationData = updatedPresentationData.initial - } else { - presentationData = context.sharedContext.currentPresentationData.with { $0 } - } +) -> ViewController { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } let strings = presentationData.strings + + let checkState = AlertCheckComponent.ExternalState() - var dismissImpl: ((Bool) -> Void)? - let actions: [TextAlertAction] = [TextAlertAction(type: .defaultAction, title: presentationData.strings.WebApp_DisclaimerContinue, action: { - completion(true) - dismissImpl?(true) - }), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - dismissed() - dismissImpl?(true) - })] + var content: [AnyComponentWithIdentity] = [] + content.append(AnyComponentWithIdentity( + id: "title", + component: AnyComponent( + AlertTitleComponent(title: strings.WebApp_DisclaimerTitle) + ) + )) + content.append(AnyComponentWithIdentity( + id: "text", + component: AnyComponent( + AlertTextComponent(content: .plain(strings.WebApp_DisclaimerText)) + ) + )) + content.append(AnyComponentWithIdentity( + id: "check", + component: AnyComponent( + AlertCheckComponent(title: strings.WebApp_DisclaimerAgree, initialValue: false, externalState: checkState, linkAction: { + context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: strings.WebApp_Disclaimer_URL, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {}) + }) + ) + )) - let title = presentationData.strings.WebApp_DisclaimerTitle - let text = presentationData.strings.WebApp_DisclaimerText - let additionalText: String? = nil + var effectiveUpdatedPresentationData: (PresentationData, Signal) + if let updatedPresentationData { + effectiveUpdatedPresentationData = updatedPresentationData + } else { + effectiveUpdatedPresentationData = (presentationData, context.sharedContext.presentationData) + } - let contentNode = WebAppTermsAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: theme, strings: strings, title: title, text: text, additionalText: additionalText, actions: actions) - contentNode.openTerms = { - context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: presentationData.strings.WebApp_Disclaimer_URL, forceExternal: true, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: nil, dismissInput: { - }) - } - let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode) - controller.dismissed = { outside in - if outside { - dismissed() - } - } - dismissImpl = { [weak controller] animated in - if animated { - controller?.dismissAnimated() - } else { - controller?.dismiss() - } - } - return controller + let alertController = AlertScreen( + configuration: AlertScreen.Configuration(actionAlignment: .vertical), + content: content, + actions: [ + .init(title: strings.WebApp_DisclaimerContinue, type: .default, action: { + completion(checkState.value) + }, isEnabled: checkState.valueSignal), + .init(title: strings.Common_Cancel) + ], + updatedPresentationData: effectiveUpdatedPresentationData + ) + return alertController } diff --git a/submodules/ffmpeg/BUILD b/submodules/ffmpeg/BUILD index ee50f168..2c1758de 100644 --- a/submodules/ffmpeg/BUILD +++ b/submodules/ffmpeg/BUILD @@ -314,6 +314,11 @@ objc_library( "libiconv", "z", ], + sdk_frameworks = [ + "AudioToolbox", + "CoreAudio", + "VideoToolbox" + ], deps = [ ":ffmpeg_lib", "//third-party/libvpx:vpx", diff --git a/third-party/libyuv/BUILD b/third-party/libyuv/BUILD index 402dc978..14e2255c 100644 --- a/third-party/libyuv/BUILD +++ b/third-party/libyuv/BUILD @@ -25,6 +25,7 @@ arch_specific_cflags = select({ "@build_bazel_rules_apple//apple:ios_arm64": common_flags + arm64_specific_flags, "//build-system:ios_sim_arm64": common_flags + arm64_specific_flags, "@build_bazel_rules_apple//apple:ios_x86_64": common_flags + x86_64_specific_flags, + "//conditions:default": common_flags, }) cc_library( diff --git a/third-party/openh264/BUILD b/third-party/openh264/BUILD index 845e670c..34e4f075 100644 --- a/third-party/openh264/BUILD +++ b/third-party/openh264/BUILD @@ -57,18 +57,21 @@ arch_specific_sources = select({ "@build_bazel_rules_apple//apple:ios_arm64": arm64_specific_sources, "//build-system:ios_sim_arm64": arm64_specific_sources, "@build_bazel_rules_apple//apple:ios_x86_64": [], + "//conditions:default": [], }) arch_specific_copts = select({ "@build_bazel_rules_apple//apple:ios_arm64": arm64_specific_copts, "//build-system:ios_sim_arm64": arm64_specific_copts, "@build_bazel_rules_apple//apple:ios_x86_64": [], + "//conditions:default": [], }) arch_specific_textual_hdrs = select({ "@build_bazel_rules_apple//apple:ios_arm64": arm64_specific_textual_hdrs, "//build-system:ios_sim_arm64": arm64_specific_textual_hdrs, "@build_bazel_rules_apple//apple:ios_x86_64": [], + "//conditions:default": [], }) all_sources = arch_specific_sources + [ diff --git a/third-party/recaptcha/BUILD b/third-party/recaptcha/BUILD index bb626687..8f828772 100644 --- a/third-party/recaptcha/BUILD +++ b/third-party/recaptcha/BUILD @@ -6,8 +6,8 @@ load( ) apple_static_xcframework_import( - name = "RecaptchaEnterprise", - xcframework_imports = glob(["RecaptchaEnterprise.xcframework/**"]), + name = "RecaptchaEnterpriseSDK", + xcframework_imports = glob(["RecaptchaEnterpriseSDK.xcframework/**"]), features = [ ], visibility = [ diff --git a/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/Info.plist b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/Info.plist new file mode 100755 index 00000000..ac58f76e --- /dev/null +++ b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/Info.plist @@ -0,0 +1,40 @@ + + + + + AvailableLibraries + + + LibraryIdentifier + ios-arm64 + LibraryPath + RecaptchaEnterpriseSDK.framework + SupportedArchitectures + + arm64 + + SupportedPlatform + ios + + + LibraryIdentifier + ios-arm64_x86_64-simulator + LibraryPath + RecaptchaEnterpriseSDK.framework + SupportedArchitectures + + arm64 + x86_64 + + SupportedPlatform + ios + SupportedPlatformVariant + simulator + + + CFBundlePackageType + XFWK + XCFrameworkFormatVersion + 1.0 + + diff --git a/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/_CodeSignature/CodeDirectory b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/_CodeSignature/CodeDirectory new file mode 100644 index 0000000000000000000000000000000000000000..0bb6997e91534e049cb67ef7516d36cc88b8738e GIT binary patch literal 166 zcmex$kB5nYfngZ~6NmzXNkGyBh?#+yk%585KmyHB3h$stR3&${qchyWQEF0debI=WDU%fzw_kaDZ$+|@s!+8TZJKrx&JS(X3 hi*1`z^~$671bNc)h31IyR0^j2bN7EYEw&4r7yx!8GHd_< literal 0 HcmV?d00001 diff --git a/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/_CodeSignature/CodeRequirements b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/_CodeSignature/CodeRequirements new file mode 100644 index 0000000000000000000000000000000000000000..52af9dc17f6db2fa7c33e3656f41464e51f5bbe3 GIT binary patch literal 184 zcmex$kB5(SUP)?EK~ZLL zYOsqpPzIugA4>B9X^=iHtu~Lg@5w1_Odt~=0K@{xg8*EfkryHWB)Oq1uHw?9tkmQZ XJ^xU!2-rN=K#zzh3ttPzFa`zyY0n#- literal 0 HcmV?d00001 diff --git a/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/_CodeSignature/CodeRequirements-1 b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/_CodeSignature/CodeRequirements-1 new file mode 100644 index 0000000000000000000000000000000000000000..914b68dcc1abd736c87f98b2568cc268f35967d4 GIT binary patch literal 214 zcmex$kB5nYf#Dhh6NmzXZ9vilh?#+yk%585KmjfVmiG=yO-?K*NzO=g%_~VQDk#b< zP7QYPW^fJkh={WAwQvk$IK21!`v(;)&u{AG-@L&aH~s$wo(~W6`4w8!!&WZj6=`oS zmD;P8Y96I0-)+}eK5tjhgL>!H8@VEUH&qpuN7PLXTD + + + + files + + ios-arm64/RecaptchaEnterpriseSDK.framework/Headers/RecaptchaEnterpriseSDK.h + + xWRr/E/wYy7Sp0yB6Gp1DD/dLrU= + + ios-arm64/RecaptchaEnterpriseSDK.framework/Info.plist + + HFvkvcH2FGyO+kbXbemKG0YstR4= + + ios-arm64/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/arm64.swiftdoc + + jUzZ70ucRZDB9eI+3lsf8228LVE= + + ios-arm64/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/arm64.swiftinterface + + d25y94i+fAFY0AJf1eu+rMi5XqI= + + ios-arm64/RecaptchaEnterpriseSDK.framework/Modules/module.modulemap + + ox0fYYZal5uL7KVNXMSAdPPA+Rk= + + ios-arm64/RecaptchaEnterpriseSDK.framework/PrivacyInfo.xcprivacy + + d34/WdITFALLLJjkSHGiQSA6Z3Y= + + ios-arm64/RecaptchaEnterpriseSDK.framework/RecaptchaEnterpriseSDK + + QO1vgAPOySSuI66Twl5ak8ovskE= + + ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Headers/RecaptchaEnterpriseSDK.h + + xWRr/E/wYy7Sp0yB6Gp1DD/dLrU= + + ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Info.plist + + XPpp9kxxu6L0eMmszTmEEo6s1m8= + + ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/arm64.swiftdoc + + brFI5kERGCy+UjGz2+Bnvt2l0AU= + + ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/arm64.swiftinterface + + N24znSq5aAdvb3FvPFG2JQXqtZ4= + + ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/x86_64.swiftdoc + + HcFRMc8FqqbOXybSV9KwUlhbBL0= + + ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/x86_64.swiftinterface + + C6izcS0172Keg5BbaKrBQZFcmLk= + + ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Modules/module.modulemap + + ox0fYYZal5uL7KVNXMSAdPPA+Rk= + + ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/PrivacyInfo.xcprivacy + + d34/WdITFALLLJjkSHGiQSA6Z3Y= + + ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/RecaptchaEnterpriseSDK + + IiGksJB+DWq7iLUrzsImEIQgw34= + + + files2 + + ios-arm64/RecaptchaEnterpriseSDK.framework/Headers/RecaptchaEnterpriseSDK.h + + hash + + xWRr/E/wYy7Sp0yB6Gp1DD/dLrU= + + hash2 + + DN7TwwReF8YggeH+U0TMRMzA+hAtRkT8GSp4WdIfERw= + + + ios-arm64/RecaptchaEnterpriseSDK.framework/Info.plist + + hash + + HFvkvcH2FGyO+kbXbemKG0YstR4= + + hash2 + + HG9mguXmwUgwDI3kYNoKB1kNJsrXCrZsQz1PHuolEQU= + + + ios-arm64/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/arm64.swiftdoc + + hash + + jUzZ70ucRZDB9eI+3lsf8228LVE= + + hash2 + + 4odzfGT3faCpJG3NWt3hfRz6hJae8UU+aCp0ewG9gSQ= + + + ios-arm64/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/arm64.swiftinterface + + hash + + d25y94i+fAFY0AJf1eu+rMi5XqI= + + hash2 + + 57iLgSycgpTwN+RwjZ5Ae8RLMoAokRLkIvxOBQNoyLw= + + + ios-arm64/RecaptchaEnterpriseSDK.framework/Modules/module.modulemap + + hash + + ox0fYYZal5uL7KVNXMSAdPPA+Rk= + + hash2 + + fTtRtwQr7CIyNnBuKrVcw3vj1GVThIJhGK83oIOaOx0= + + + ios-arm64/RecaptchaEnterpriseSDK.framework/PrivacyInfo.xcprivacy + + hash + + d34/WdITFALLLJjkSHGiQSA6Z3Y= + + hash2 + + 9ogbL0FljSMHAXmM5vR4v1xZZKB2oE8aTODyTk0qUj8= + + + ios-arm64/RecaptchaEnterpriseSDK.framework/RecaptchaEnterpriseSDK + + hash + + QO1vgAPOySSuI66Twl5ak8ovskE= + + hash2 + + Wj/ZAiLqd7I0t4P9sqkBGyBdqRw5F1Uk8WCqLAHkqHc= + + + ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Headers/RecaptchaEnterpriseSDK.h + + hash + + xWRr/E/wYy7Sp0yB6Gp1DD/dLrU= + + hash2 + + DN7TwwReF8YggeH+U0TMRMzA+hAtRkT8GSp4WdIfERw= + + + ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Info.plist + + hash + + XPpp9kxxu6L0eMmszTmEEo6s1m8= + + hash2 + + jNmz2Ms0G4kzG3nA4uuN32s0hI0xZegXtFs+RdawsAw= + + + ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/arm64.swiftdoc + + hash + + brFI5kERGCy+UjGz2+Bnvt2l0AU= + + hash2 + + MQ/hBDf541HqUnmhOfBwcIB06j7VZz1VRLhZReMjeuo= + + + ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/arm64.swiftinterface + + hash + + N24znSq5aAdvb3FvPFG2JQXqtZ4= + + hash2 + + 349kl6FePmc+ifxSuhyz6bybqAvQ+M4S5vu5kRh18vM= + + + ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/x86_64.swiftdoc + + hash + + HcFRMc8FqqbOXybSV9KwUlhbBL0= + + hash2 + + xDrbEvkrmidMdLeIbDEjuQcR9+XSoVKLLlBgXHb5PFU= + + + ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/x86_64.swiftinterface + + hash + + C6izcS0172Keg5BbaKrBQZFcmLk= + + hash2 + + nEvIMb3VJ14kpMhYGWZ66qkdddhaYtOtXAjAhtcyKFA= + + + ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Modules/module.modulemap + + hash + + ox0fYYZal5uL7KVNXMSAdPPA+Rk= + + hash2 + + fTtRtwQr7CIyNnBuKrVcw3vj1GVThIJhGK83oIOaOx0= + + + ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/PrivacyInfo.xcprivacy + + hash + + d34/WdITFALLLJjkSHGiQSA6Z3Y= + + hash2 + + 9ogbL0FljSMHAXmM5vR4v1xZZKB2oE8aTODyTk0qUj8= + + + ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/RecaptchaEnterpriseSDK + + hash + + IiGksJB+DWq7iLUrzsImEIQgw34= + + hash2 + + 4JmnqfaRg73MZZaYZNeHYUUZyixtalbnGDoH1CU9vME= + + + + rules + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/_CodeSignature/CodeSignature b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/_CodeSignature/CodeSignature new file mode 100644 index 0000000000000000000000000000000000000000..216e65c1238ee3c6a4af4e586d35476fd7cfa693 GIT binary patch literal 9140 zcmeHN2{e>#`!{0-GsZ3{$rww>^2}HUW#1B_EZJ+!3?|DkGxiEo+47PlO9>^(k`_eT zqiiABQYb;B3H>Nf0)+d?s%nz=Y`nOm^9Pb|?f2 z6^^WA(`(H%4{zCLm3lHElzwZ!n-Ob~Z%aeC!9_f<+yH`u-5$mv1hu!t3IPHv0V#wu z2ng^cqs{$^%2-)Inl;ERB)%|cM5Zw)o)jX1L81Di4T2cnR2qd5h7|#XS@YP1coybu zqf!}YBLiUVN*){*(8A%cn(FFUz*qC5(#GUqvM)7&OhcO+ zqyN!H-;T_Uf|}vJ9~rgu$V-0|z=U9y{%r8xu`?ljY~VN&%7j4JIu#FlOWLI*Tqg(K z65aBlm1ihLE~uBi_OL{!VD>iS!6;L&Ir)QZ2jUWdSSw5a&6N$K(5Jmb%aXg}6ORm4 zQufHa*m2TA!H&AJ49Jt0Q#%=%JR17cO-e!1^j>`3j+tGV#{$JfPhUVau_M(lK#P># zSM-bRyS+|QpLXgQJ{bG#GRJksg%1H`mSswqcd}W!g&qPzagJ70$?f@_vW% z)bpaiq74Pvmf@HJtJeVn-90hU%r{=DX{yfp=TqxT=o#Ymu3v{<;-TyiHb_DAYan_8 z5CortxDW*L6~+Z{fFDo@8z8|NlY)r?B2mKY?@`RsgArfe`TU`*e`?yyDw4|`Fy`P| z&jEqMIUx|3CZGYREy@5SM$VhT2+&qhp%Uo<$^@2CDHExF^CFvE9;s;^36m}V+EEbdnGJ=Y%&bu>lq&0{QC@IRTHAm1CA5UFkGT{ zMY8xHnf}$woqWaoL2#a`F!^{$dXv%uWH2G&AczM4#nt#&t~U=NaDW~3z$LEwOZ@mP zz%U`Kuq2L#F(F*wjxg}e_AQPPNpJ`dJq0K%Vh$7{vX%uLbP#kxLPC}x$2`~&2`WHz z-S^2*Ao}9!=!$J*UkbsWNJfJJjp0q9qlw>R92xBqh9>xjp#ums26#oM2lQADuq9z?p^+X5J$%@N1 z3ZR0ztj)lg-}h(5*Z>-V$e@771J`7b{mA|dy7Eu1IS*g18rhagq1CSvkM_R}IXV@5 zKLoJ8DRE%m0kLcyGyGKXQT-&vuJeZkA6$D!Kl-ssTA~<6AN__``wT#Z{gHjvJ3c9J%!!J9k10M6446SQ^;}`ns zS=Gvwr~CI})SG3x`}lf%x=u(-2}~a^OQuKfyd&m%=f!IuRpv$nfjNH>V8A=BENiViOy@=5YOtfjBAuIkf>ycuiXSCoATXZYzscVO<` zRS?N=TA-*lFXEhm=w$3_K$HWC zNR^FGn^Gh}ASk5C)=iGinpTb(PmVt7iepimPTl~ zAFA?;tcZKrf{W}9h5VtmjQ@dt_0OqNONRXG{Mgfl@Uc%v+s$v?)&E*pS_ixRE^j2t zE7+_ntslOKF}os7rFBkg7aFSy0L#Ly`m6ILF@YQblx#`1DcwM&bh*~~X3s8$W# zOMH@GqSb|XI+lK8$JqYixHo+1HT12$I!!fG$)Tk(YTmC5$a=09+iED&<9FQNohTQ! z&wO;#He}R_*DNwMW}w`*ThebkT=ZdSX>Rt&#llK?d8rP}vrRkW-y~e;J9Mq?h+t#1 zYwhz5Q`c9TH1uoujNBOTsllmYulE;UQ*Dv0D%fS3qSYdI=hI`Kk2BA`q9c5@osm(I zrDwTwY^yYpnTn1_8wV>J12K7(qxvQT@Nc*Wbq1JF2LSaQ=b*rHn)FLXLR&-~kS2Ko zB#`iYqfG!b2D8X@K)|mn*-v%!msiF2d0tESiV{kc(@uvo8r*eTf_@ zN9KTY_k(=X$Sx&0z^iGecv#!980gYL z!NtK}(9%*`xDwt0IbIxcWv{@M-C^1tY#E~4b61tWtm(`(oES3BYZhTRI!b>|XuGVAVZy0XIyC?Lo;gvx-hy_iQM*s_!fr6z|hpW^y%@*C%X|rX@dGOhiF6G@lCJWxm`vo&b<`T?Ii#% z-xP=>1Qfdkv`hGUUUukem@s{{wr}j+RbNy_Z;MF}ry7qw=6Me&EvXT}&}WpNrf3Qc zbTMBX`D2AfO3Klbc9xt{D4mphfB~kpnbTIaIrb?O^JzeBRNn>jxJ0}|xG{|}A4782Cp{2PI8l$A5!irI3 z6%}JUW3-K>xvd=$8CaF9Tw5m*<9jU)ntz`7uYQZ+oxJ*~_<)q>TD z_8yz9Eo`@0J9-*>+Uv09sVq+Xu0gZK@A9$pvQs758fiEOZZWkZZ1Zsr@^LT;S*Ed3 z<=bxH8uMq`s4{N|beDzvZ2DY2E;zoB!aDcbCoFweVDo4H)Z z7`Njv88I5wvF}Bb{H-4J>t2rb^T!6R!25(R&xECP63^m#_hYGcnda6LigK+Xc8;&d zT?i&;)~t*{)~$(~*-&Lpyo%ZzEO=5X70*Sw7p!ZWC6TA+vL~=BB~0T;+_tD41@hMf zxrZ_1+k!6XRc-fG+E#rsu=|>(=EkZC&17V9VF}zL&>^DGZS?w?Y9W{PCMR_rthwcn_OZ5 zQKOP3t0@yx@`3bTlSJE`OFnu$tD#&9yCl$uInG6kf}p+JS;4{q1j^+o#|hLLdllz1 zdG3J>6AC8C;|mOvi^|(J+aaU4`KKoh?8XA~^$d`_t(*4+v>gS^8W;q>fPYxkS}e1n zep6=q!QvKHp0Cj@D|oSZCku1{EM5)3u`*~-#DVoUmIz{y0ahFkTcDQ0Kh9-K@$v`j zZdL}^MfQmNzt_}C0-L54JM@qbG?ACn7?Ywi56v|u(`Sr_+dT_w>6ALX%~PIJ!ra3m z>r1w<=V9zWGeTRcyZvjrC)y4BQ=ndFQMJWqm`t>rc zTE1>+Y03J^UALGZ-vlA{>OUchYOOL`S_kn5G><#V&dd(W4^FK~16AgWS1i&72L&*{ z{qj8su(I7rKyHz#fNW>YH!8RsQ3)&r2CHg6<+}f+?C#erjSEm^u{Q~rBA@`s<;&(v z|KQQfYKDwO=liK}Xct^W7K7Rf=dFkcb!mJ3dHr2$^k1mmFZfu+^yigcx_nQA40;p< z2*b`Zu<-MJGUhKVBB`0>$T^{}JPzkR>|3-@HUlz$GR89y#T|`(7IFjGt5ZMr-wWTr60&BX z|5~`vX~*z=&->bSCwJ7+kI z*n>={5uKy!t6Z&rU~_w2fP5aIjaRw097&^U;UDIb-&Ydf9cnc9b3=D(eMZ#2 z(|+5wnL>hRn?8A{UQZTXdq<=|kWi`oelngXv0vu{M>{n}#u9N$tdTb~?vI<8np#Pr zqs@ON>LXVQKyzO>*Q4=X9)o`M{vRLlL%s@b#q}JO< zsyTHuqaGc@t(moF#FJ9*2OX?)%X#0D*uQI+_P|TKJ+Fj)-uCFGHTs3y?U*u3;=dQ{ zdXx<%zNL9%>&LO_Te`IBn!)F4B`fmFUA<1D%8srjS3Knd4AowgcS1Ug1`O#zySEx0 zII@$!zkJ<&AFn1=6KYe|+WaGtRsv~5l&=#OCU3ViV|cgsVmL=Jm9Z~!I!#*q^+uCw z=1jX?o6eQOwf*pN1*c8PRQu!JapT)`o8EPeebhTSA{$ku?bA~?I*W^L#GTkQl*Cc6 zYNVoNgT><~cX=xd{lkugVjS>GX_&AJJhJ8qU(Sk!hF$HIl^RW5_f zq|^f#IeDfhPm05RXl_XK1;n+;q%IYhXoa&u>d=m`*GYn1hS4#pd-jV5mPM4nE z<&CKiQqgL;9p3VgXzC$&wa{<3EMn(#pku{ke1y0{?Z<8SOOx?WBx=GcuE(quHl6i5J=ssh zKE+-*HQchU5c0OuOiXHQe_!a7`pYrd8wt&v9~xIHKiVXJyS05IG`=#s@4TcJ+x+W) E0BwKT;{X5v literal 0 HcmV?d00001 diff --git a/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64/RecaptchaEnterpriseSDK.framework/Headers/RecaptchaEnterpriseSDK.h b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64/RecaptchaEnterpriseSDK.framework/Headers/RecaptchaEnterpriseSDK.h new file mode 100755 index 00000000..faa7f5ff --- /dev/null +++ b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64/RecaptchaEnterpriseSDK.framework/Headers/RecaptchaEnterpriseSDK.h @@ -0,0 +1,519 @@ +// Generated by Apple Swift version 6.1.2 (swiftlang-6.1.2.1.2 clang-1700.0.13.5) +#ifndef RECAPTCHAENTERPRISESDK_SWIFT_H +#define RECAPTCHAENTERPRISESDK_SWIFT_H +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgcc-compat" + +#if !defined(__has_include) +# define __has_include(x) 0 +#endif +#if !defined(__has_attribute) +# define __has_attribute(x) 0 +#endif +#if !defined(__has_feature) +# define __has_feature(x) 0 +#endif +#if !defined(__has_warning) +# define __has_warning(x) 0 +#endif + +#if __has_include() +# include +#endif + +#pragma clang diagnostic ignored "-Wauto-import" +#if defined(__OBJC__) +#include +#endif +#if defined(__cplusplus) +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#endif +#if defined(__cplusplus) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnon-modular-include-in-framework-module" +#if defined(__arm64e__) && __has_include() +# include +#else +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-macro-identifier" +# ifndef __ptrauth_swift_value_witness_function_pointer +# define __ptrauth_swift_value_witness_function_pointer(x) +# endif +# ifndef __ptrauth_swift_class_method_pointer +# define __ptrauth_swift_class_method_pointer(x) +# endif +#pragma clang diagnostic pop +#endif +#pragma clang diagnostic pop +#endif + +#if !defined(SWIFT_TYPEDEFS) +# define SWIFT_TYPEDEFS 1 +# if __has_include() +# include +# elif !defined(__cplusplus) +typedef unsigned char char8_t; +typedef uint_least16_t char16_t; +typedef uint_least32_t char32_t; +# endif +typedef float swift_float2 __attribute__((__ext_vector_type__(2))); +typedef float swift_float3 __attribute__((__ext_vector_type__(3))); +typedef float swift_float4 __attribute__((__ext_vector_type__(4))); +typedef double swift_double2 __attribute__((__ext_vector_type__(2))); +typedef double swift_double3 __attribute__((__ext_vector_type__(3))); +typedef double swift_double4 __attribute__((__ext_vector_type__(4))); +typedef int swift_int2 __attribute__((__ext_vector_type__(2))); +typedef int swift_int3 __attribute__((__ext_vector_type__(3))); +typedef int swift_int4 __attribute__((__ext_vector_type__(4))); +typedef unsigned int swift_uint2 __attribute__((__ext_vector_type__(2))); +typedef unsigned int swift_uint3 __attribute__((__ext_vector_type__(3))); +typedef unsigned int swift_uint4 __attribute__((__ext_vector_type__(4))); +#endif + +#if !defined(SWIFT_PASTE) +# define SWIFT_PASTE_HELPER(x, y) x##y +# define SWIFT_PASTE(x, y) SWIFT_PASTE_HELPER(x, y) +#endif +#if !defined(SWIFT_METATYPE) +# define SWIFT_METATYPE(X) Class +#endif +#if !defined(SWIFT_CLASS_PROPERTY) +# if __has_feature(objc_class_property) +# define SWIFT_CLASS_PROPERTY(...) __VA_ARGS__ +# else +# define SWIFT_CLASS_PROPERTY(...) +# endif +#endif +#if !defined(SWIFT_RUNTIME_NAME) +# if __has_attribute(objc_runtime_name) +# define SWIFT_RUNTIME_NAME(X) __attribute__((objc_runtime_name(X))) +# else +# define SWIFT_RUNTIME_NAME(X) +# endif +#endif +#if !defined(SWIFT_COMPILE_NAME) +# if __has_attribute(swift_name) +# define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X))) +# else +# define SWIFT_COMPILE_NAME(X) +# endif +#endif +#if !defined(SWIFT_METHOD_FAMILY) +# if __has_attribute(objc_method_family) +# define SWIFT_METHOD_FAMILY(X) __attribute__((objc_method_family(X))) +# else +# define SWIFT_METHOD_FAMILY(X) +# endif +#endif +#if !defined(SWIFT_NOESCAPE) +# if __has_attribute(noescape) +# define SWIFT_NOESCAPE __attribute__((noescape)) +# else +# define SWIFT_NOESCAPE +# endif +#endif +#if !defined(SWIFT_RELEASES_ARGUMENT) +# if __has_attribute(ns_consumed) +# define SWIFT_RELEASES_ARGUMENT __attribute__((ns_consumed)) +# else +# define SWIFT_RELEASES_ARGUMENT +# endif +#endif +#if !defined(SWIFT_WARN_UNUSED_RESULT) +# if __has_attribute(warn_unused_result) +# define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +# else +# define SWIFT_WARN_UNUSED_RESULT +# endif +#endif +#if !defined(SWIFT_NORETURN) +# if __has_attribute(noreturn) +# define SWIFT_NORETURN __attribute__((noreturn)) +# else +# define SWIFT_NORETURN +# endif +#endif +#if !defined(SWIFT_CLASS_EXTRA) +# define SWIFT_CLASS_EXTRA +#endif +#if !defined(SWIFT_PROTOCOL_EXTRA) +# define SWIFT_PROTOCOL_EXTRA +#endif +#if !defined(SWIFT_ENUM_EXTRA) +# define SWIFT_ENUM_EXTRA +#endif +#if !defined(SWIFT_CLASS) +# if __has_attribute(objc_subclassing_restricted) +# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_CLASS_EXTRA +# define SWIFT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA +# else +# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA +# define SWIFT_CLASS_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA +# endif +#endif +#if !defined(SWIFT_RESILIENT_CLASS) +# if __has_attribute(objc_class_stub) +# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME) __attribute__((objc_class_stub)) +# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_class_stub)) SWIFT_CLASS_NAMED(SWIFT_NAME) +# else +# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME) +# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) SWIFT_CLASS_NAMED(SWIFT_NAME) +# endif +#endif +#if !defined(SWIFT_PROTOCOL) +# define SWIFT_PROTOCOL(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA +# define SWIFT_PROTOCOL_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA +#endif +#if !defined(SWIFT_EXTENSION) +# define SWIFT_EXTENSION(M) SWIFT_PASTE(M##_Swift_, __LINE__) +#endif +#if !defined(OBJC_DESIGNATED_INITIALIZER) +# if __has_attribute(objc_designated_initializer) +# define OBJC_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) +# else +# define OBJC_DESIGNATED_INITIALIZER +# endif +#endif +#if !defined(SWIFT_ENUM_ATTR) +# if __has_attribute(enum_extensibility) +# define SWIFT_ENUM_ATTR(_extensibility) __attribute__((enum_extensibility(_extensibility))) +# else +# define SWIFT_ENUM_ATTR(_extensibility) +# endif +#endif +#if !defined(SWIFT_ENUM) +# define SWIFT_ENUM(_type, _name, _extensibility) enum _name : _type _name; enum SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type +# if __has_feature(generalized_swift_name) +# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type +# else +# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) SWIFT_ENUM(_type, _name, _extensibility) +# endif +#endif +#if !defined(SWIFT_UNAVAILABLE) +# define SWIFT_UNAVAILABLE __attribute__((unavailable)) +#endif +#if !defined(SWIFT_UNAVAILABLE_MSG) +# define SWIFT_UNAVAILABLE_MSG(msg) __attribute__((unavailable(msg))) +#endif +#if !defined(SWIFT_AVAILABILITY) +# define SWIFT_AVAILABILITY(plat, ...) __attribute__((availability(plat, __VA_ARGS__))) +#endif +#if !defined(SWIFT_WEAK_IMPORT) +# define SWIFT_WEAK_IMPORT __attribute__((weak_import)) +#endif +#if !defined(SWIFT_DEPRECATED) +# define SWIFT_DEPRECATED __attribute__((deprecated)) +#endif +#if !defined(SWIFT_DEPRECATED_MSG) +# define SWIFT_DEPRECATED_MSG(...) __attribute__((deprecated(__VA_ARGS__))) +#endif +#if !defined(SWIFT_DEPRECATED_OBJC) +# if __has_feature(attribute_diagnose_if_objc) +# define SWIFT_DEPRECATED_OBJC(Msg) __attribute__((diagnose_if(1, Msg, "warning"))) +# else +# define SWIFT_DEPRECATED_OBJC(Msg) SWIFT_DEPRECATED_MSG(Msg) +# endif +#endif +#if defined(__OBJC__) +#if !defined(IBSegueAction) +# define IBSegueAction +#endif +#endif +#if !defined(SWIFT_EXTERN) +# if defined(__cplusplus) +# define SWIFT_EXTERN extern "C" +# else +# define SWIFT_EXTERN extern +# endif +#endif +#if !defined(SWIFT_CALL) +# define SWIFT_CALL __attribute__((swiftcall)) +#endif +#if !defined(SWIFT_INDIRECT_RESULT) +# define SWIFT_INDIRECT_RESULT __attribute__((swift_indirect_result)) +#endif +#if !defined(SWIFT_CONTEXT) +# define SWIFT_CONTEXT __attribute__((swift_context)) +#endif +#if !defined(SWIFT_ERROR_RESULT) +# define SWIFT_ERROR_RESULT __attribute__((swift_error_result)) +#endif +#if defined(__cplusplus) +# define SWIFT_NOEXCEPT noexcept +#else +# define SWIFT_NOEXCEPT +#endif +#if !defined(SWIFT_C_INLINE_THUNK) +# if __has_attribute(always_inline) +# if __has_attribute(nodebug) +# define SWIFT_C_INLINE_THUNK inline __attribute__((always_inline)) __attribute__((nodebug)) +# else +# define SWIFT_C_INLINE_THUNK inline __attribute__((always_inline)) +# endif +# else +# define SWIFT_C_INLINE_THUNK inline +# endif +#endif +#if defined(_WIN32) +#if !defined(SWIFT_IMPORT_STDLIB_SYMBOL) +# define SWIFT_IMPORT_STDLIB_SYMBOL __declspec(dllimport) +#endif +#else +#if !defined(SWIFT_IMPORT_STDLIB_SYMBOL) +# define SWIFT_IMPORT_STDLIB_SYMBOL +#endif +#endif +#if defined(__OBJC__) +#if 1 /* #if __has_feature(objc_modules) */ +#if __has_warning("-Watimport-in-framework-header") +#pragma clang diagnostic ignored "-Watimport-in-framework-header" +#endif +// Rewritten: @import Foundation; +// From module Foundation +#import +// Rewritten: @import ObjectiveC; +// From module ObjectiveC +// From module ObjectiveC.NSObjCRuntime +#import +// From module ObjectiveC.NSObject +#import +// From module ObjectiveC.message +#import +// From module ObjectiveC.objc +#import +// From module ObjectiveC.objc_api +#import +// From module ObjectiveC.objc_auto +#import +// From module ObjectiveC.objc_exception +#import +// From module ObjectiveC.objc_sync +#import +// From module ObjectiveC.runtime +#import +#endif + +#endif +#pragma clang diagnostic ignored "-Wproperty-attribute-mismatch" +#pragma clang diagnostic ignored "-Wduplicate-method-arg" +#if __has_warning("-Wpragma-clang-attribute") +# pragma clang diagnostic ignored "-Wpragma-clang-attribute" +#endif +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#pragma clang diagnostic ignored "-Wnullability" +#pragma clang diagnostic ignored "-Wdollar-in-identifier-extension" +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" + +#if __has_attribute(external_source_symbol) +# pragma push_macro("any") +# undef any +# pragma clang attribute push(__attribute__((external_source_symbol(language="Swift", defined_in="RecaptchaEnterpriseSDK",generated_declaration))), apply_to=any(function,enum,objc_interface,objc_category,objc_protocol)) +# pragma pop_macro("any") +#endif + +#if defined(__OBJC__) + +@class NSString; +@class RecaptchaClient; +@class NSError; +/// Interface to interact with reCAPTCHA. +SWIFT_CLASS("_TtC22RecaptchaEnterpriseSDK9Recaptcha") +@interface Recaptcha : NSObject +/// Builds a new reCAPTCHA Client for the given Site Key. +/// The SDK accepts one Site Key. Passing a different Site Key will throw an exception. +/// \param siteKey reCAPTCHA Site Key for the app. +/// +/// \param completion Callback function to return the RecaptchaClient or an error. +/// ++ (void)fetchClientWithSiteKey:(NSString * _Nonnull)siteKey completion:(void (^ _Nonnull)(RecaptchaClient * _Nullable, NSError * _Nullable))completion; +/// Builds a new reCAPTCHA Client for the given Site Key and timeout. +/// The SDK accepts one Site Key. Passing a different Site Key will +/// throw an exception. +/// At least a 10000 milliseconds timeout is suggested to allow for slow +/// networking, though in some cases longer timeouts may be necessary. The +/// minimum allowable value is 5000 milliseconds. +/// \param siteKey reCAPTCHA Site Key for the app. +/// +/// \param timeout Timeout for getClient in milliseconds. +/// +/// \param completion Callback function to return the RecaptchaClient or an error. +/// ++ (void)getClientWithSiteKey:(NSString * _Nonnull)siteKey withTimeout:(double)timeout completion:(void (^ _Nonnull)(RecaptchaClient * _Nullable, NSError * _Nullable))completion SWIFT_DEPRECATED_MSG("Use the new api `fetchClient(withSiteKey:completion:)` instead."); +/// Builds a new reCAPTCHA Client for the given Site Key and timeout. +/// The SDK accepts one Site Key. Passing a different Site Key will +/// throw an exception. +/// This function will timeout after 10 seconds. +/// \param siteKey reCAPTCHA Site Key for the app. +/// +/// \param completion Callback function to return the RecaptchaClient or an error. +/// ++ (void)getClientWithSiteKey:(NSString * _Nonnull)siteKey completion:(void (^ _Nonnull)(RecaptchaClient * _Nullable, NSError * _Nullable))completion SWIFT_DEPRECATED_MSG("Use the new api `fetchClient(withSiteKey:completion:)` instead."); +/// Builds a new reCAPTCHA Client for the given SiteKey. +/// This function will timeout after 10 seconds. +/// \param siteKey reCAPTCHA Site Key for the app. +/// +/// \param completionHandler Callback function to return the RecaptchaClient or an error. +/// ++ (void)getClientWithSiteKey:(NSString * _Nonnull)siteKey completionHandler:(void (^ _Nonnull)(RecaptchaClient * _Nullable, NSError * _Nullable))completionHandler SWIFT_DEPRECATED_MSG("Use the new api `fetchClient(withSiteKey:completion:)` instead."); +- (nonnull instancetype)init SWIFT_UNAVAILABLE; ++ (nonnull instancetype)new SWIFT_UNAVAILABLE_MSG("-init is unavailable"); +@end + +enum RecaptchaActionType : NSInteger; +/// Action intended to be protected by reCAPTCHA. This object should be passed +/// to RecaptchaClient.execute. +SWIFT_CLASS("_TtC22RecaptchaEnterpriseSDK15RecaptchaAction") +@interface RecaptchaAction : NSObject +/// Creates an object with a predefined reCAPTCHA action. +/// \param action The type of the action. +/// +/// +/// returns: +/// A RecaptchaAction object with the given action type. +- (nonnull instancetype)initWithAction:(enum RecaptchaActionType)action OBJC_DESIGNATED_INITIALIZER SWIFT_DEPRECATED_MSG("Please use customAction with the regular RecaptchaAction.custom() function"); +/// Indicates that the protected action is a Login lwPz0qSe. +SWIFT_CLASS_PROPERTY(@property (nonatomic, class, readonly, strong) RecaptchaAction * _Nonnull login;) ++ (RecaptchaAction * _Nonnull)login SWIFT_WARN_UNUSED_RESULT; +/// Indicates that the protected action is a Signup lwPz0qSe. +SWIFT_CLASS_PROPERTY(@property (nonatomic, class, readonly, strong) RecaptchaAction * _Nonnull signup;) ++ (RecaptchaAction * _Nonnull)signup SWIFT_WARN_UNUSED_RESULT; +/// Creates a custom action from a String. ++ (RecaptchaAction * _Nonnull)custom:(NSString * _Nonnull)action SWIFT_WARN_UNUSED_RESULT; +- (nonnull instancetype)init SWIFT_UNAVAILABLE; ++ (nonnull instancetype)new SWIFT_UNAVAILABLE_MSG("-init is unavailable"); +@end + +/// Action type intended to be protected by reCAPTCHA. +typedef SWIFT_ENUM(NSInteger, RecaptchaActionType, open) { +/// Indicates that the protected action is a Login lwPz0qSe. + RecaptchaActionTypeLogin = 0, +/// Indicates that the protected action is a Signup lwPz0qSe. + RecaptchaActionTypeSignup = 1, +/// When a custom action is specified, reCAPTCHA uses this value automatically. + RecaptchaActionTypeOther = 2, +}; + +@class RecaptchaToken; +@class RecaptchaError; +/// Interface to interact with reCAPTCHA. +SWIFT_CLASS("_TtC22RecaptchaEnterpriseSDK15RecaptchaClient") +@interface RecaptchaClient : NSObject +/// Executes reCAPTCHA on a user action. +/// It is suggested the usage of 10 seconds for the timeout. The minimum value +/// 5 seconds. +/// \param action The user action to protect. +/// +/// \param timeout Timeout for execute in milliseconds. +/// +/// \param completion Callback function to return the execute response. +/// +- (void)executeWithAction:(RecaptchaAction * _Nonnull)action withTimeout:(double)timeout completion:(void (^ _Nonnull)(NSString * _Nullable, NSError * _Nullable))completion; +/// Executes reCAPTCHA on a user action. +/// This function throws a timeout exception after 10 seconds. +/// \param action The user action to protect. +/// +/// \param completion Callback function to return the execute response. +/// +- (void)executeWithAction:(RecaptchaAction * _Nonnull)action completion:(void (^ _Nonnull)(NSString * _Nullable, NSError * _Nullable))completion; +/// Executes reCAPTCHA on a user action. +/// This function throws a timeout exception after 10 seconds. +/// \param action The user action to protect. +/// +/// \param completion Callback function to return the execute response. +/// +- (void)execute:(RecaptchaAction * _Nonnull)action completion:(void (^ _Nonnull)(NSString * _Nullable, NSError * _Nullable))completion; +/// Executes reCAPTCHA on a user action. +/// This function throws a timeout exception after 10 seconds. +/// \param action The user action to protect. +/// +/// \param completionHandler Callback function to return the execute response. +/// +- (void)execute:(RecaptchaAction * _Nonnull)action completionHandler:(void (^ _Nonnull)(RecaptchaToken * _Nullable, RecaptchaError * _Nullable))completionHandler SWIFT_DEPRECATED_MSG("Use `execute(withAction:completion:)` instead."); +- (nonnull instancetype)init SWIFT_UNAVAILABLE; ++ (nonnull instancetype)new SWIFT_UNAVAILABLE_MSG("-init is unavailable"); +@end + +SWIFT_CLASS("_TtC22RecaptchaEnterpriseSDK17RecaptchaConstant") +@interface RecaptchaConstant : NSObject +SWIFT_CLASS_PROPERTY(@property (nonatomic, class, readonly, copy) NSString * _Nonnull clientVersion;) ++ (NSString * _Nonnull)clientVersion SWIFT_WARN_UNUSED_RESULT; +SWIFT_CLASS_PROPERTY(@property (nonatomic, class, readonly) double defaultTimeoutExecute;) ++ (double)defaultTimeoutExecute SWIFT_WARN_UNUSED_RESULT; +SWIFT_CLASS_PROPERTY(@property (nonatomic, class, readonly) double defaultTimeoutInit;) ++ (double)defaultTimeoutInit SWIFT_WARN_UNUSED_RESULT; +- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER; +@end + +enum RecaptchaErrorCode : NSInteger; +@class NSCoder; +/// Error class for reCAPTCHA Events. +SWIFT_CLASS("_TtC22RecaptchaEnterpriseSDK14RecaptchaError") +@interface RecaptchaError : NSError +/// Code relative to the error that was thrown. It maps to RecaptchaErrorCode. +@property (nonatomic, readonly) enum RecaptchaErrorCode errorCode; +/// Human readable error message. +@property (nonatomic, readonly, copy) NSString * _Nonnull errorMessage; +/// Required by NSError but should not be used. +- (nonnull instancetype)initWithCoder:(NSCoder * _Nonnull)coder SWIFT_UNAVAILABLE; +- (nonnull instancetype)initWithDomain:(NSString * _Nonnull)domain code:(NSInteger)code userInfo:(NSDictionary * _Nullable)dict SWIFT_UNAVAILABLE; +@end + +/// List of errors that can be returned from the SDK. +/// IMPORTANT: This list is add-only. Never change any existing value, since this class is +/// publicly visible and customers rely on these values to do error checking. +typedef SWIFT_ENUM(NSInteger, RecaptchaErrorCode, open) { +/// Unknown error occurred during the lwPz0qSe. + RecaptchaErrorCodeErrorCodeUnknown = 0, +/// reCAPTCHA cannot connect to Google servers, please make sure the app has network access. + RecaptchaErrorCodeErrorNetworkError = 1, +/// The site key used to call reCAPTCHA is invalid. + RecaptchaErrorCodeErrorInvalidSiteKey = 2, +/// Cannot create a reCAPTCHA interface because the key used cannot be used on iOS. +/// Please register new site key with the key type set to “iOS App” via +/// Create Key. + RecaptchaErrorCodeErroInvalidKeyType = 3, +/// Cannot create a reCAPTCHA interface because the site key used doesn’t support the calling +/// package. + RecaptchaErrorCodeErrorInvalidPackageName = 4, +/// reCAPTCHA cannot accept the action used, see custom +/// action guidelines. + RecaptchaErrorCodeErrorInvalidAction = 5, +/// reCAPTCHA cannot accept timeout provided, see +/// timeout guidelines. + RecaptchaErrorCodeErrorInvalidTimeout = 6, +/// No network was found in the device. + RecaptchaErrorCodeErrorNoNetwork = 7, +/// reCAPTCHA has faced an internal error, please try again in a bit. + RecaptchaErrorCodeErrorCodeInternalError = 100, +}; + +/// Swift implementation for RecaptchaTokenSwift that holds the response of a successful +/// execute call. +SWIFT_CLASS("_TtC22RecaptchaEnterpriseSDK14RecaptchaToken") SWIFT_DEPRECATED_MSG("Newer implementations return the Token as a string.") +@interface RecaptchaToken : NSObject +/// The Token to be used for verification. +@property (nonatomic, readonly, copy) NSString * _Nonnull recaptchaToken; +- (nonnull instancetype)init:(NSString * _Nonnull)mobiletecqlyzr OBJC_DESIGNATED_INITIALIZER SWIFT_DEPRECATED_MSG("Newer implementations return the Token as a string."); +- (nonnull instancetype)init SWIFT_UNAVAILABLE; ++ (nonnull instancetype)new SWIFT_UNAVAILABLE_MSG("-init is unavailable"); +@end + +#endif +#if __has_attribute(external_source_symbol) +# pragma clang attribute pop +#endif +#if defined(__cplusplus) +#endif +#pragma clang diagnostic pop +#endif diff --git a/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64/RecaptchaEnterpriseSDK.framework/Info.plist b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64/RecaptchaEnterpriseSDK.framework/Info.plist new file mode 100755 index 0000000000000000000000000000000000000000..c049078eae2b7c937b1395bb2b1916fe2c42028c GIT binary patch literal 685 zcmZ`#zi-n(7`@LFTAr&oSeX=^5m)0m2*?)FHB#$JTrUc>fE*Kk92EU_#xvovhmciv6Kcbr{+LJ$pae2 zOax{UvB0zO#z_3%6Ln{M2uoIn*zNv-s=#8(AwKUram1c|tsDZHE$_b%d{yFyvH~x8WzoD(73i z>TnWxs%BSBO|7ooyk#ujet+gH2CVd1I1m99vDQ%QmbOrnFcv9I|u(W#SQP21Xi1ms%meK02qA@g& z?w~I6(KGZMy+AwY3;KqBpg%AIC76Q-G{J($@Dg6ZYj^{1VFy0INB9Jv;VXQHU+`NQ ZRmPQ)Qc>oW1{xj(b}PD(*Ssiq{{o9>(4znV literal 0 HcmV?d00001 diff --git a/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/arm64.swiftdoc b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/arm64.swiftdoc new file mode 100755 index 0000000000000000000000000000000000000000..d3d178d570a9b991eeb6db3f6ea0eea7c75e4643 GIT binary patch literal 13328 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 F{s-2>T;>1( literal 0 HcmV?d00001 diff --git a/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/arm64.swiftinterface b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/arm64.swiftinterface new file mode 100755 index 00000000..46c8ede3 --- /dev/null +++ b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/arm64.swiftinterface @@ -0,0 +1,107 @@ +// swift-interface-format-version: 1.0 +// swift-compiler-version: Apple Swift version 6.1.2 (swiftlang-6.1.2.1.2 clang-1700.0.13.5) +// swift-module-flags: -target arm64-apple-ios15.0 -enable-objc-interop -enable-library-evolution -swift-version 6 -enforce-exclusivity=checked -O -enable-experimental-feature AccessLevelOnImport -enable-bare-slash-regex -module-name RecaptchaEnterpriseSDK -package-name googlemac/iPhone/recaptcha/enterprise +// swift-module-flags-ignorable: -no-verify-emitted-module-interface -interface-compiler-version 6.1.2 +import CryptoKit +import DeviceCheck +import Foundation +import Network +import Security +import Swift +import UIKit +import WebKit +import _Concurrency +import _StringProcessing +import _SwiftConcurrencyShims +@objc @_inheritsConvenienceInitializers @objcMembers public class RecaptchaConstant : ObjectiveC.NSObject { + @objc public static let clientVersion: Swift.String + @objc public static let defaultTimeoutExecute: Swift.Double + @objc public static let defaultTimeoutInit: Swift.Double + @objc override dynamic public init() + @objc deinit +} +@_inheritsConvenienceInitializers @_hasMissingDesignatedInitializers @objc public class Recaptcha : ObjectiveC.NSObject { + @objc public static func fetchClient(withSiteKey siteKey: Swift.String, completion: @escaping @Sendable (RecaptchaEnterpriseSDK.RecaptchaClient?, Foundation.NSError?) -> Swift.Void) + public static func fetchClient(withSiteKey siteKey: Swift.String) async throws -> RecaptchaEnterpriseSDK.RecaptchaClient + @available(*, deprecated, message: "Use the new api `fetchClient(withSiteKey:completion:)` instead.") + @objc public static func getClient(withSiteKey siteKey: Swift.String, withTimeout timeout: Swift.Double, completion: @escaping @Sendable (RecaptchaEnterpriseSDK.RecaptchaClient?, Foundation.NSError?) -> Swift.Void) + @available(*, deprecated, message: "Use the new api `fetchClient(withSiteKey:completion:)` instead.") + @objc public static func getClient(withSiteKey siteKey: Swift.String, completion: @escaping @Sendable (RecaptchaEnterpriseSDK.RecaptchaClient?, Foundation.NSError?) -> Swift.Void) + @available(*, deprecated, message: "Use the new api `fetchClient(withSiteKey:completion:)` instead.") + @objc public static func getClient(siteKey: Swift.String, completionHandler: @escaping @Sendable (RecaptchaEnterpriseSDK.RecaptchaClient?, Foundation.NSError?) -> Swift.Void) + @objc deinit +} +@available(*, deprecated, message: "Use RecaptchaAction enums instead.") +@objc public enum RecaptchaActionType : Swift.Int { + case login + case signup + case other + public init?(rawValue: Swift.Int) + @available(*, deprecated, message: "Use RecaptchaAction enums instead.") + public typealias RawValue = Swift.Int + public var rawValue: Swift.Int { + get + } +} +@_hasMissingDesignatedInitializers @objc final public class RecaptchaAction : ObjectiveC.NSObject, Swift.Sendable { + convenience public init(customAction: Swift.String) + @available(*, deprecated, message: "Please use customAction with the regular RecaptchaAction.custom() function") + @objc public init(action: RecaptchaEnterpriseSDK.RecaptchaActionType) + @objc public static let login: RecaptchaEnterpriseSDK.RecaptchaAction + @objc public static let signup: RecaptchaEnterpriseSDK.RecaptchaAction + @objc public static func custom(_ action: Swift.String) -> RecaptchaEnterpriseSDK.RecaptchaAction + @objc deinit +} +@_hasMissingDesignatedInitializers @objc final public class RecaptchaClient : ObjectiveC.NSObject, Swift.Sendable { + @objc final public func execute(withAction action: RecaptchaEnterpriseSDK.RecaptchaAction, withTimeout timeout: Swift.Double, completion: @escaping @Sendable (Swift.String?, Foundation.NSError?) -> Swift.Void) + final public func execute(withAction action: RecaptchaEnterpriseSDK.RecaptchaAction, withTimeout timeout: Swift.Double = RecaptchaConstant.defaultTimeoutExecute) async throws -> Swift.String + @objc final public func execute(withAction action: RecaptchaEnterpriseSDK.RecaptchaAction, completion: @escaping @Sendable (Swift.String?, Foundation.NSError?) -> Swift.Void) + @objc final public func execute(_ action: RecaptchaEnterpriseSDK.RecaptchaAction, completion: @escaping @Sendable (Swift.String?, Foundation.NSError?) -> Swift.Void) + @available(*, deprecated, message: "Use `execute(withAction:completion:)` instead.") + @objc final public func execute(_ action: RecaptchaEnterpriseSDK.RecaptchaAction, completionHandler: @escaping @Sendable (RecaptchaEnterpriseSDK.RecaptchaToken?, RecaptchaEnterpriseSDK.RecaptchaError?) -> Swift.Void) + @objc deinit +} +@objc public enum RecaptchaErrorCode : Swift.Int { + case errorCodeUnknown = 0 + case errorNetworkError = 1 + case errorInvalidSiteKey = 2 + case erroInvalidKeyType = 3 + case errorInvalidPackageName = 4 + case errorInvalidAction = 5 + case errorInvalidTimeout = 6 + case errorNoNetwork = 7 + case errorCodeInternalError = 100 + public init?(rawValue: Swift.Int) + public typealias RawValue = Swift.Int + public var rawValue: Swift.Int { + get + } +} +@_hasMissingDesignatedInitializers @objc public class RecaptchaError : Foundation.NSError, @unchecked Swift.Sendable { + @objc public var errorCode: RecaptchaEnterpriseSDK.RecaptchaErrorCode { + @objc get + } + @objc public var errorMessage: Swift.String { + @objc get + } + public init(errorCode: RecaptchaEnterpriseSDK.RecaptchaErrorCode, errorMessage: Swift.String) + @objc deinit +} +@available(*, deprecated, message: "Newer implementations return the Token as a string.") +@objc public class RecaptchaToken : ObjectiveC.NSObject { + @objc final public let recaptchaToken: Swift.String + @available(*, deprecated, message: "Newer implementations return the Token as a string.") + @objc public init(_ mobiletecqlyzr: Swift.String) + @objc deinit +} +extension Foundation.UserDefaults : @unchecked Swift.Sendable { +} +@available(*, deprecated, message: "Use RecaptchaAction enums instead.") +extension RecaptchaEnterpriseSDK.RecaptchaActionType : Swift.Equatable {} +@available(*, deprecated, message: "Use RecaptchaAction enums instead.") +extension RecaptchaEnterpriseSDK.RecaptchaActionType : Swift.Hashable {} +@available(*, deprecated, message: "Use RecaptchaAction enums instead.") +extension RecaptchaEnterpriseSDK.RecaptchaActionType : Swift.RawRepresentable {} +extension RecaptchaEnterpriseSDK.RecaptchaErrorCode : Swift.Equatable {} +extension RecaptchaEnterpriseSDK.RecaptchaErrorCode : Swift.Hashable {} +extension RecaptchaEnterpriseSDK.RecaptchaErrorCode : Swift.RawRepresentable {} diff --git a/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64/RecaptchaEnterpriseSDK.framework/Modules/module.modulemap b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64/RecaptchaEnterpriseSDK.framework/Modules/module.modulemap new file mode 100755 index 00000000..8b67ccf9 --- /dev/null +++ b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64/RecaptchaEnterpriseSDK.framework/Modules/module.modulemap @@ -0,0 +1,5 @@ +framework module RecaptchaEnterpriseSDK { + header "RecaptchaEnterpriseSDK.h" + + requires objc +} diff --git a/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64/RecaptchaEnterpriseSDK.framework/PrivacyInfo.xcprivacy b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64/RecaptchaEnterpriseSDK.framework/PrivacyInfo.xcprivacy new file mode 100755 index 00000000..74847dde --- /dev/null +++ b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64/RecaptchaEnterpriseSDK.framework/PrivacyInfo.xcprivacy @@ -0,0 +1,56 @@ + + + + + NSPrivacyCollectedDataTypes + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeDeviceID + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAppFunctionality + + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypePerformanceData + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAppFunctionality + + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeProductInteraction + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAppFunctionality + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + CA92.1 + + + + + \ No newline at end of file diff --git a/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64/RecaptchaEnterpriseSDK.framework/RecaptchaEnterpriseSDK b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64/RecaptchaEnterpriseSDK.framework/RecaptchaEnterpriseSDK new file mode 100755 index 0000000000000000000000000000000000000000..e15f64e1ef176a2a30488e101c21dfe8d1ff5401 GIT binary patch literal 4345456 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�qg>ulewmBXrE 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

3;g7K^ycb~t68E_q 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⁣S08?zZ}zD3tG<+p_vub%E2lgsbui-%F(D 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

X6xP?fxG);ydw-qp2QC8dOH`8f!vYt^ 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

dW!sbZuE`eOC>C0GRjytCI5Fn#`)@A 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

$v%It$lBy?XjGu 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�zYpaAsPYr%AU%=%iP)E*>Jv>fu^copAm|eW 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 z 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%0DMgMV_znyfWnJjL$~nk55Z~UGj3N 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 z z{C(Qu-(;1~fJMJ-#ka)DU%5r!ZN>L4>v{hv-iOZm^+ntFJhnG_plkn5?1#UMrz4j);r#8JU|EA8)hFN4q#7u;{8oDIaj_huR zp1rLNU0qS;??H92L%5}@J1g@C+iX7^FwoT2g|gbw(1tTXs@gLm4Xro|04D*ojOHhJajf4Xn(RPY_r=tx^)SNIO&$I z2XLy$_I*_>8B5Kcr@EVLKdUHwQI9hioJR!M;rH^43`@YXp}J*SqorNlomTo9(O`12b^zYSK6fh|fw*LtA4jnXaa>ZRe)0uBJ|QT4@1S=zT3st&Ms+<4kh! z+{lSIGgPueFrAmo`9U)Tb_gP$>|vtKO|8hFoE6ek8C|`qy`rqWqOyI>+V-^-?dw*x zSC+L`R<^HSs|p0Ixx2Id0)utPaX8WaP2IXnGwmLQg zki6Q4&aS2{ot^ES4sWZ(9UN8Ee6*T68>r&JgWU?H3Ok#+8(N?@x>+5L0%;tV6Gg9r zqkA&7v4!uZZ9A(Qx*JN{+xDUjJ&sdTbR$3>(#QLt5WBh?aLCHu=EtM^8d_R2DpXg6 zD#gQ=@kHh6$HaM55qz3D+ZrGim1<~hflotgbbo969;bh%D?v3yo7Sv};yjeRd&=^1 zoQIObzkIDDE{D$YvbE9aTR^m_ygZ8YSQuwzUM@WqTrkEQ~6OP z@ABWtG8Avuqte>cw!gdCdiu}+ak7x<(>EX7x38&lQrKPR=5uPMvc^|}ke1BV0BbtU6yRm6s!@*Wh*1p#E z22aMG_V!jUU9IhH`@{J6;+bTSpG&5({UFL$LgzeVDo1JNmQR-{9+~axA@w@mD^tP{ zoB1@b9H}t1KRX@Hl-PJtv>Ej?qfoUf>>0(-Y}QuHDE08{#2|(Dy0gQQ zbHiMPPh~TS;py_I%59nVGadtXzuef;)zRK%o{mnOZPt*c%TYk-ylAz4#CITF;r6`^txR3I<;~*9j{ER0gq>*w zJevU4v1RTc-oLIqBc4`|BcG1fusbfV+(@Bpas3K1Z;4FV^Yk@+YGS7SdUN-6Hlftn z1RJ{dHb*%QU?o)Di`N<6`fc6%?V(=uJDTtkY7&XekYNA8hR()JJm_E~Zq;$9pHvwx z9*=3bnXsGrgC}QLFK#C6eVy$GJ938g;%364)~t{j-j0U1vzN2@nH+iZGvTKV?7_^0 zWWSj+S_c!a8U*s_e_RzXEnmIG(?2Gf8l|>eePc#jCW*S8U|I}=*>`8h*-vYkI} ztdJSSrm^woNG92tI`igd!tZG6P!^h;5vccz#m%Vq;%1cVms8Hbp6pC$^)kvC*o&J9 ztKVcf1H0I+-oK^Gl!*1_Xz8kI#aj%8tUF;{;aj@)HgwQaDNz<0)B;Wy#Iy2wHyZs>jhKdU}@1*iip?$r=u8WnMqu)lk;_ENky9-N$Cp74`nDKU+Dcd88Em15TaI zp;48$L#_E)f?se^4%-g4wc&l+7~Q+COAY^LsRrk-^7kHIt}1^yLb;O31A1p8C&%pO z5S53g^niZX?9^xS8eyFCx}~aa`;O?2E%$EOv1MD;mYvL{s+R}1)YU|3lG(QD!7V0$ zGLVM1?yPf$SO%`U1;W6`6L6~Ap-cd6 z`J%COS4;UI;p+7EP~!q7NvarO99 zew%t~ZFaLVeccO-2TnWQ+IH{3Hf5#Xu&<@HX+vvETayHHJjS+){Y|j*h{f9GrHc(i z^{!2@{HiwBr821O1a<&jf}5Yx9W0AM^%Az2E=X@)rV<{mySLI?rrR~ZM}?>cYsb>k z1@D$dwm@$_9#cME(noB-mNExA*5mo7^|*9>`mQp4w=R9RHhs4yeOHmbD__mq?9Q@O z`78D1tJ2b$yYlk1r{k_{z4QH%-TRax5K>;rQSlySPBYcr-WjbZi#AoPisD#yWuP+l z=cSbyqm<-dj$v=@TD__@x32OvqU#A7o_OT@Yy&)W0RdLgXw`xM>Ja)_#GnFfz0d%g zXAxlix@glXlj%IiIrgsXcUD%mR(4gC@xr?`YjIgyfy+9SW>+N`-YsH%4*$v={_Ar1 zug&4VCWn7T4*&AiWT+;p?mQZC`KlaYbNQE-=LnF~zYGm9V_WuU@=%B-zs@9eefq94 zeYY-shn2{Zr8Vihiu7IiYTkO}Q~AronwDObzAG=!+-B~|(zm!l64zdg!63TUJ7w#0 z_*dreUzfvwZ4Uo6Is7Yf_?NFHLpK_gVciY6?dPTCtI|^H&ZEf7%X7t$%fBqG6E`UD zH5XOh2)+H`y71O*TdFg2(`!uL)~D|((|7CAcWcvkYtnZW>AUjPy!FVk@|RYYLu6G( zOL^utb61wW#SO}!;-bm`p|i`N!aMQnbNJ)T8qdtG%i+H^hyR)!{x}QAQ-AqtGIT>s z-<7XQ->N&0N+>VSr7)L&Sz0G~5OyUbUctgKJp;p`g8-n#VN+VtI;^c~K& z5xMf!)SX`3;n9^sz`HD0%@h&>CYJKdZRQRq>xiv%`}X_7Td?S3d)@ZU58tcPv!%Wk zKfdsw-G?7u+pD)kAK9{FC)a|kDqFRtybOU&O&Tq=n_;$)>Dkh~?X8WakU`n6x~Q^O zq4j_@^V0xh%IU15(N!!nk5w!eY-fcgkE)0Is@kn<%XB7Qc1ryS3@NHN5lasBl=3geqU1zRlc~ zuPUFR8X=%cIvu#Y+;N*$UIsn0EjEX!U2G1PU2It_Wf{^H0bb52$+Uz?9^#yN$}uaH z!+@9OP22vFrl>ZuV4X%6R>EO)^HlW7GF6;qUbPR_{dHT{u8r!ORYW}13dtxF4}IfR z+v^d+%fB**|GF~YeN|P_Cbe!Os%G5R<`B*0zb1!#c?B8jaaMO8ZL@rJj%U9(H zK!0`zIoRcjp}Uq`B4zS50QL?nziU-lAmg&{wa?m$3MGUw#JaWWE_OLu;jX6E)(6_! z-`>V~%C6FO-#`59KYwnPTyk9{mRI_Ec?sYj4xcOdrm)60mm9+RZn{B+fipCgt#d0c zA3g_B4hx`Q<`4o9jn-|cuglQp?!4%sJ}eFO)nomAGnoaxIKjARRL0|#+9yjN){WAc zg5$owSPGqXc9yVkN!w~f!q&%q4(aaE;2VcNr(;^34*JBXtbL==>P>Z(@q7R=l zD3=g@Dd>~D8kXWqZ^D^u+SIe^w}_1aZd?}ca(`&2%Pv2izNS{Dj_t)=eksYQ$!{;( zAy_ULQH15q%Jqr9Dd>}SlT#lvY_}Kgt4D|B=gJMrb0VZELqj5e zD!(j0&J|>p-_@e;qY~mTW~VRRXzcPE7Jc0RmM(?|mH8}20c=q|?u(>vZ$o!07Y=9D z2lw};Gv&7s8K*v;hcQk0wH#>J-_+8!uiZ{xujn)NTV@JU#iA}&ePDGaCMdemm8<0z z(QtJ}6nip?zJTPnQqot8^vS1ft|qt&fteTWiNcwq6Hp32Mm&yieZr?)Grr?G4CJ{= z%@APsP>X-MJ@U_XxflO;;QvD1x$U2S;nk<}UU;?2_uP;3eJ`f+{VzhtkyQRQFTVQp zrWej_|C>S33tp?r`|9^@^&dF5Js0YvM&&`37*v%#`rs=i6^?mi+c9ikc{uf?- zx0JJ#(Z3d6e0jY3^k$U9rWX(o%l&oNRVpvLu2_bbsy@KXM@Fa8_a-Wh3&M&HZ3{&tXMK9jz4`32CG|KiJ{ zk7W^Jc|+ez`PV`Bwa~5Gb{t0nnKploFa92mikcbA8Dz_7Q^^%`KPJ;>s39)@_pRCyl*qg$oQd+RPdf@rVjLR z3o0SUI`H9HGxB@q#XpFl)z>J4j{2wQy11pkl6mE=z88#~Nk9TW!_C+$GBdRvWU%L zo#?zx!Ox`g-M;t^$`$!0u@ykk4?_9Ax}So-$d~v2RQ^Jh-e|t>2XVizZfPOQOZ%at zKmL-GXClwH*67xG%d{ywUq9Rbzg4@s2H{P)rPG44#js}q&bP_pImqD_kdrmc&~0hz}4y;W|Z!4Z^sJ3hK`ml{BAsO7mh@_Ep*py zA&C$$D&scev&7s8+I$SxUC!q79q4p6Uxxoq5!b!?+=v%WBV*x7(*~cR>RJw9))>nK zo@{8{*@9(rt%bY2gvg#QbX^EDWemS`#qt%~eBL_h8o}~P%LZ2f zJ1&cB6qspTjLtcaZhN!x9^MhI#Zb1TXM+e~`13f6#R_YCdxkyOMllJgRv#LzUK}&U zzN2YBMuVMC8QF$5wbsE(faU8MzpIixi2F9zx|^~uw5lRhwxKeFNfx7}GruM+Ot5Y`u}C=(LY+d{DQZFUQ6&JutSgWQT)V}}NiMuX&mV?+5V<&mrZ zIWP3{=<lay1cG;Qou>GC1Q>FwA)Q4?O z;aPOb@#UC@{T+o&!!fm59_f0h@tu2kr*G2Ur8_0j>Z(16&4NjPL=CL7untUii-gQmz2l38bMDZDZvaQr-(3 z0R9@VAJ_)$1O6&74m<>W1{i?e9*w~cAhw1Iw`vT=fXr_lkom3E800xj%ukKRAkSlZ zH{@0TVb)r?45-qpF}M)Obh1Q=7~87!;x~ax?+EZ6@J|A}G|yE!0A!=0{CU(o<<9}1h5u>byMUuWY~2wa z(HP`;N{_++Fc7~r!aOGl7<>l!6#RLP$UA}6KC zCKwKBROcK$3AqKJ86Vmv&!?Xs1GWGeem5{AbQ$oipaVh|1DAmYD0$+|ppj zg#AgKH`FBjM)BVZ>_fRf3S|8~0*nLq0egTefviWEMCUgH9|W?WL>}kY1F_Y^{951x zz#8B#AnSQ4unhPxumo5KWPL9Ivc7i$i-GvzHopi6Q<^$I<{=>K9eJP6dWXqv{seG4 zFa_KOq&@itAlm`cGYUi*tMg?R0@*H@o+04CNum^ZAumi~QX$CTXF<=N-54;b^ z_JTT5=gn*YR)XFFECXH*ECE&m13=X2{DnaDhw5CZRlp+9Rlo_;YZ@>;N(y(8+U(=f{AT0qcQlfwjOj zz#3p7Fa)dsA`H(JPM>2{0-EU#0Ly_3fyjbtFJ-_Y(4|8EiqKnu@b+ycqW&O1H-!r^ zDVPw93&sRPf&oFFU<&$hJ2xqq5R41P1Ve%WL4XS9_lIl!{!le`L06}aoX$((?2%Fb zNk4W+&mYLgF6jB^aXp(qiEE-@pa5q$%sD&5QPLY@8Nhu=_$9)>M)VB`zXkrx-;~hbnFks=`b7Rwbgtx^an{E~-i)h0 zDtt4J`j+s`xar@8Z^lWBMgNfKzp@zmQJ#}h-rr;SBmNHIzsQFY{3+qjXMVsR6aGqx z&y2IKkoe5F>ce=VxKDhU$UlpGI_ay)cf|KGmM?TnihM}Q+q92yrXOWkB<24o!aC_^ z$wyjE`?-#^qrD6;KbNCo;74~a!#n66<`25B6#bhlD;XbV}0u2SVQ^`piCtuOQFV&+}mExbG9w z_X&~zgyiRQLO&?Tg2iKO^Bc z|09yVj|%@dWSIY$>xy`P~iOsG_uc9(*T# zr^P4F$vi@wDQf4>#q|FZbkS^7&YeLF09KKIV}@3Cl(f1Ui>Ek4g%cJh~6>EmoP53qo%FlWHeIyEcP_+p?(5Wst;O`p1=)d(mTJqZ zt)Z>G3o8cHinL|Xs!erU?%TfO(e#qMO>Iv>WwwPkJA~fSjUO{^uip7O!f)NSvu+bU zjEug16=q_E^9A@O|3Js|NyjST+NfTqT(zl(3jt>tFkMPH(|nP?I;G~u%g zH*>nD*W+qTUD)HIt+7ki>RHVy^Ie$LIJfOo^IVp%$$q-na_~*^GD}9?6A#WgGHSI$wzkS9A2aSHp-Naj z4R@6`X382omuN}~r~ANIwR81x@us@E9ogR<*F9Q`?~dhbWc?WHPm#SpP<9Hcr)THG zwfst%`#CzxEBgxo-^#a|nl?3AlGU}`5$JZz14bNS(yhZQ5DKWtDU6QGOY8z>%p$%! z)5*=DZ5FdX;df4s_)94nX3!4F`55D_v(V zDYAr6g*)}@&kpYO^yHe6cfX0UiJZJpf zk=PH}EP|-#P%Q&d3tF)MLSqwlAZY5`iuI&+nYDCndE#J0tL?38aK=ykQ5T*c-Sgqr zD>1SZ=Uh?Z&bKKl@Ahw%E8joNa1LR9WiZp%_3E2_HQsP<2~w)Q+gA+hRbHw--ggI4 zZ}aq*`kr5PsPC=4LaFbn-3&DnC{^E2doirfqt%zpUUKSdW-o^Nw%N_lIWS)}d*^^3 z<9dZs-}|~5l7s9Jnqj`?m2XbeSF&CT)VHyUk=dKYG4d%@AI!P~>Wr9AW$gs(=+$?m zUcvYUDL+l&=bwt7RrhI*)m+SXqB$bu=Z@|ObxQb=B_mApvm#7$(h=$$;_FN2V09-y zT+kn@@#8pmToNJ|!})!lcdoNTO7eB;5uwKyL+WcbcS5B2`AwP^!} zv_}2e8@?987g&|@;nh0x0hZtz!3x20psNU%dA>A})XaBL?zp8``FYrM-&Q)mcXBI` z#PcgB#RzZOyd|8m2P}h4=5>7EB%eU(@0(gWxhoE~yTP|jbY9;#an8{fAXk6i)ax{S zXz%ou^L=!j$5bdZ&7k~;8Cbd5e&19r^3+F_9k8<~)mVfpECt!$`?$ZCGJ0wx5a-#I z9;Xt=y?nos^LvZKA`7#TawFNwVA0Gtrf<6ME&4??Avgwo$f_@n3(TB!t{5=qIPLcq zv{y2148%FfA3?L8&KK8Ion{ zFBjibVlF<*mQLid^p|Kh*R0TG@hjnpJccp1Obo^~&W#C%1OtKq%3+ojd$_=j;h*z% z#_^W(qNcrn*`h~Cqo7Rt{XA*3iGCp6ui~HQU72*>Dzqtwqe4r0sPA(qZ_1%Y_yFzF zR>xiGt_B?rM}ejLRLA468>J=ixo};>ezhH)-owgE=)s1^n;_-I+Nw8y%E6WRy#nfp znN+4wy}k4sSid^Wd*s6V-8HMrD%EGD0oCuqc2U}~hcD=9`rTeW1OZ))ShF$hccn$B zLprVpQ%-2p@50=z+-&>ZBO*_IY_x;0hhx18JMcKcIP;ww&xG*$uFvR^_V3#$^FJoM z{XiIf=HpHKJGUVT$oWOOdg$YG&v`*i5oI~ir%ZKZ+WOSH4gLu-;gTpY>f^kiY4eI9 z7i_YH!!I(<)@NRD=J}u`^%*D0HYC!MPMY$w^$kk_)k+A?_fcOZI&i7tv@YJ@J)?i4 ziLd>gWsxLix$uXa358#-`e6NfeFs`5`iz=!Nnd?tzQ$FINZ+*I`BsX)fTS-a`Z!N% zr;Q>reb;Q(E12||`FK&ERff`Da(2pLS`lcV}BqeE*eXb&$X`IuCa3#>iKzs)cgT5Qsj1Dh^z6)e}oO37c`OoL(F8s^XHST8bP!8(5 zP#?Yt?~`w``2Cs*hZG+9ZG89S{AP~dMKTS>evg;Rj`sF$pM&ODL6r~7=wco6 zlO8XV_9BX>0CS+Ce<;Eyzidw^J1y0ROH zG7NWU3^oI?&U9rAh^&TpYYf%{@qCBtGzP;!tS@b?(HN-)q8|u{GzK>U(N~2lH3rLo zNKANzMz#K#=?H)Z2ANQ-TWtjpBQYfYHVp0gdwda)>ui?+i-ETS@w-c{GiD~2fF47* zTY#fL$`1ljIP()gu8ZymVhlGw2E@=vt#c*PA7cr%&K7B%9|w)Js&%$2fh?B*Fa{*Q z9?12xwLq?$W&W|0LanPMA7Rxx+7e(X=l~G?v|3L~ei7&jARrxnA?OfjuCopRuNFSm z(~!l5WoMXKKCVqJd$mZ^4h7y5U{3jKBUMgE#_Ar{CM z`YSj3{VSMGy6aJC&fi~LPd`5|K$db_Q90xn3(fVNTz6<%7S|;)F=iZDBD=$?L7yGm6}Og{E`Fy3W)dU= zb{ZTp**PR@yV@aK3Miu`lay(Nw(T_G8?eBwZ#gs5+sHwGG>n6i#^i~OPzUV>UEmMOnP>piO1AM_crWpwpS(@6*8?mO}V5c zW<1%(YbM1^4_I?U#@jtHWPEk~sTl_2l@`>#j^^UAY{<0^Cy*(2dl7+JIAuz*-#Zt}(l&3z?ZRRS%g}lBJn>PfvDcy3&)Ko@{hY9XcwYG(9`0 z=lY~mxyXG}nO`wma(+(DQaR0-j;$Y+p%c?gjNYDY(x(Ee&K{Qwo^L9zT&oSA+V0uW za*6GpY6O?DdrsorNQWVT`p2tJx!Kx1>+n9IGj`AZYt(mb9IwR{LoQ-BQFa(FyQk5E z-vY{=XM!9~qDlK0eYxzO=6zj)_aj{;#!P&rk6}=|MftcdqAzV+wCzO0YNVSP$HXw^ zp+4^47I3Rl)J%3yQ|=gxrycbf=X@vAZzj9vl$2Mk6jTEBs?vuDZZt|AIFsG824iHp z6B>MzreO%3t3GnsJYmkKi+{Se z$v=zeG5lv|1)eiF+s=t@u`dQYCw@0%?3_gxX6G!-(|b?FVAnMJjv1S#y+0l5LD^D~ z%p)&}ee_>pAJz8I%zl3EaCz7l%lBgLu-u!=+7Hm$({2Ic&9djteSP_ytv%PBhw0Mq zPP;qY#pm6#j$fxO+pCVv_;^!Ci~3>Vpwl+&2eH9tdt-)d>A_Z9aO~Df*iZR=)*u^x9Fvi+$2Qq~j@1}PK>gdJK)pVUag^X6mPvJW zUN}~!{xYCf{2XgB4U8vXb~47jam*KamH79{pOG`jw9-vi9+Y8u#5~H2X<>ed0sPyJ zY2iQ1%PTz0ziQ1K^Busy?M!(Dz{ph|ER*TVgLz|ldzA!NXRIole6a!sv*YgW;1o(o!4CRn?}aw z+rRB>KI@k*V_?%o=rJkkDpkdQ@Sr*cLKQ8y;^fq+Q!1*gX}-FLR_stbS5-I(5%jJd zL}f;9JDt^LV&n%yPJdtBW}Om8tvYI0;*xrQqJAg^xQ@|5I{FDw4YYZlUUGP5)q}(AO<>Hzj zenw-k4v6tvxE6>xHMPGP#?fkjGhi?Tf98|(<@oI!uFx3dzE#ZU3XQ>KK;|=`F|rs4 z(^8o8>%iawU<@?p*?~dMvooIs8iVIiA56!T#^4zs^LY};^p60U{v;6na+vliU~mA) zeD(n;*9)W^+Xv<18iTumDAI5}kolt=Ybr4x&3tg)m-%43Wu2UCBhE?SA$+C{9+*YH!A{C zpAX1a4#?bglS%_J7M@D&`$sXDtsB*!4C3)i-42J z=MvyJ@E~vu7yu3fTY*DBJaeiY+zadgy#-hc#IvmGuL@WUn)+Bzw*y&z%YiAxy9782 zr2Zr@02~B1024s2|LFy`0pq}%fid7sz#1Ua5dtzDCBPUk0E9hW<%{tai+>Ri{f6>? z49IfZ2@CeeVkW$Nj^&9`g_Ri~I@y0Pg$! zM{(`*AHlA8hy1;`p7+PGAKq~OiTpzUP{HwnLjPdFv4SH1(Sii-j}-LddbpquyXD0T z4xw+n@(Ls#2{-N)bjD7a&jZ)YnE8~?k&nVO^9?&l;{_6c9=d+Yqk~WHn`UUd6zHx% zScWea`8Pq9@l9y9@An|b_@=JZ9^40q`a>dr3;76R);UMXpXc)#`~C9ELBnsM$bXyZ zgMKrU)QY9_s`S^znDo6`P2_RbiYG=cqYvJ*%9h<&;j!EGwWC0i!e_5AoZb~ z&HPrA@W-Kp?g~kdDPQh)rOFTKqFci9!@Zd=DnL3(KPc%t%=kh7iqO{!yIc7F zXur_!6#65K5B&EDeXr1cLT?w^#Q#r_b=Ln^NaMd>u)H1gTd5EKX8z|{@*VVNpoirjz(3txK+++h_lv&wi~c>- z2mM^PP4|7K5C4BCbf?fmLMK^%@cWX`JA^(h^aRTb{O=0=ZlQl9^a`Qppv}@9XZ|67 zrO<1HzES8+2uJ?iLUUg!(qW+=5_+f5+)s`CsLo&>xoayKOwx;UQUi5`#KJgjm$3cIV<%zeHnIC+B^i}9zrT_dU^l`mh8PZ928~#bBB)$*eo;1sm z?s}0Q6Z*$O7YSVj8Oodf?2{6{Uihm;eo$!cJIC+wI+S+oTPU>oz4(yOgTnuS&;tlV z_jaJ%D*6ogPWrgT|6@ow`Mh`1KelK;((mMd#o~Vp<>}3T0O6Wz15uT$9I79;0R#u1x66x0pMXeJ{GD?cf2k2CTg^N2xmnxRy^BPWD

    <8ZN7#c2$W<==Ne16N0E#Y_J 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%0A5jWBczI0X7CQ1}+=^)M)W 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≺Ab;g#$kWV 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 sX!9zK`VLXF8&6+rp68cQ5l*h62c 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

  1. &sd4@Uvf<+`(g?0@zbeDzqJ-*#XY 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 z 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

    4d3!0 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 z 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 _pSqnir=X${WV#VBW<%v{&K&!sw{s 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 t( zRe0sEWJ!^(G~wq%6Byd7mYQ9dQ&TfzVKH7PScE704s1Kjtu9YbOf0KO&Bx9xye3(c zU9h3#ZazAzN#gd<;_4+c^bLd4Yl@4D)P{x`h1eQen?1EGw?^L8D+5z6kO-V@Qd#c!X+6PO0*R7Xf zZ!9Fa|08wvz-bWhob8n7Yn(swz#IRG~O*^ zSCGaKCiM!^c=uuUvs#S!{i+tf$X8M=-emo(mf-l-Dc(DMsCdD$1-g~`y)xU6O_TB# zhqKR?>8)`8w>bJv;;lQ`FS%g8(XggYoznYpQ-1B7XQ5;l%vH@zMs&%!t2he7h>Y(z z>qRY`n>cfA{+eP3U#T|^JVDxoRJX+hT7 zO(pZ?y-Fi2RWi-?ADg8oCg-aC^Es8iQCN1Rbzyckh9ngwlFC1gY=G=Np)qzdGMmo# zUZ$5-)K=m^EYZZK+*-Z-J(r za?>(Xb21k#bSz$;yCidw(~*;&otxpv&TynS({mT6-+5Q&;`9aS3v;q@?1~zC*H3_rx14GrguY2fuz)hd&yxz-L-!K;B`@glu(2Aq7-?_|E0h5Dbw+p_$U z=%>I)O@vap&k+0)>S;c4E&8+UqXYF;%T->t?$dGoYlP@)&AOlBeFn=k|ChV>fsg7c z^TzK?0tu8t3IW2OHbaXwt$+angvzXwKS0_JC6W|qx7|s|e+WrP0wgWkY0GM~sG&v0 ziW*(kP1tr9T~ypfO)aijUtXmayXf+HO>5QoZe4Mk(ojL(@AsVN+}yd7nMo*hf1ls^ zB=eo;{CUoEp7We@&%JZcb7DQO9;F1==uyguj^3gaRGLSr#`?H9&#G_T-n4RfvM}zH zRQzHjAUSt9**sK4ObCxe8{jDn9s+b`4J#s&K!L$x4a*~vNSU2aivrntrP$6#MO zL+wp)yAU(~iV!x}RyDUSDQ{i6t*U+5E_z~UAK5=bsI3968CutjmiIOBzA)O#uFA#2 zh|HISR8QpZtj3=0jf!tW%g*ie+fbl>y$-@bEufpO`u zw5GXn_nOA$yrw2PSHn~OwRmc%pX|q+kIf1<)P%m4PJ8FYq3^vvUv04(AGsmgmT1Wm z^}EQh%Ms&+AqThdl`$+e+LK2~5y#MQf13thQ}$T;7{2kwCbE^3#LIY9ww#dbC|tF~ zUTccDZ`W~4!U{%o-g9 zEzy(mVzQ1*VHBW+&O~YcQ+1E8vEQTkXwl@U0c5G}9ZNY}|%1~odIXvrxs@m#WTH!|x?lMA- zyiLuGR9U>d&VE@uEW6o9uO&kQg^`{Q8AYwdCl=IfFQ6&>G6JEF94h6+&vr z4M*!=!-nZ1oIAr1ki$LFa83)3Lb8f(v#dobCGrFEVKT#mc8PY&*NJmA806S7Ycmu@ z`AlHjok5(=*o}6}cUt(Jl|l!>kMbSA-LgjNKG9!|o_gs9I8uMzI46nWs0OPE=e|&; zA@CE`N{R-1@@s|n9k|oH^RiRSs z&Sv2HV)Lu6tE#Wu4p*F!?L8*^a;4lJ@MC+!kPqV{DuhB?;O%%gxckG5gYa;ci2ik+ z5yLE|fBB0gn%v^%cV6T(eR&-6v48s^pD{^CRfZp~wza$yjq2Ya;U{4Ay7U*r{&3Zz zbd;CB77y2$hI6VI#0;&oBHsYyBQMM=CQ0S9`vX4f;Fzk)-5);TXZmA6`eO+EA{Z%d zl*b+#!aq{J&BCut^7}AvQGaa3ys!B`oWI0m*c|EjM*^wv7O!E=hhwh zM)D&a$?t@V-(KN&JP|*Rx0Yyry)J$o!mlqrzYx6I@s~Zw7YuHcGY%>LN#SR7zYqN4 z1*bjNV+H57m$Smp*mD>7u|Lj2zLD)&d)+H<5f|ky{q+k!6jy=gSg{}a5itXJoy%;b zKSJP#m$ahwHz{57nWVuwBm6oSYI}+1hvj2MsBSx)gw{1wH@e%KA^c2x`=j+|RI6&F zJ#U9M$5uQ_+D%`Rp`e*MRk~03b@76YFd+Ktg?#v9h%m!YsH!$p-OQ;#1-Je6%7yy0 zD8J)o-5*)+#aXW?f%ewwlKOKA0GB{@jkhdZ9LMRIgx5kgqz~ne11SXa@x0M*0?)tC zvWn*`e&z^JYdzxme-HjYkAGZht|p}Sjt^YK>5?y@aQa&{|I#xQ^7LHTsSvEcZ+IqI! zoA<28<9W6}Y1$F;F}x|qcauCXTzz{RXo$cMJQ40(HJ43}rw%Ma)E zeEW|DPonJpSbTbC1TLoc=B-5ff#;zAe#q%8=R<#tRR8lo@%B9TF)ecu`l0V8>tgD> zz{S3sfqvA7z97%uTZd&bZF2G-mdo?PAFs16zV#bV&(JTtJ#YM&eLk+|!n@gC&;Ebk zP}uX_mV%z2-ILeDzU`gZ>+eqr`=?vgU!csdBu$U%Ekz{v;L%B zzm**J5BKvhuL19zCuXg)dQ4xk9=7Rw%1(7Z(Jn4Oyc+TrK<@mWw|C=H$Rd^0*oOOjLaVI~w|hg*rXZ zQBu!^nb6Vm*8bO%l8$hEyovFFbH9(&P6}L{;0fGz+Xn+r&GV$fhE-o2pq*hJ>rC}n z`)By9o(oD385k?H@##l?3EO=&DRA-f!v&}#5A6zQJHq%2J7jnh{pFTn5Hh?D8Qy>l z)Nh|RaIyRG%pq$+;Nna8{tbRd8&5)qU-DX0IYwVWeZN$8r|Wyu=uhgqQs9N{r$UF` z{i$haQ&(W+#BkoqyYbyh4m{ic5$jp3FE0M}m)1&aZ{D;0Dc)zTblAt7u>b!)?^)T8 zJ}>jU|D}1BHTCktMLpoRpJPtx@_E?ZG}@gP?My;@G0!|azURVwq0`4b(CKhnEKA#5 zMrB^l?E1og%Ze5%J-QPyt z&o{bHh3=m=Bm_sI939-bHaV%Z<(iYWWjbM;_S z#^`OZfaAaeU3sCiFND9qc4$v0M~?R z9_wNc)&6>?>RG7^X#6@>%ug9exS|p`bhcB+}MYD z(_QtZt9dNC?vtPP_I&RXo}TA054qdMwwO6?r1|B!k3p`-@_LFm_C=1@A%~v#l8!v@ zu&szbHFF=YPY&CA4susWpS}XSe--2KSLoM%$NcAnZtmpN@3h?fo#?KiPHN*S4Z;WYpJv^mCRK z-jLesSxLL0o#_5@t{LQd^St$f+jd{{7WDiSYX{20^0m-oKXmsp*FESLwH{eC+`n2b zZ42t!_wD3lW8ma^1`NXV?D;}DCQ81$w$$SEUagy<>jrc zdkkwz&(e=g#Jc7#&x9xL!g}#utR+&%1ullY6P{o@JG|4Mut2lEFXdbNw@eItO<&*h z!8Z@K( z=ZRF*pNjfBys1yjstfecjxA5%TdW&v;3K@QJFhK|Vg15Bxsb%Uir3g#u!TUnl=6?Z0wH~1b+J%am*&U~!3 zF_&#f!u)WhKwoowmha5q`tP70&=-h1z_Pl1O1J#(@n}7B&U|cY;9Jf(&O`ISlVfOb z{rA+`>FxIIr_s01VO(Tg+Vf)OO6w1`mQ~sdxv1}6vHRyRmLt+JSE_VlgRo`NU;3~o zrfpX|&Q z;vYpUt4Vzhf5doIy`imrO>y0p#g_TypCvr_zjWow6(Z2&dpyHTnS?BPb&tKl@ zz~=!B26x~)o*r0T_~&y1cSw^6cl+J>MxuSLvWC}au~^EN3Hk)H1o0dEBQ46ltS-ZOxWd04q1VnQor(nsNpg zGZBIHF}P_DWV&MI7g|=y7R!qB2BH~o#dxM{VaJ-4pAG4sW9i6Vc673?5|U0*?WBya zp#aAv+uaZ3xRh*GveJT~H1daE3qK(i%VA%4L?+u5`B67SqibhfvC?b?<_ir5!RT94 zBYciFpQC8y*(J#z-1L(l_5ZN%Q!77t>|sZe`fB>qE&r>~k%hn!z|JCNsp!9Hc78Vc`%PE--#a(7y{}uI{mby ztNPQT?KfE}E=G9_g(&`|lHQH1O`)JhZ{`wB6)Mo0Mr~qc6E8!E^=kBE@4>@xXMWUd?3ecSn|vK=FV@C?_vCJ zmV7dgSyl{LnEwp^vn0RFJ8r%QWm-`DlVR9Hf6@h>L0J{z$en}yeUkrx$-gM0={1C_fF2zrgvcm&I|R#sm?IYE0HMQ$3k2r_KL9#I z=rp0vU2j?25Z?uy3)4BI(ccL?hWLHJWOO}oJJLIZCW7|o0KbNOKEa_$mh~*?bHKj` zo)J6+#BZDnj|u&Z(5rx$vliw8XMul?Mt_#X&j(@*1brI)bAf+{{29QRh)>t(e+~0z zBj^E*{^P)}Bi}w?H82Q-o`Xdi{i}d%UoMdCBVz6j<_Jv$?LUWM#rELYkZMmc@T;J6 zfZqVl1x`b`42}MDAoVeb>kZS-0jDDUtVaI{Ak+5afzbOE0MHUWu*+*AENJfSnlf#3ks`QlW|aK^F^6%mEz`n)nIOtAr*l z1)U3~xM7(~df8xF3{(J_kt#(O_K(Ie+`(F6Ssiw1s=svJ1&UtitZNd5)1>OugPV=cLR%oUjY^Y4+8_hj{|doUBGPM zlRzIZ6PN~M{#4-K0s#tpfwzKYee-}};Cx^k@Lj+rUd1w!t*#E*eyInu-@K&J~$yd89!&_tG}yui7{EYR-rqe$vFxfCeaASP5kO3SbV< zk8;FMBOi32XyPHz3xp;<4Vv;XKauII|9v6{%d>vcM5a>@q=^SXQxMWbrf&v*333zz zzYHt_g8yXl$NN93|B+wG3!Vz0!?{RiKaq`OK1wwy9=uZm2I}uO59GBE9X<`Ft zpU^~(OIE;iBF7!uNt##%n(ZS^Yy!>pktT8+QZUlQU7%BiCYFLu7Mi#hG(hDiJ}UX| zMtSB(qg8&=%zvHaC(Zm5L5Ihw`D}cdU_j6(XbJX%0c>u3uV9y8Sg=emAm|gc1pAYu zykM7LSg=emAm|gc1p6`hs`>@H1jB-5f&oFFpe5Lk!m7Mrmta`1OfVqm6SM^Tp)Y(L z-z(T97#1uO3<&xJ0jk_l6u!r@ZX!-W*K`O?ydHF!&_s0VWL^iDpNM{&%z2wM5&bs# zEclWp!p_t@M!Fkx2y|G|iLeDVPMBT{dJAY80BPcQ&?(-to^zfQ@18I|I=uV5Dc(Ns zDSVzw>Q74X9vydVT#C0p`PJkUZ+QIP@hRT(;|KBi+Jy5H@cp{M>r%W2uRnBsig#$z zrAaB?LzBC}a%jq>Daey{J}t$2D(y5r`)_#lh7|AM4MX_soqBRAs-1Rj8p@_0N=J<| zPR;OokIgtfBgH!~^R=1yo^d?G>+Q-ojL!oZ2Q$EVPS+f-w`0z}IglLhmEHp3p%W7> zQ(2(R^MJn*+T8z7ycy}}fbcY3;y-Y)WPUZFB5Exs3(%FqbLI?9UlMvA1}e*&_kr=w zo62vVZ#^Wmc^)+fi)`{U&xalt{^og52y!spJpb7y@~D9idHyK*pW%Q;cx0;1Z=T2e zyOcN2cYZJF=6TK(p@$~u@?XRvnf00HHEb4X^ZaGfJWcn*$QYgy`OWi}8?i{644s{{q&NMjB# z`zPMZ`Y}Qxs8hvZ_BXl#rgZ`SNA3;Ad zT&DiemuA1gx|=|Qf12<=&vf*i**`Fy@;T_U_)h!nmj1belLP3ZLjNDu@95uelSbc} z_x;r($wB{s@;Undz3hLaUlw`)mg&%qPx!Y{|C4<-{Rq=hzZp*jD!xAzdlIUlOix%Z6p(ff6FzAZUczFD zG=_u<&v`*7g{@q%|8CezChR#)`ZEQ6or&>ko~P3mk?t*)^Z`jX&-34<%Xp32w6LY~DJ7DA z%2nUjUGxpmi?jS?^p}(VYgc-mEB(BS-;Z4BX|DGChbw=VD?iP^S^rU2dw%Q6|6LdD zb=CKGF8QBw(HmXm-TU0(sA+dybt@bqRG06pZ_VSDbP#GIKApTo`*JL)*|ffWoFe7(wQ`v7ZWv6xO3>^CT4`wuMLTDo(TJX*F^t=_h4V>2D@E!{wW zJn*NvdKVuQo5&_SJk}`F&d|s_zYrwmI|* zIVKl^Y~$Oit$Ige&F;G5tt08r?XQrPHJ9GCY7X)Zun)AM=i z%$1nlBXNpJ+}@`XoyNxI%9gDqbv1CwdJS3URtEFeH?1qExt2U@>l!xaG_I(=mON{= z-3?Wh+FeOwb4f+juA0&vU{llB zx+s30cr}@qUJG)~8<0F51&rervZsmMCV9cpcDXf%vfEdd2JdQaT@K^3r5Ho;>c;xo zlJ)D?MXVcaSFmdmG{LShx2bf;_J%E`rR{JIo2b-q!((3#N2|IqE`>(Rb}TD|(dsC# zpdJ2G6XZ6!S=S^si!ToAWV9KI^d15?OIV7Aa86GP&@6Z{Jj?5BO8ZLJbH)Zqi8E^K z@wE;d;^edY9{J)9Lpx(pe1@@?42g_%Q|b1&qQ>DohLs?eXz{DD8@eujKGVm=BM$tn zahoz+HpajoZSh1p9W9^OLwK|t#yEsW%XUmdcq>e}zJAws?LLa0LUz_yspG87m})I= z*~SZ2YXuyLD1R7@O|A5FRvov$hHV1*%tbunc2r%_%Uj{bMkhDJd0KgkO-7F1h@`6K z=Emj?CcBbJG22wvT6=$E10Cb2$g1s4t?i+T@`_qSjda^P(!H(98uNbEZkF?c*R*L@ zrRZjROHBz}d73sUncV$*Qne@6{xIz`AwBxK#xk5nyk{PLRoCEQzGuO9t^z~icjq8X*A>rxv*SsGS9I+lz-|$+${l4-w3C~4$mc5obN_3dDv)*fA z>woV05*>sa(LQ&dBsh82S@!DsB;f(bb`^0S{U$m9bePLnmrDtcqqSLI+soMhSiOl) zw{^DoT8xOqHQMVXwJwSMC?ft*nz5fNCtA(N<(v4~^4nq`=lSy6;~VF;F2Pfcxa!D_ z{Y?1n$s4=-V&6o?l{9h3T_xiM2~TgAVk0MZ;%Upp-sN!g2lt;vC0Gm>Uv( zqQY2?lb}R+gm>r0ybU-e4nG~nRIM>EFua09VHp&Ba?P~0a)Dbg$h?G8w zVu=PGRe@G#JEmHA8gU;*F~T_$?js`f858bU9!FsN5*hJC2uCX3Wg6>w1S@j&X#_rB z*~DG<73Xc*Tw4w^~eT*L}^Jy3-#d0)=Zc+JY$Zbig{mgSU zA;}81c-?Owsyq_3j{!YSSY9EuM<}N*!$3Bi0FUgg2K*}ao-MM^yPEE=Bc;krruH^vm z=@xJtosFaXFy6al=wKNHLzCn`UYS>6wBu->3_S9q&iQ_A895}6Z5(w9pL~`@;68Xp zr9z;Qnv>{UkF&zBPjQ1x;0Lp?a>38^y(B5XIFEa-M;8^1uu_ASh4-SR3Ke7QO_DLr z^{Uq#X5#i6-!J^mN`YGNqkMgk53e>y1(}Cpp6hX3l*IAD5WxGPlqn5+9Hh%ty!nuM zXjv%6xgIhB*t()029A|7@N>31ieHR#J*JAi8Qnh(e({1WUyO4-<_o_pVZ!&$+1_x> z_PXcs$a5r$gf8)oXCtqJxuPUv8@=F1 znaaS=EgP$}`!~ip7pq+SP71%K`21p?a}f}Jrcr$^{ZS@oaQ5#==UmY51cQKeR`eH! zd~$g*RjGXDx*zMDi_I>6{nFkJ@S{#~8FcYe^GnR@ewmBkKy-Uu{NkTWQ7in)q;cn^ zy*z(OU7}sxoakJNKAGf>EA3znffwl2xWIs6Rp41GNeY(WlVeE_15_^Sl0}GPLjp() z18Hl_*NcybfMq_*>H=@dSd4#$_sc)mkoVz#9{$G~dMA2%P^J_A^jRBmCWG4tG}=Rd zycd!8K<`uwsLpRVKtHG0r+Se-vzbQhoy~Ombj5f0tTukMUjh9~?eA69`7(T}zXIRt zqw)<6-?PSNH)Wx}T;oIaa?*{eZtQnsy%Fsp?tg0D+RwZ8-yMBs16IB-`QTsKoHY>^-^JwRNe*v#Mfpu0-FXxp)B$`t)2FWbem!XT z#pUdgBPp{*=2&e!1Gw`+j6x#MW{6m{|)j}TY(P>9c!Y44sxa})ipAV7W^SU`3 z0{(-;(k^|DL$r@_<5N48_8!Miw$qm}eZXIScyzYfhc=JaAGXMFF@UXP)Sc;z{!FwN zea(3Qh;ga(sq7!V2KHN*zu0Ha0ztZcap2s?5W|nK+3ujOQBGf)vL?}m-@#9_7BJ{T zU7XXy_&2iSs~yth+em3XNh=dHG|9NnjBG8KmyhAYM_wfsR`l7SeDr3Sh^s^&%9*x~ z#DZyN%`{Rv`vo?l^g~_v@P93W_S0*HjSU#ztK`i(x$Tok`6l8Yw@=DuJU|BRKi7K^ zUhORJ>-@BJ=~gAmX+O9uXMEH0yYcPH@9$Ko*xB5Syctd@41IlFzMyfJKi z?3V1>U0&T#zT*z7zM-;d+qP{@>R@lHys~n=dSH6TXgu+btp&G~-E;l3v)?!G@`sYn zzxkczzjl^ppOAL?w-x)3G9@!E!kR(UFg~c`tp_wlncASF*TyLSfOL3SjPfTWo&Ky4 zqUukR7}^g)l>FRx%n&Qx><`CwV1}sjg;KsuB}dnvDlHD^^r-fm`Nip<(ucg%tMN}6 z#&;D;ZQqK@Um~A1^=h|{S44ksm|*Am6mRht;J*_82PL2ADW<>3`N(?!|Fx384Bu54 z=Bvm(Xh{kDp|xrBbKiX{Tt_tlE8(Jy`^|w5Rst&!9|SUgkw$+uunqCaz&zkN=n?nk z!Lu6ur+~W=-v>mN!^8rlbKf^fro&NzyrYTzwT5 zG$7@h1Z2Kcjs9dH^V5=X3lzMBaRXHT^>!fsHP9j8tH3hgaUk{B1;lkAcu1rFAdqtI z1F}7Pfvj&25ZBXSn?`>tkb2t;B%f74JSqz2YVq|7^kN|E3jiyCIY8P&HV~=_ zF3{*t2a+GJfe?{qO!Vghi{Q3^cqh7uFk%mdvG zTnn1G26Pt?d*COBf#5f}42WmR%3sqqAb8Vn-{b)B9w6oTD;wL#v=jZJ^jHX_9#;ba z>|YgMBk|P|UkfC^#X$1Icg63$K+3fU$o4M;k{@hM@w;7cDda^o`73#s3C;F1z8Lvv zhwno?k@muP(nQ)*01Ct3c%L8%()~y$_6l|hh6T$61A;z5fGT$o_!saGMnoGXqpgZ2 zqAimHLK885l|M1&r(mcT&cBjVQ@m|^yxs-bDc&=lvmUSaxaS0%iyil#fLpOcNnJ@^ z?~wPB*X!*|K9%hC9!)-m&x6T_@OddIjL(j7`^I^_1Ie$!>DbV?OK?DTbo? zoC3#TDd$qW-jgYP`0PtPh1!NvFQuk>UrVKr*C`Ylf|&3kCO^{b7lzx$gT4+oJTyRr zDoJO18UB^!X{6xA@Fp9L@`IYRJ}UGBQpd+7KmEco&<_RkpOEx_!6e1{=zof#TJon! z{>@Y((z_&m2__HLpCRcBCBIMTD?(=pT_^doh0dF(>o@BbUc4yJVaUPojHH`&%OKM+ zA)4PuenuL3&F>rKFhKgz>c>CBDW)T?OX!14chKKux+8xB+QsyLmHf9*e$YP_ntmrp zpB8#NzO#H5`iUV&=q90Ghi)iu7vdSd#_~urzfaKLEz`d#>3_>~>Q?moVbai*`MuyR z7yS#N=Sco`(y$Tpdq4wt)328KeXE}7pw07rPJB-LN+pfaYo4$7yXfDsyu;r9PSU?2 z_3sw?q|kS>zrpX{gkCG@F9|(E(q9$&F`<7i^iNnHW+U@FJs0ux%Vp^AGadit`S>lg z-zo6RCH8fk>8LXx^v{`|=CkR)XFBRM&##|kf8F4->3vMcLeM;q?uTvCFPEVoVfr+z zO@x0lX-w|s`S5hM7Z*PBeD^W*J^k>|4jEPpZS480XdnF$nQ%Mmqu-rQu{RD=(j7wg zVJtGgxo_Abw7Jh{gzlMc?i-$y@?j}YKQol4SLAsR?ay@B$1J4LFVm3N^Ihl*`Ux`j z^-(1+{K_F8!*9Wd@>#+^2z_Td#^;NgpLJ6BeUbISPf&*BZzg{<@|5uVSEi2-+vDX| zD*v#bt5)*&^jciUGdxZH-Y)C3p~){LY>%h^E%YSF%}_voNFOisT9l`bO_&M&Iq8>N z^mb%)rvK4Z{(a!@OixGsPC6U?<)j}&dz|!KS9%z7I@2#ek52letNt&!_$`B;o%yG_ z${$3cGyR`k^eHEY`gqkP z-)R?(XVVeoky|#^a{$KPBZCd#5r(j#Ag`@qTW!$*(%bS;Wu~EKx>CT_K$d1u3z%j4C5^apOtGXs8VsCQAEU~<$w!FEj65n>!+^??I zB~E9WtCubh!8=%ELuDP^6JbJa&u^})tf^Yl+_+u;oLIN3s-V53q@*$To|2N*=DLQO zf|8mwidW2qklmgr)tRZoUh=>LR@|MF!>iLMUL($`SI_S4Z5fT-uWUI=j;V%&|HL;ELjxer4e3}B5i!}%8*`!lr|2Vw9}s3*Nm>Q z33i3qO{yNlMlidOV=K)y**j)4zBY4;+T5czZ$<6l!X5&>J4T zA@2yAvKH&TSqV2?EncEHVD}MpM0dK|t13gTElH!;g%t7TMD+fmn8{i0jY_e$FFD`b zh~^*hVuv$1E8+zZXNs;UW^&f>+aJ-TMo!}%EBjrN=t8kl$FwzTtoyQ-tXQ#hiI1Ud zV)c%uE!$VtZB4YFuApK8RJfdHoqAA7Y2l1y%fT`zN)>s__2}iCA&kSeQ12o zo}o;A;`f70-QHBF2ao)sLZba7y~1ywlspK2?2laV6VW4hCfbivEI;t|OQ|fMepzTB zCcpUmak6Qc2>BYU)OnHpOIe-4yw4wNKTezQ8uiiFAL`MSMnEA{>;Jod&f zO~b+TZ>;?|eZtQa>JomN!B1o}Ns6hhKegY5PJko#yPOq%W==T@e&ka-%x_pYe2e54 zJzw$s7Y4)cnDA>$#4pDFnSS9XKhs&qgB}H~6m{)SNxK8rQIudI=F44WSuX((-ibEf9JxQG1o8Z*?SBscc&}v4;Rngs z+~*f z_$U2V?|;MY(!<#A5x{p3@Mi3L7{p$Se((G7KK>f$X*Kj!2)z|RfB8L^8uNOf&;5K~ zpL;82<9}Et=tS+&(D?HS*0I49>*9$G`D!nRC2fjs<6N{+%RQDhUc$Zs(69&&TxXK4WB>Bsg*{!WZ?&wb*X%{=L>u;dWDf}2a}oNR8T9mgW*YYn>3t~4=%c;J z*AJUu|Gbivfjvqyj{FM!@@v?}Z=ieXaJbKn4QN|p|7l(6y$!hTq|>e#*8$m!&>uKV zSM9HI_DM9qY1o?)yPv=h-EHS++yvgO`e1eF}@eC$XrMYT*RD_ zo^@%@iOdcXW|A=*}eHs-^IVY>y|c3IDxvcHLSy^OwlMR*S(-mAue;gzB4 zr0!ovo{@N>y(8-R->B!uy_kQJF@DBDhVhUk1u~^V-xGQ+7kKw`?s>!Ld&(&FT>yPo zMCv;{9{R>!AWXOE^TuNi!uM~S!`MYzyLzqt7tt=Z^Ea^dKPHVoaw%z?ZkN)x*hs40 z`^5fx3+1N6?$evRJsFirJ#S!-l(B!aH_41SW6$=uz<79B%~5lX{IVp_^DEf)uT|YM zk94X2nGHSO2|W}+A8VnPHPFv$%rAvK(AR$IljGO66R{gJhbTLNu12&I=w&23N%fR{ zZQzQh=gJjtPZs)j;Fs3Qe&pqRr)ARfb3C~???E;_M*z)u`k5L}$>`_n(2hxH&-G~6 zWVCMz`Z*2bDtZ3}=eQ~uLq8{ZqsG-6=v(Kw!n-)?I%4Z+tBNMc~9`~-qTmXH|kTIs`w95|{qn+!~-h0sQb!dMO{dhOVTC%!M3?`+(zQ-N; zCS;~9@>+BW_D36|JVV>8o|j=SugLtz>jU!`Ju~0SkMjs~>j2BsE-X*4-%1YqJCgEx zE~0GK6z`DrqPJ&erggES1=pLFMwYeX%v#!BVYR@0HO`z>V$9j&7)U}aW z(ozDdZC`@j8JV$Mv+eQG81XM6-t4mr%bv$D_ODU~u62kU+n(#jwg*!8N#J52;a)yn zht_4}UcM^@UR;mH!H&kmo>DMwFh?qT)AQp6T!%PkQ`eN!$jI;9XPWVVr)Nf~7q-Ol zuh$a;Qvw%@JX|Mex*usT|BbX0ST1eGk{8!s9dE`6$IQH>;rWnrj%g?BG=0kNW)0Sh z{hrSF-YwQj-PXXxonKDw*@ZpM!?N(&U4*u-MVr@P?5xI^@v1R%C21VylF1lLQxKnq z^c#?GDs(cf=koK(7=y`Jr|R{pLnnG(a>z`*U?RQvbF5{19cx+I{7$T0Y+0}mb&zWo zj6tq#IUjOuJ0NS@E3&ptooF39BWv5~xP}j}M~uzt_0f07<6Ii}Y=dQGU;)Y`6SGx~a` z$0==;{lfXftlw`RrEFeLv>cC(Ag|Nzd~@HPY%fB#476dUbxEz8)1i|yKI@pVU0zpy zi}^dtllsK0d)3@y&yz1#c=w-1d2>I2XEL2%!am@B-Xmdb7&P|-=l+N82S$=<5cdK< zwa13Oo(-z8VD=tk4eq!X;JDE`vG%88ZPcFdo?s^K3Fcz$Gac)$IY)d+yeBYY#oP~? zxm~Ua&~q*HY3>JD-$?fCyyisPOvVu}XR;)WmvY83Wez@+$HF2D}-Z<>Xqpmwfx8~a3_v|M&bbS2T zX0AhvaD7h=!!}>@_RN}V4bNNROQ0V*sKQVL>J%03chwapTt=BfD{p_1s)Bb*}aYkytnrGO4V@r{? zHQfF=w#?CMd9$7?!L?=st~npTwWk=@p!HbK-P40>lDakxmos(no+x%1*x-owM(uyP zGBYb28cO{@;9GrG$MOm2pVS)zPfpCU_7{1wKIX$*Mo-iY>$3C04+Z5^ zd1XCe=S5n+b|seXZkx;}_SohcI+cAqfn#qP<_?T8JU8Qa(|)(Exn@D#HV>@lG!M)h z%JxPmZzTtwO-;k~Db2d*OSYcvhn~sTGYN61f5M%4E9d~Tj>OO_Srsr6btS;cuhwHuW09Hm#5b?f26rf<9@odtJ&VRyq>k7t7!d8o%3;>M|~Z)TIyb-_SU=}j=_#whudJQ zSmCLeo(cb%y7;3{TjQ)7n4XF_`+GRBvhPn1%)TxZfA#VbXnZD#62Q?~m~<)MR;o0BrZrFNF65KGxn@ zRnveC&E9_mVR`Y~LFK|N+ZDV56~g;ZYf+wvJWuFY@8dugtO;4(-S}>Kcj%P0mbV7q zLQN$#Qn!eYE|jP85<#C6{5^pomd}YZV0D`8?J#;j6)|3wF}ksTobn0pY{7@>?#r!T zy7_)nrSZQ(x-u_k+s20KZTgoGor8Wl3h;}FGoR_J8-$5LbVy8p9{f#@eNCNA#6Py_ zsNY#UK!y+C-_&7zfv_G^7xTQ6!PH}LGb-b6DINId?pi?KVcs=Tedy5z3qmeqdmJvkd1@|Rc*1$Q^D-p#?F!lU@il7Ag@b)P;I zGxj?Y2c{VvTx~k#3=1FX`|SiU)7lEkqIhZTNS_m zseEHX{L%bKV8$< ze0Np*9hUKov!TAes;RZHPC39?x3#8vOWRfumF2DFmb?aA)vP@0)U}lCY-(z3#;zeH z+n?*>qZ=oF^2EZgy!Vr@=l`~5_U3pN#B{u?v5+X zf@y;E1KfxB^H{+!eE>+hTd+%TkEGWN76Q*8UltHID+^DdJs$=30k@+5lN$XU62DjC z!xCQ${1Wm%0Q@1a2>3lTjJ*e$v;(a5PDbj0ZR~n3`jY;1Z#mm0-XhHWH})D z58`6Z_PqxD3Gh`Q>pLg(Vc-j(>C^HbApaqa{)51O1bslGzY|D4^+1-d1d`t>iO&&w zf#7`Lzal*Y_zk$IN!R#b8t|uxPX&?>ou_;OcpjZd{SN@yu3jM46u}c3{l|c(5&sO3 z`tAn)HPR1j^mhWkjC%HId~h!i_Hmea82Q3N6G8i%fG>hiC6MiU07!XCfoxY$;#Ucs zBe+1~=L1#yfU12OA4~(P_5oG=p8=}&0ag1n`u76a zzOcpz+kk8zk?m_0n#lH50%<43z(c?Qkn+&a@8^N@fhU1;fxWid|` z^t(*@u+V#e7|Ovmjs8|3^;ZeR5Du1U^lt`!AMrsT>OV|OM|zRaM9}_Kz$cI|3rKzV zfNW0&5L0h(lEhoUuOogCoy2?tK$oyx3UgSTm(ccF=hWHbbejJD?Hh4^<{~6%( zNbd$di}=GD{Re>LzYj>hdx4ZAtkGW&q&?IEX%Cee{l&m9!ybYfA1nmY9*DGufY3zR zLpG51Fc(OBm;_|KmoO329?k>Vz5yWR=m*js@Vfxfe^%m80cj6?8vQ3F{sfTra9pGR zsKh@5q&;+N^dAP&9=d?EheI0u2Y{3>45WNbK+0DJq~%9j|Am9 z0raBWagF|?K-$G&N$&#EE)Hq*9{|!WI)Tq3exF8v7A-(L`819GNkH~Xsz$#BWWGzOmi2dtAJXU_1a^ZyuhBmM zychZUfvo4O;7MQ~=%YZ|UAM${0cmFkft)7}X!Lgizliv-r0)UJ&e}Bkn}D>FdLZqr zR-^v`Am!ey(O(MuSL7=MeiRr0?nOHN!2tcaz@H+10q~!I^MQT9xq|6}bOgqD`lW#? zg8dr(d=LLL;!gv~{}k}UNbl3=KLOrbm<#+Ikb4Lo2l{~Q?=&FW zN53Rr0l!p@{!8PvzUhCZ8}gq6o4%8@SP%R= zq}OWnR|3H=SfL1|qppOBmucI3M^l!!bx-|Ot0@<(h7s+yEK(^xnAltE7qrVi$ zcKCp7$6O%CG4IEK{&XP61O5L1{gZ&~hXJp~ejwX(Lhz^{@3)w*OX!0_cM4q#gvx`J z8vSKJ_8O>mXaM9}_hAp2`B@O!{?Am;)4gM^66A0*I!34@;L=YhOV z(eESav%r(U(?FIx2}IFguSWlI;4#EMBkA2h`~?qd^dAB~k97LC;rwwxqn~~v$$t-! zeCZ#O@-%7m&y{rEZ&DsIW&S}+%kvtL<@$k?=bT1Abfe^ft!sIKC>re5=symmJdjz* zb5x`MFc4)I!q(I{IUw{tAp5mLqrVhLef|xw6MpuHWw_*h7Wnt@Q}{{XH-WGr3uzzx zAn+OBPT;qIAxYl^ge)KY0I&;K2xPwZ1D^(8Vj1|Z1hU*>Ao;%=_!QEKdyrlWd=NBo zHRwvAiJt)dfY3zLF=?~VM5eP|mM5Y;lLEjt(8O%etAr*(C(6Gi({n(-7c}yy{6zB0 z5}LRO^n9U-OivSSS;fxAExcY`hxnz#ovBj_-&3^Wn#n1nov zCjJ#@$f{^!0q9ntiI0LNALLd1kVmzjbO7<~pjSycaRKODp@}S?4ul*Lel1C}eN0c) z$jh8{6X>(Rtw<++8uV%4UxOxo26P{AFK8lcX;Lrn!=Q-=K_3TVUQ+%;KLxrQ^#8)r zEDVI)lgohb0Tu(l11tib00w{$19O2#f!V+h0e!%mfoVYIPX&G(2vFD$yd5;;T>Y1P|r6vj%muv^ zi0{@+U>cD6Oceys-jp5#(0dW+&qD7k2foVh>yx0fK!fjGBGbtae3k!E+CA-rG?8}C zdPo!ZgPtTb@oCTjU_R<)J$b-vAm#++NAxd%KG1$3>w#@5xsg}Ny#k16YnjmhEcCw! zu0_2}hn=!I`mI$o+Bqp1bPzPLOw#X`^gTj<5PZteU-%mz5cCN$lJWg0PwW-!5)2EL z2?hjxf&f(x-aS40;1l$mryrlao|E`I>N$qb^WH)D1no^a3ICvnle+PFAn71JJCgUo z59q7O1Nc0V+>6hH;}5|bXy^C?_&huQ+<33IE9Ee}fgVjc2LGVp3414KpP(t;z6q!B z{nEtnbt&Fs6OT`X$I$CeUFY?lz3v=h1}42WDaG4A=~a9lz5dws@Jo9AC46>F-ZvR} z($1x!#V|_iAZOYo#1EvshCD+zT)H8}d;W$&e0EMfFg3;7 zF?An4&rR*0>h%sz9m4m+)4HcY@@a>rrFuK3ADHg-hNthv=Ro>v>Ci@ce>(U~zl4}q zXAI1M&(xVGW_rDcW_ICo-^@;Y_GX;S@Oq!gIEv3h8D03?m(hvOaK>JIo}1M_3$2@V z3ZKVkoxo@JtY`3fXjT_KduN}V4IR!Nn4O$-YWC^bXJ&i7r{|oR`uD9iNYlKxqgV}0g%b`{HeEz37UmrwCU(tMskKbfbczIv(u zxX^zqbg$4;q=>{*r3^GTSEJp z1wyhwT~(_vp`JS~*;+a&)S+84@a3;hdKpU)Z;dpbw`p}u5E|0!vQ z{U!^)zmW72;a4E^$Am5x`adQAgF>$n{^>%0Skku${cWL}g#JDHiuyh#`u+jSL%&am z{+<^4fY9BfagjFrY(Gx@r9pqfFU)j|1+#DVX3iHk_-y+9Oot8hOa4mMKh`$)IL7U;M2 zG-P5pjDOO5gx(y+rrgRwzBu_k;S`kZ`AGS^iy_M}ndRXZD&=%5d)G5M^LiY;&n3T^E z`p3w_{F|lxr`evAu$3j_|DzZy)OS7lm|+3i1NxZI{5^v7pwNE_+ao{o`|Ui~8q>}1 zwNuD%9LA^U>md6Nbf?g(C7tK@F?gtNv;k~Y*nqs`e-d&qd>&(ywD~>xcT)bal>fQX z=kWaU9_g7RU69?xoE2Mm8;`_TS2>7S3oFfvny=jZ>#clO_)@c$tY>E2GsUqb$n z*C+fsk(ctB-=lvZ_LU|1|G@I&!}j$d4e_j>i+_f%vwrB0&vzLfmj20*^#3cgPw3}_ zP80egLZ=G74daXa)B+#;K8m@Qbgqn_Nf^&cpJE@Mk^Ild_4B`IZ{xbG?@0Uqp88D+ z+t`Z-zafJMHio^ys8Nf%Z7*KJ=fHZh~H&^tO~p zI@y)}GmHmk{%PocC%qN+>ZE@T`*PApUHqQMxOArTyOW-Rb~@?VF8)72e>>B^?2>Sajtjr`#Q#}lTLBfU+8MzlP>+{yY&AJ^q-U84_)#;<&yU)SNe4>{m+K| zI{7^Y!bu-?(WfzHB_H{&%_f{oa-S zvWs7ftNm+S>F;;(8*lbosIsi&rmMp0|9> ziZx5uef(sb8~rnp^2N{;`Dr&Q;65X zHZEQUXSPkvRV_Gmt(+bZ*Kb;n;&I+5Hm|*I+}+k@>VePN()OmR{PuO_4K?*umG{6c z*_{mqjSa0;ZLOo^)`E=U*R~yQ&FU%&${95(Nve=B!l^Ic4{cezBve8*XrFhIW7wT> z48Po32EI~P(pp}z%@h{Vs+7y!$jHd3_U&65>+33f@|tv{TyOxrrM+s`mg?3P^qE6s z+X1vAS*v4Nh(3$Zx9FS@`nRIF4)6Fi+scae{Ta2pr<*)?_QrZMdhcw6bYy zUhdA_8y>1zeOFQL?zJoLy=zNB)8Z9$$^zHpkgTY@p|ZZJnSL^BLU7GkmA`I#%j%}m zjqNSV;Z=EOee3#mMBB!L?>j%RwYYl6lI`o)R!pymgVz`mli&>WFy~jZ>^{Er93wFV;dYn$PwY$4QWo%ity1BWrd0qTcrmGWVGi_xy<9^6tnY-kaM%yEH zYk6Hmggb6yv623;bv8Y^swgcyb*qwb+G%%1?mK4<**O%Te9N-sC53nAZmr30ZO*I6 zDOkL;q7iS|R%qyB@=VR?>R! zau|8_y|BgXC0lFS?#nJHtzO*FP|*--D6P(EXlSSjL09nGS-5ybWt|%8<<0Fu?J}>V zu)S?ZXl+|)N2s+jL^D(7SK69gvkawGQLR65cCLBF^i6APFPy(;eHqD-ObUu6P zNl?t5iKN()r6FFs>S}g2?rc$qi?hUpsm^j`D}ASzPPis-Ef>w5I3V; zbAp2m?0oCCD}yUoQCOgQE?BiOFW_IP}1Fgl^=Es}(cB z{MoK0ICde<>@h6PJ2!ijZiS9w9pDg0HZ#fcg0g9SE!#1QJ}J)2TfI_Eo&}ASRV8)1 zYRp^!3?B z#_|%LI5Bc5oH!A0m<=~))A}uObDCLt<8EvOst;|)3QO$?ipPDpOhtmzE$mrA<%`hh zy7)y~o12$x+tgfFkRYF}o&@jujhwy%{QqYwM((k^Qmn}nbd)_+j^X~n z+%~)KR1?)@FM@L`8n!H3(Xza@y&d-uB}+=yu4!-IygE^NdsS~gu8J7byP9zH#b^$k zGD8cw6CXI`$UOXnW(>tfdEk^?jlF`kW8zJQ(JH~z5_ifS)2+TRbDi4owoWFAo?AxA z4i7gPm)}#uhl6s=NsD=+mEcqcdrF{U@*6j8jH@6!H}j3Q3fr_DMc~fBRX)_@lliB3 zc)@GdIF8J5mayj1T^zR!#&!xKF7`YGBo1r)qQ{w2o*(6@lS3l4fL6p@x|wpQ$z7m%E2x#}r?t&=(bOOPk-97m18|;=1b)kov>sQCA+gv2vBDX7sHrIyGSh(7<`tk~f zuS0S5Zx^|T*-A+x)Ag`Vp2hqK`}dA-iC5s#-v)gPgzL3 zFS1XX!j;vW6E%G9luHhsfFr0Jne~BGi&dn0+!f=8$VK+aRgS{L=U5F(q~maYl_MW1 zX^VNTquas+T$ZcspR^a5gRfB9JbhEYb%(Yzw&4t{#-{QeJF7xfZFMcJ_$j8YydKA5 z*?Dm+2zkZJ>l8~LsI_GHTrH_HmI`#nY^%PY_|9b^jf=DS1-7z8!{Y2w4#s!xsr1{N zOctRD(p}k$tY8SI!9b^t&7(aOO#7~hK4~jDK8sFnqBFW^5GRvHRmYLB;)WxE5aiDvtl3K52Y=$ z5c6#Yy&Bk!_fF~4g5nnApMkF^^B=E3TZ{28uejse=);xgtrwg=Rq^KfPf%K&VROYk z`$YS472mV!OdIGA=Z{$X`)}bHP}AUBS^I~+{BS;ewDKGzOZ*|jpDujIzoLD#+dgzr zM<@RGvo87~rT<*|BfXO3ec{5Uao~aTryj;R2RKjZb?|-zzFz60J`BIc1E`bbblp6g zX8IA*gA%XLrr>!r)8R*VEqJYgUss$TkmO~#^r6rR&>qCYkL<%Z+Xu3K#_QSN@mOFb ze6v28JFoBQ5-ay(wSOvjG4+KHJ~^LNU^aZcPM!9IH7?MT zX-)r6pSAHV7-lJD;aNwx3~ZoGWnOE=P*Uy@_?y^2&65$ub0FDT?&i~r*Y%_LSl04+ z;E8P}>9Kfqc%ylh{pm^+uinckE1>fYvG@!i9rl;bJlY3u0d$oQo#nyj^+~&*-l|Cg zos9U%{+fof#Y%9_7xe@k*!@gjy~ckqzr#oS{#ll1|JNV0_TPcJ;L8|iQ|P|D5DN70 zi~*i0L_Kz40;FEXW();x7+iHjI^h`K%+qSHs4)g{5kUcTuL93?&{;oaK z;S!)g+eim||EA(BrCYsLX$O1&e;wyMw2Zg@@TE&v=t2U1`K`_a${#;{01w;BP^b{T zy$h7h&@O$J?+pJ3*e2NXbC$It^9Jh=9eQg;=WQ4ZzV_PV%*(N1!KZrw+nPulztR1f z+9uG>2)&rGWL^GZpBV?L9{4qWJN~wv{+o@jWM_Yj*8j8xU&ua(-)Qi7$Ag(P4RUVe zI;{!+oEJwE-Vi^o#;+^O#P#QDetki{NAaH}|A4CwVU(oo{J$0%yR}kr?xxJ|sS?oX` z%O)JiD9CPm_oKh6zwV}O-~C(*^(5HcS0Q-Q;fX9I=fni_(xCfYl z?TAEdaSXNzO$6<)2j(GP5Qw$M!c{swn5)sB4TMe?atyr-Nd5i=+U3*uU^UPOzH&Y&F^P`Dd61^np$W zrU98BWtD#hfWk5$mT<~%1Ce6~^~@z=+$g^hq%%MxPHihAP5CF`k9Tq zV~1|6tQGJR1NGBC3*G22KZ+{;tVa25A%cgZO}Y>Agb|N2B0Tu1fX# z=BIeu_P`fF@~g?I-thRn@U(Dpd>=e5oEd)>-}k0;z|+Fe_)GYHJ_YAbS^=ynxj)>5 zd%U2bh%>TyUCMOC`PdMITS>z<&3)z|;<$g;+}F)wIuvg18{Y%oPI?;C9r=GC<;$eL z>zR)F&3)bb*eMQrEju5wnESN9B!8s;K+5Ma9slOOOYP?cPjlb2jOpkYbKf%oZJ9jL zXVYs~KPJI4@MJi^^y_>!jRm6GzisYI{sHxJKeeIhUyl2)&3y*WxzPKyh2O6rFZUms zdFK*zmFdv$`@oOsCVUoOGO_sGL_G}u!upadFa3Qn%t9LZl_|=yUSWDl*w(j)^@BhCi!t0!{+?co^BF^rYG1Eq z=G{EMKt>aRDzT|&Z>OUy_KF<2bcUh;U zeYc`qW@^~V7yUJ|KG4QKQpq3ma!LOV(!s(GUjZK{9Y(!Q`Z*W98+@GU+g;^<2mL$K zKjq5r-hW6tb>_!h5<%bRqTl0^=Pz9Jb})1Ddlq_g(wkiQA93+#UT6MvSAP8Tr8a~m z*r;XyY$vM+=MJS9+ojYVrr}q1V&pPU?6Fy>s&ZXbYi(ntdYe4%BXP5OD#$Bnm(4Y! zc$3JK!ZVv;u@b*YWEYbs+)Mu-dv60DWp(Ba-!l`)5CTOB@*$exLk&piBqU){NtsDP z5+DdtxU*~~*AyNpvHQG_~FQH) zn%b&r7q?YzZKzrN`wBLiXoitKd4waw6EJ1{$mxx!hbRmtF%D4}?mpuoibw|pggi=I zBKadRfvCMKcY)u%=)2x}hyras4x?I?(dFQUTmt4G8`pj&QR~iTDc&3+g&0c`SRJ{I z*Ig7@$9Rgj;a!UIwT@c;JmRD%P9%7$Dw`{->e@Pe`QmJszm6xN*cj?^bMXo!8K3{Z zP#{URZ<(2kBTRA;>(-o-%GTPNmJPXqZFM*nSGO0ZP&$RIi{GDB3%jhk#NBR^{F>q7#%4pWgrk) z(8LIW-`>cbY;3L8RQvK-EYR`M&Mey$;baz10R=qmtu-wx8@DxeWbrJ}`n>^){&Gb? z=M)+--NrR~X%(B-N~nPw*-HF?QO(Q@1j;J+gtxI-C;-)+h;M+Ox||)vhMfHNs`{F? z4LMr{2K-yIw>7mlR=0KdOUm{(6k)Vcj&4lkL3GA}To$Gcl@0AR@dgp_<0N-TFieXf z@qphW!V282H{=gMj0gaLFt+hA)*94&BE76c2hQ>k}d=)WlAGJY0q?&nkJ85Vsa(i-s*2VE2iI&`1+kmSd8 z1e#6Y$1J3tI+1SicI02IeNWy$K%@LN%lPSU!~d%;duLNM3pV0cTDoD)h`ZyGpUS@$ z@QY?*?X&&8k{^};YNeF`tSN~#RhIM7_U)+O zrvo;O1i!Hp8*{n9BUpf?PCW24_U)4Kn-7BInpMd9EXcUbwHIwKe>hbe9czfDSH=6{ z&d;u`K;AQZ0r$XjPwk@IW5il4(l_n6a>sk5k!fA;!438ZzjrJ6&e-RDeCE}P$Co>1 z3{D)cm45&~+^76d?l0-QXW-pTgZF##epvFJhWmf@u=P74$s2i@a_voR=on9p*Xb8rssLH+ptB7DOY_?Cse z!&L>nAL8z0d}FbP>cYMg)A!-$ZX6n}vcZiVZn1B9e**4+w;g7DKfs;m@8Y{(!FM0R zcUO7Idvm)HH~LR=XoI(6k8+l6*1;K$nS|bgr z{H-B^MJvBca}JINzANu=@50{PSFk_mv1tdVJM0JV!gmpS zdN$kOudmn2=h$oq7ii9d*X*{zH|#4vd=dL`%=cmB!yCAdmg#!~-&prE?U`-YX<{ER zkf2>&JsJ1UV$X2^kK-ZjWvq1^wpT9iMIFX{$jk@sKTbW)dz{JVNxOEqJ6X$_1-kQ- zFP%U0CGX=s`)3R-Pt%sm-hYyb>|G~P0x38@Ou+}SKxOxe%Io6Gk#~`cgK0wv7YZP>17>b{p$H{X74_@ zy&vva&U$v#deGK*HYaL_QytR>v3^v}_;lktcaGDtdmNjF0uJZkzc{t>zneGH)AQZk zy}e6-1$drm?>+7ZR^j=pw%%u2f&1||%7=O$pXvGAr%C&Vdj6L0fG?6R8|pbf)ARhB zGd=(CF6e)-z3i;p>-p8SYtws>c7}Bj^|@#NM)7+gez(c`+%5Nn66w%?)eCo7tMZyUN+&braFfAf&FPCbOO{lfwq?l)e6vR8;Q zSb(yak22}Qea$}>d)V|F@5Ap;x6#UVcjD0ZDIf54l-UnOdTfKY<9iID4xX`VgTF>Q z#5(#O%4QwzLWXWBQEsqzoZX3Xfi5m(IqpUFp*=W``($%%NrQ>_&2W41%@^3F)!&Nz z%zeQ7ILc_sOq9bk@%)kw`;>el*a+&<%5YOgOk7~;J9(n_u@C7 zn2Y-RrgX=45$zK0^<`T~Sqip?)6mxH?H>7jnmoL=KjG!8Rk+i8j+-UE8<3@y=h`Vb|eN>NQ)=+J7-@l_jft-gO@+J1ewmJGd)8UnJF$O0?IFlri2Idw`IM*ZQx6(42Jp{5l78r(_c;?) z-DW)+DA0ykx0gG#T^~3OJb~rhlh!>E zZTJK9Cwo!186NgO$_rQy(0;g3hWXCMzPbSYf!#R>{f_0=>^Bbn zX68uUfNt57EMux}44|wf!tWXIVZEZi)id;Z#or6@Tek+@AYx0e;WJ?_yvoaJ4=6!?pI@0lz)>t&Ko@183bH&l%K(4yQ_GozRT-y|0LSO+tJ?Z{qSs*?Hf?G z^|HZof0f_S$ z{rF_$zpUd<@cW7_SeA`(!mpFG@)?k=Np(ycdi37`T=11S$YYGB^*qKmb|StQ zgQqwj^geF8hOwJlyNvye^5qy43?koy$cHz?SWqADacuLE2!mrpJ>7acrrVp-_EYpv z{BC1?m`CD!i*Nkd3OM${V39 zzJjT=7{64Aw%mvB_9M~%@$Pr$dhh8o+JT%2Xe*sJc^^yiX@^T}XmPEq`Y_o-=AZotqW;5Aiu&H4HJ<5UW;W2jWv2{HvFW9{H`??HbINUBEPuY`%`Z)VFvl_J)VF3I zq^()~ zJuC4MiM-Dc@<8dAun;f}?gw1|( zX#?}Rftheu3%nh;2#BQ(agQJ|Mbcb=m;?G*WH-J~dNptn!mH463-ti={W>816-smi zF`rJK4TK1CdIB&5coqY0y6XdafX@O^cuIM{A5h%u2YHwDF3`+Boa{+lz9o9n9XGq#9=}n1xey#&zT}9jYuq*D)X8gMo+8_0Xvn4c*?x}T!s7RqZdzL$WE zZ=a-3NSbn9EK(IfRJqc7b<9fxF2?(pP=m;?Psc4KK-9~0-U~*)^MT|``7MUu2c-Mw zb=<;xy_oL}5;p;pK`#ShX*PWdkmV13tiU0ZdD`~_S?*tx2%ds3(oEcf=u^Jz0RZz& z$RXVXuic=*YJ5;)g+#AJmqdW!4n^(eAn5B6)pbD9M8reLSCM7_$fwB(js@dG%A|9XUd1&pJyW0MWh_&>rY1Q0 zQ!b_q;3Afj(@#yu#W2$^O?N_-eLsGmoqi6Nukd1(MEjMQLo=QBfti=^zklXMln;&f zWt!!l{tD%dv}*5iXpe89)|J^znQ7Jw%0a1h;)SG zS3$cazb}G!YC=%!mh>{jhwj;q(fj~uqa2dzAG#!e)t`Jz`d8_7NcxE6XOsNcPSLO& zlE3P&mLY#g*UIp?m_fQ(y5Aw)UzK#NbkF-@X-?uv9oKE-?++6}JA%gd=tuZ6Yv^1% zX`v5T=t&6Q?4Ao?W_lOWXQp3AcxL)fEc7aif3{<0_b2c{%=A_ZeZk`YKP~hM3qSHQ z^YdBK^KFa$DU1CtE%qxcbi2j9!xH`~i+!(!K4;;VWuY}3-{m1@>lS2WopXln={L_SB$;KUDz{eZ1Xpi0hg}O9jxyJ;VUj{!`>(Nuq*T3(36~f*UH8=&xYcFd9PQ` z=yIXy6>4IkFqWUfUl}0|DqKC(knqY{_v*1gm@=ryA*q1EqY|z#JR_T$Uhisbt=t9; z;Lvo~+0o(OxO&ry)eS|RZBX)x*t;{bL-(+=@|R|9SdvMfBdS)VmerNpe7h^_8n|`b z+2P;b;V&D>9G$Iig$MwetUB8Q<;B@_;a=z7P~2gtzo0^zvO0HQZ$oEi+sfAMoh^Ht z0?@ikox9f3oUsJ^f;il(w7iC;D6_6HyRx~t0nyRFtDeBNs43i_SgCDY+J4C_JwxiSn7Q{R_CwUv}Ut= zt*8C&J8QG*o7avO1ZtBh2;Zua03`o-3Tr2e4uuIvv<|CSQ*sPV2bI1Ds%q;4+fWAe z%4SMfWoJjRIh>5_k>V=~zpo}AHLU?ka#2qs=6>z!oV(UE>}gr+E084?Dxj!WH79L>c9Y9t?3GgeF9hpi@US{_&MhAy{UvcX32p)_~}`tc>`YYk=@#u3mZ>TiV*b zwHoa+nqgJ8r2LpUW=c8H@1@)BM3>`RSuC_E#@&VMx?{{rUvl^rTA?DQs;RlNK-Vmf zHnzNJq*W1P5Oih1+-(wdk@~P_>c;4J*1Ct31|Hj~hBI zXeyGRp=al+(v>aIX1ub>V<`*8hWw%=1N`*Q5=lNu@tGnSFH|p7Ko*Q8pMw3SGMsit_Ce!BTI#-Ww| zvgX5otoh6OZ@+bN`_V7giFxtJI&uk^n%6V(Ni%ZNECX`ALcPM;4rrG_W6emcU#NUx zTvP4c_%Dr7aYmW!L>x`9tuVAP;kQy>GI}2~CiH)^9Jn2k0|rH_ah>X?;~v+^!58^)4B*80K`f<*$=Qo>Sau+S zT)Y~@aXdtG1Q8-@?muF95oTmP%Eob>nuh9~SdI+U-<2ifwMhCr3x14W5d75G%*-^_ zxU)j?`=azT1b)oQKJXiD>?Qd{)Cblr`Kghv2a#g@-Uh!EbHwEDSmyv{&@;>_9kdhR z$9y}4k0G}T=UUsU~U=OjP3^i_*}1U2U8D`_olIS;EiZl26QU&y_pPIu z@bVIRF5{f!H$^hxJ`-Q1A%51pqZ1>3m_N2PwSo>U&;630%Bv3WBOgy({KC(*@O(Rs z;Cm5Ms<;mYsyL~q{ zwI2e*H2K8h9nIba%Xspy5AImMLmC;-pVo||BsPb?e(VQNSG<*&6S(iG>Ob$j>b3e9 zSv@kwTGr2CjKNCb8R=Eskin{4{*vu$nE%&cpAWgCXMt$5(hmTAz#z~K`6$Zp0rRSX zXcJ2-bj;fXTm*OPfXjg{ApNsU+zNLbdja#(fnR{!(rrMZkilm9~mkgj>pOAaPT3B)xXz-d%1dn9!6f_Y}GU6s^;vCQw`uo{L$fYIUD`_I) zn9TIR-)th%nY>KWM8q>0aT4}K@D$cj@`-lO>!A@F75Hv)wq=U>LyQi z8T2CBe@@fXIP*TFbMoh02AxWK7Dl9jhH_n#r??FIdfKA`sd3;q($}NxOZRgTUMl=h zu9D^^@TdMOW&RdFU~hj(zWW8-lBPT+O*Zl?)zNK~mw%=I@j>Hzdyw8#g!QO&KcD_h z`F908$S)cGXzr8z3Z;9YAIugMhdpTjbMk}Ty(D1v$nZ8v|L?($;d4KN=Km73rnw}& zk2KjeytMUdus+K&qw<$WdjC3I^?fi!p1-X1jS zQy=|487omKZ z`D_2ZnyJ(XreIAAWH#a$yq2^?-ELRo&~={rb&bG@mJv9e$ya2&$45{jOLeoHcM4&-Iha16;?}{$_GK7Nv29w>Xu?S>1aU8*fHL zb%dObD=dGQX)$kARO|}Y+!eJN816#8HE%97B!=Q>Kj1uGO;tvKLZB#8X_r^$MY1-E zWjq()8G89tS&o_^%2CX#+>C@kd6W@^s+v)QFnn$sgMui-Qv_{H8A>S=_w=EJXGFJA zhFLO)*EkuQ{uyfTUslofW~>v}h+E864Yg`PLYY*TNR6%&NoED^1rlbg@;KS<+)TA^ z)440NvY={rVXS?d*D$8hTzYsj0VSj(m;pI%8j@}ljbVkxheK7Gc=v5Qx^s>E$hsc` zF$}3Vz6}zTG1jFc?As_li=+qYKjk=Q3g!Yb%8HdhgI|Pwn`-IWC6Rhc$;SnL7_x<# z@OAjmb@M~ghuXI}0Dk1-1wWBq3&&XdHs_@KT8U?5{F=c}Oftjx8Cw{_#=4II{UgF0 zk^bEHm>4>0{ose`Pne0hZzK8LDt(>-KXblWzlmBmkF;+yB>Aa)d|SrPi@BVY7lRD{ z9-;dp2c#!eUV|uiOfSy|j?Pc+8%Ed{IU+r&G#&y!zN(0eU--VrQ42pFl;*4ESo|XF zi*#G~9g+Nk(fLK(7oom(8kK)X!7rLo-#5~}$SKKBu`%`y5kL9qRZt?BSo>O_(k6rIV1VGr2jtfV|n5Jjf|gkp*x7RFVYQvj1oOz{YU7~!nsUs2x|#!k1>Td z_C;|1Q|QpbIv>~8L^}%@lbMTs5ij;dxVK>&!+kYT1}}A+nmQY+syo&p2e9fsvXd~{ zK7uu}5%v)jPpxHFWxl5_t6upt?-MZJm5Fa3;nUv-p~CsELRDxKvQV-gZN|fL5y?c~ z7Z}OBhrl3BK9P9E^F9bij2oZ-DpNK5+W0*QGn!gfzw@VoZ%q70YdG<&=QFLSd*VI@ z&){&Go5%Zy1rDo9*ePsShse8#u~L) zzjXsi6X~Dh57I=`Td@v~_p`C~%=6%49h|fWG~yPr4v(rIvJSrxG~)?AA?x6z89&;y z4FMYK;DYOLFfb!x7Ed+{E9v$#(JZHid8UI29AJ$Q;GN4h6} zq)iMdh!vodfGnFt+A}-=(=8Bj6YJSbKi^|HQqZs$G}0t!<`?bLq(2*mLW#KJf$x5b zHqg9|{R7gdB+NIOzb1`_pjs!jW26(X0W0ahz;84Cb=u=OC+!iIu>Y>4ze;%XU5NUj9)h|v&cwBdVU8Zj*_;X!VnQ!3-*MYCd7heK@(w8Lt zW73Xp<9jYb`nazAytIejL);57l3)KJ`MG5nJU>kTZPNYM=^l10r!+rfe8vZjeU2v? zU(@%#lQiOgRd+7#|A4){L-L;vJMv?Br-6PwxDOiTDU<#ay0x>qwf4^p-ySr|>vH-B zeV??qt_!no2Mfb+EMTTTM*Pk6k1g~RglD$D$KrovifweU|Me@sQ5P;%JYuZqDrf6F zwWXfTojLxsrj?dJb7#y2UD+3!?mmcPOcQ%SH{#NY8o3n2T)9;-i?wpA+;QbLo+UWd z`;YD31{H5rQexfPV0DirI3wUVayat$jJ9knR>|U0=&vGEu;zEWimW~cN}(#6ajfl* z>Wgc;QF3Y|1!6&0U$Ts6RZ+zYtBUck4@n7EF5_N`RH^z*OOYzJ@hn9~OON6cd*M+< zJBHSJBmc*=%&3@0SY{mK!lGD=R2Q}jLLD&@gnP{T(=&429<@H$(z3myt|h-Y+WH{6 zIc*dA8qI|VhUx0qH z>nT1mTH@ZjD9J!WtM9r*I;B9y7p?QS1OpE0q<(MWpo#I^q~A2!9yme~5g|E_J|`~42@@P&!cr)uxVZ|a-W^=M*9oud7)`8@C1eM7!Wz3SKn zxqxyv>;|AG^SycY-v0f#Cm6b~slSn7(Tx+jY(3hHgHG*+RTFN~rY@W|!@uyR@k?&^ z94cF7n>ca7IPJOxH)+?W+Ge(tT=$h(6Rzvqyx;+c{~^bOpDuX&f%9+8{mBz01!)JJ zwgq1ufG%`~LEh9mdF4g@x6%!s3f;HQ3}1r|OFiEGi3P~JmB_;q(`c>K^cB(%8D`}M3Zt4F)Ajk2_@F&lKD*jJ^Hd$+wgE7<7E%Z8z z`+u^~4_fG(EHoM#+0u-1?o`@@Dqj!^cOsrhMGx5ur55`2E^D{$z@0A}GTg1TxDlrM zuJR&}Sp^I=QRuB)Xb4ih1Fs#z?JVt$*mcLPBh^JMO*_c~cb%BI=-2Hq(CV5h+?3+4 zs%)rifkqrC0BPZcGrqRgrO;JDyR~j8=)uwVjLcTys#GY@Xfw0pEhFN-8_O*K)t|e1&r+(_ zfxZ^MUnzYsJm)~24UYA9cKTOg9}`=H<_$t~&yCgsW~}>p=5xW@a~n5QZ*K31)n7Z= z5W5(f-e3RWeErCtj_O4JaK4_UW2AjA0cCD?^GUNDNwW<2|K{`c8M!0(26g0CX4l?* zcUiQ0#Bpj52TU$JFdX;JARWORlJCz*H;Pu{Ui5{+#5?ZQTMMkOfgeA^CiH0>TcPI- z5|oK`9#8QRe&GpvtHn4_`M08ZpAJ1zYuG-q^q}~0B)}ve0m^=IikYY6w-Yy2HuAC> z>v=M-bj!5MTEzT7AM;~|Fb=EzFpKdp;1lr2xX}~m5vM?Nt#be$q6Y=WJdeP*kz+^^9{n=h zu^M$23t=A8#F;N$$GFY(8wC}2KD;1wgj@MTcaK^^7FG`Joo}t)`||XzfyWDv{^B2& zO*zfiBkiX0@78}Hb)jZHNTjLMrDNV~AaY2Y^FW@6a~{AvrWaz|=@=uk9+fiBfO!Ds zBZkOgjwKN9apN`R{+-EUogXih`6hm_omTrKl!xZ{w?sNHA2V_-!JM3E(>EzKk>qgDU4g!28LGE`#Pgf%_y|rTZe%XdIg*4N|O=tN0C* zwgsiFrd5(2-))5V0BHJA=HKwcOb=S+6Z}=co}NVi4musbsh8UYPv-ue zeH$OHBSJPn+z$vClfkzZ_hq1qI`jyRhvom}qpxf|+xyP9C-lAJ=)U?2n5Bfd`PZ+O zeS5+27DrIGk9Nn^wf)-5Pd}d9%lidC#5$WM&y4dvooP5T-ZR%b{FXyI?99XS+kKCD zY|fvif>!Rtoj5#OJ}1;&F5E3QxfAETEpG0Bn+Hv9t|Cm{1LSoq7x#!bF^1t?a!6wr z?}h;$!XwrMd8VB2-vD-3*d zOXjKlcMrXR@ZN;qi?ADqv|o|;&hXA0RJ7q(caAX(4e_iP<$S!dV?6oj@j?9M-7o4q zy>jOT5A*r@e%$v$e;16jN4_^@K)Ct;)_pce`>1BLds(2*Q{La=(EiW&_RM+W;xGL- z{awz?ai{P47c#MqU^z~O390*hA>V5*5~J7`>hHCCQRzlo7ajn|+tDOY?gZo8(rz8| zD7Uf*?~ed)#{CanI&OIsh;}5MaxrMwN*~cNkLUI1zd^@5?ny0#yBR>tCrVRv%$ouv zU!FId1HN1f18!l;5Wn;R#FOqX>X=7)o@t=Z>X>&1xB%{W9x@Hs2ZYyBt^)(}jsfYu zTgSXMAo3{P3!IDamg%^~1H1+AX9KSXvQe52oC0LJl6A~W0H)wQLig?t#@xp4m63k09>_$?41FcEVQv>!-#G{J7al<+pV zbdNhW&S~!&cLbMYJTmUUxasIa%tyciy6wgD~Cf>hs1|EKFq$ivo=`t*zy}3PR1XR`5`*P;f1;Ymna#>Z!|H z*9h69F@1`VJogolgTA|nG-4;RAmm(}2y0BmQ3yd9W!3FlvcAc`VU2hjE1H>ZUprPE zu=r5fT2s_ggL2Fu{Ll?m|&Jd|L}b^O7&z85yS9u+qI2ezv#aCXaY>Dndq+OD;-_d}(DWN^bo6~G<#w+8L4HhIBqRAn)B`$S zdgT0p=27qy*#f32jk=BgKh`>3sdOwR68L>mj?28@C$rMbPW4II0ghZo(TllBy2j92 zn7+}NNIQZNzZmCxG4~PXtW57F#1BKpFr#|jNc(z0>4Wnqnt_<{6RY!eJ1e)>)HQBv zvaahM(%Be!&wX2qQ9+54QJpvfOK)+?G(oUJkv1Rx*XGuxoYTm6?8l%!g;=J z?Ypv8uk&mR`Q|*cw;oK*XZqg8JC2hXU#XpNm}d&N6_%~snUi%_XdJ@z68^`oFaKix z{D~jjQt8qb7Cg%2i8T&>U+lllCZa5g^HiiU=VpBo=e_k&&b1$@kv9I*d`*{0bm*hi!tbS=pVx4Q$eYep2Uqa zQBNC<>VK>8gY|U?{7lW5aKnE_{~Mt{A1A+Sy7aMAF*Z1a{xJAO=+CnxKh+){mFab& z5ym=dm@)W8(3cRD{M0uXko-=8UzGGl)R(~Xqcl@=&@RPHZ}jm&m*l6$W>>@csh~uN zEH6>d3rK!089$}R{0!17+vSjCXTqKA`|vA#U+^6KF>drkcdvn71Uv$#>yW9ex9jlG zu+QYf@yTs?WK{4geq+}6yhQU3gBSLivsYOUw&%1*wCBRV5zad>f9HF9HKgyrt(wTg+FO0SOwWN^ZIaIK zW&97^YL|3DFXMaQR)?eudl|n2w~l-R^<5dJ&8B7fY(q<(2xEd4#JP+KgU*bvJb?mv z9qpZXXZ-g1a?gBnEz7BSPfyTJT<^3^n5gU?(uS740X{anmQ8a7SvoP;M7kPgo&%kgmTk9G~4!+`aef&=UEswsU zEq?BuieLZJJKbyk&pWmW+TjmiHyw7!>%(+AT<-;6`ki*%Cv}jA`1|AE3bz;F&jx?= z^HJ-FAJQT2(=WYq;Rcpjo&N__p>9>&Z0Wk25Wk3{i2rTg-rXCt-p(Di-uqkZy?eGg zdTa4LKfb>R>0g2P79u|ikT3Z->*DK0K8dp~@cWsJYO53`qPNHVgu~iU6VgiRR#N~j zJZ?N*JTwu8dX2jqH)^6RTsBbt=*(;Us1YY)tbU` zt=hAsx^~T)TCHkVd3|epYnfK%zpG?#OYz-|gkh46HdpC+r&T|~cpVRaQ@{E-`TGp; zCHeb#;3@fgJq{dw___I0SAXYx*7G+n#Wtco2P3{M^TmzMBz2Ub%|#5J(DtYI0T%$d ze@D7Y(vL{G8n_VexxYsH`O^L(_#>Z>5iv(9?UytWbe`QOQ`(_}1Pp$wWqlF$1bB zL;p+#!<@EVaJ3*n-Qe>u}$=PKBOhjBcafULSE` z$B3~<^{Tir*&pCW39dZf9_uD{!+R_(AcjL|0g1Rk6<5}-fuf7ivcWjc89S|eaLZL( zVeP4k(_xH#R3r5p!xzwEN7uCGhS5)a5if|Y6~l;7Oq~|-EZh10BgaGi;#6zwM9GD` zT)Bs4tb^kI_p0Vjk2^4`;Js-<&sg>|y)zD_n%)^A`lfd{uo_s5?U%)##VRL6JfL;L z?Tj;?r8gVY!;np12q>O7_J*C9TVb(Tad{|C%>ZJ77kG5^I{)cu$XJ8Xw3#}3{?-rFhU#%~0+46_*o_xMnb&qFSf_C!z`!^5u z6x`F%>$vAs+C$Fc&VTeiHsb}c?sN$5#9G2r4{L|tkZTBBm!N;=(1YilHrvZTn``TR z0rp%Idjo3?Z#t$6`E$CV++yfqxQfPz+;&(>t7IvkeX`Iq-zf zKJ@+N`280API&LZ$5O{>m*>yS|FA;b(FU1+o9*BPn>ROUoHuvk1aIzwIY|o__$DrN zPV(luDxC|tHp6)Fv98rYhsnYetW9z)F4Z<|a02v$I1_?p6KwYS7ZbE{ewPDD(+)c4 zcuy~XK-*P^FatqtSFcEizK(VS^U$s>RqKab7sF&F<%U~+GApo8+jR!{g8H8l{N!3W z-8$#|Xx7i|&V|ao_mjjN75+-_Wq3Et(N4G7c_z}jKBuQ+w6rj;2Mo62{5$?0#0 zUA>laa0l#OolLuw!Fpjg#ZQ0r+B9MJv+H$#&%mzsOEZMOI`~V2zkY`!Y>koMum9^$ z&bol|V~(FQ8Ebrq-!Gv1WW>$1QQf>2;%0O{KM!#e%7=CX$P*2D<<*A1&oW|OgTAmo z_*3l#*Fmn;*-=LwvYu9mZ=$a|(~lkNEY~{i+!rrKxit*egB;oehv&X``jfZqd!N~R z$JXzhWX%hc^4I50XlG>8X0QvCSE~6p92}LMz*)S(cMtQAb#Gz3a5tL$Yj`Q- z*n!t${h~u63oB{2#6#diItWA`->^@|EsKCy*GRt#{#e^jzXT-B@UXrkQU)Y9rHSXXj{^2fmmN@sMc|d z8;Je=bQh4}aSXul*bQMlLC6VUP2mBM&X>BL}k2JqDvw`yiagc*ES?LX1nGm z+K<}0ZBBd8{)j!nK`Ht!$kAU-&}ZUnk@Mzthn1v}H)>o_KpJ_ez7Pfdxz3>a^`)dS zm{R>R=dqK=xeWS2_~p8S>SsGiBXO#q{}1pn+YejlAHdRVf5JkS zf~VR3R~Gw&mhf6E^sN^ENf!HDi#@|Khd15gUQOwu$_Zm+Bdo|an^GK}4aGt` zkj^uHmK_^6x3@#6T|`iEGGeMnF@A1!tCkjIjZy9K<+iP5cRM!f{N|0u5=q#(* zR?`Bh<{CtzsqxPChK4|6B_)DKzHq!k<9dM@MLOIZ*X`|BjJSn-E`@27vPOQ!jLd-ec=_$ykkcD7-9kXKeP@lYvAUq8 z213<$*6hj1+A6G9%I}hqy%W*f*}k)&vbnMf^0669wzo9xDQar*39V7Se9nEhBC7ta zZAB{Kf%5IyqUgjGrr^<8WRz#&o3S_M{P-QFh-$3!^~}udrTPK;ilol$&3oNh8U9%F zb>3s3x$wZCFOGVz%n44CV20=i#*NOMhW#oZ^xeY5J70ekg8~}zLzAkd&DXS#`C^Ro zb;aiv`h|G}gBZ5gQx@s*5-&~vV$Ii2$#`)bL30B9*!7iwAG+o+6YG5afaK?tKKmuV zTJV$oipo0SB3uqBRIcf5$?t8+Z$R>E20wm7s|n{9dcM9NgCm-M(Ls9+-<9d~q-$D^ zd}jv9FQT0CvoaEC($g`N8-Cw`#gXK$mtKF*AieU_ha@`?sHqKXYoY2ZVXWUHi26lyLI6)D5(oVVK936pob!*!9)H~3J5X0daSfioc zz$ZSe8Qn1WM|Q28>qKv0eJ9o79Hf5Mhpy`SPZgg&@WEW{P#4yR6hE$mj3!s*!g>v4 zd2$!vf!t4h|MVwkan4OS8?#)^<92WEz&x&L>1%0M@LTcbZ^lu{qA1xD^IV_n073A$ zg1Nr#c4U4ZIV`O!o^ZgA+u6o5+PAp@`S>;D$M>-YHDtfxnQd5u;#!W96YH~USv`=c zN=+;o#F^sqTDVJdXeWChYqbFDB@3v>0{K6P^_(8N^PmPDyOmtq!*^hM-nYRm{NNis z^K1C;fy`MF;&jbEWAMU{jy%CNm^YVlG%2_y`1*f2cB?#T=4h;#4T-{^Z0qJS6{QI z45F@-pPZa>P_+e&b06vg+aIMX8s#TNtx2y~YqI1$VdXdJ1hu?_6K+bjVe1 z!*^JUde@IOoHC7!GtM|2w*}dr-hlj@j(vb>*ay&Mc$sE>AK(V;1E8M}I%{Sef6zXC z(1mqu=2_1t73dqL)qe-{^as3;FGahe(i?3LK;`#nW$8VX9m?2p&jMquLvzps6Pqo2gx%Hvx7z(x2SV5q39emkXS*`-1dmwwt3} zu1bX67o|U$2g2Vii0^pVo_E4ux^|g+9~G;lKeJtib{Tc5 zyy8ynbOqeBm(dOGuao6P_|JtK)WeDmx?M;*^NJa>2Sxg<3!onI9@wB@HuVxN|hLIlwClbknLmF0Cdg&FVy=P6QF+{GO_*8({x9% z_Y~bHP4W)UO4JUgCaxN^BaO2r;N6572k*f?&k%G$eSmiGDeMnne3%lD`y&kF0_;>= z6iDMTG$-9-D8tG5+e*Lz44%6baZ$Tmmu%9*3}y4Kiwvk(vR zQ{yHzUi#pkLgZyZ???5J-GwfGHGUecUJf|y3vWPO{0YiB_xj#K`FPinfV%3y_)>4T z&LlYx&cI$8>xLelmh+Zl`of>W&x2@#-a^?rgFH;?tUI;JHe+H^=YmsjIZ_tVZUO8j z;(a~Fu{&%@tFVla(qKzkh~G(HwI$UhN;Mtid@i zJ|X8c`rMUcB)X}+dhO%^%baO7+LQxw&Lrd^WV@8}5!}KL=S)RtR}Lg;!;rV1uG&Yo zp(;)K{B?9$26LOj-{MY{dF-RcpFSqy1V6y zWdrd0(V*6wYO`G)NY#dK$mn|FAI51XuYO)TJ$xJ5&>Qvn{grt(&aKhc-Dvc6w7YVf zclblR`zG>fHoohO+I^?4z#qR&C;GYv@qOl^ulok_?m*IwX!o_#NAaycf-b|&iQ46d z(BEM!m4fsNc>soU0d{i{mS|^W7^<)Pu*%n~^5Y|<>+0vdr?IAzas_@gTzo6d*qudXqQtrccnfc*F`Z-468KVJ=sgjU?1NNy0-#Y!8^xkhj%>W%|(B->Nw;d ze&d*Y{8#qLgL9Bp#_MRJmN6IQxz%nv%&;N5ayj|Rweo35hbrIvt}n;Yzo#Le(ky*E z`@|2BA1t4<=V_!%eLnR?)RP^@N+z; z&k;V5bzaf*!#ReTzVIUF8#6G^(a+tnf4%~Lsyy~1-{`L&@h5(Oa;Uy5)5>@6p&a1+ z2j((U#oXlM`&T0kt5Dxo_6`*w-42I1gY<49%Y;6kxq`JK#zV#NeUvfqGUjv~2lV64 z9})I+ZTNlqg&%b<5!R}Pr(sQED(V{f(A`rgKdPOhf6iOzNAQqtm`=*0q+QkLY89w6 zoSRhwL&qWdc`=5k+F;ie?_=PV!gaCF>YwvduIqgkc^Hn9HH?$J7CoVeqmhpnL^%4K zm-(QNt&t~E=P1(>EiP|BHpeDqbEtpRfqI&NhrB8<_EUKy@`QR{*LzQ&(GKKHK-!!) zc^^yiX@^T}Xs*UHBj&oXA$YY5^I9XuQ+x+js7bwS-bA7c@^#LpjBJ%84_V$gAA6 zInIU7>$LTnI%}EW%`LfhEt_kyc)!az=gG~=t|X2)1Q1`y^UYfRHW2+b`?<4>3(|sf zBe@rBS~ey%DGRSS)~`g|c^6j_(oA=daS`q&z#Y@i`r%`Grf40&EarjAAKJYD{u5v) z+)mL>;XGb8f8*ULBEGGWm-v3-H}YSUQI+Svi*FB!1gZ1Y{+^H&`%hg`Og|rZE%6hO z2*)s^>5Zi&<;e_3zvCZBzc30l&1KxV8&6O;Q~P^05zc8=`WlMdHEk<2rh|HH^hh~0 z?t}dn^jmoTZ#?~Y{sRwW2(SGNkA8+yPd(32TK$uz{{cK|2bX6st?pDfbQe!J=%Woo z{W~1=Fy8#l?~QSfNCWxqD#|Kaot@vQ?I`t>Y;Nt?p>5r9SLWJ{9XZdw-3Me^|^!Io*fTuzGd=n*`I|-FP;UBvAj7b zIx+5B=E5dSSn22&uOascL>`9upU;eiNy9UmeW|qfMzDWW+Dpb^;U9`&-xb6DNDO-= zXV;JTjg~)@pQJ$>II2C*)6(?g8O?v8OjxA+rA#|bB>RRK_Caa?I(Up0{=FDv($wNX zR!G<4mH79Pv`0TeImrG+E1n`%*k7bT8Lre4_e+b8+7B z5nzY(C*z3?XKPq`M(;zX3(=3#4juE_fXDEv8HltTBQAk`gQSU|^J;;2;C;1@dFz1W zQ>tTL2@oP6>C1rRLnI%Mq>1Fyhsn<-ynkNDyi>rZ;O`{x3E&AJvbywH9rLUCiU?p%Jkl~l=m{$lyACsO1q(2I6 zlkX79Gx3tdixOX#cow(@_Rj-v2R;id2X+G&0Xu*zfmg26IDts-2YwUyDvcm2?G=;d+7J044(;1ip@Tgz@SF z?gBmwq`M|RF-ziX zi3t)fCF^`&mH52GV-g>g*e3B_iG>muNlcN5Jr)tqixSUDJSp+0#787HNL&QucXk1r zfsjKG`R4^Pe>@VW0FQ&FQUR8uE7(}WCp<=EIeJ^tM3y7!fpWv$t3ZDLHi?^n{0`+h zZYcq-hCR>qH32#CBz8zFm&n6ad{4y*L>>quUPNa@JR|X0iH9Wa17;zf#74xE_Xm+C zg3hZ3BA&;Hw6Bmfk@ls)MX)E*zC_YQ+Vk9B8tjR*UnFTF?Pmkg#~dTlp7+R*Ceohw z-mv`hex5Gm%OxGR90M|+x^>)A4XlT|65tl#0Q&Qtz>`3_JE7y2F5nToe-wBW7zDE2 z*$2E2;dkhmR|2HJLLIlHNqWAdX9K?le@@`tz}FIVzC4Fdz9)hIgM5Jx0c(N#faSm- za0Za|jw6jP0bfOr&3f7e{0pE5NP8FX0pJz89^OS@C*sww;}-U>biWQr_Z}eYc`}gv z&Y}~dJ;#3sfgC6P36SH-M}Zu7{xOicJRb+L-+c^N4Ll57Cg~~Cp5r6hUqxR{`)7eY zz+*u1J#^fHJl4ALz5)0hV730fp+d(k_X6o}9k3f%0_+7Y13m(D0Rs}Z)xCVmY^QU-uu2KEEL z3VaQC0C*1g72p}*9|8M-dw{2a9l#U77GMvs71#}I10DhH1|9;o1Hp^&oD4s_gl}>! zun1TIECy}@t^j&~g+Mp30GI}>0=gv5kT^x66No&W3=r;K0)7$cBhEtlPDz@$9P~*^ z6H`Gm{&Y{w2hHzDns_tl9!V3uppQwKcoXPuNfUjbk4l<|Oiq4O(nO>``GBN}H-dfy zcn6#kF{DcFkTh`_=r&0c@vW1qB~2v13P}@ZfW8-)1Aau5iR5*XCN2eCDrw>kpi3l8 z^nfmuG;tbeucV2&pb;m*pO^v~egsWK`AkNf1WlX@dcLHIjIT@5L<~KWX9MwG=*h_d zjWh_Fmd%r%0N(7PM1Gp(iI1G~)$6vxz7a$!XirSWAHyL{MBTwF zohH_SCjWV`C!$;=CjjSyCgQsZ_j5plWV!`?i=>%e%^}9Wcw7)&|%2eEFbJLNV zoc4j6FWv04pG`fNicnH7;{WS&`sXCt&&_*v9`47v_1vv?`_;7If<*g?1t%9E4hwql zyKBLb1$O(ng|9A5v|n8qyd69jUcv8yg_joE?T2pf!kg!B@4Fo_x%~uwpIZFr z`(KbA2mM}X3}|jeCFJ?Reil%eVpJ+K{Xo2Q;hX5` zlKu~NBrsPQKJ0%j=?jwnPf7nR`Jo~zx%}ryCt+-a#*$_x<7c9KkyfTBDC2WGDk13u zlCD6ZBz;KIKSy4XJ|gLxr2lS752Dc^{}VDijuYs8J|*3|(b&`MmF`bT z`$^J$pQIm`@i`;u|0(HnlK!%E|C*#vG5^qTsB-{6Mhh@`qRXJaA?anvN1E-D_DcF0 z`o}vZ_ut6!gH}?}x6mGWrsVjaLf%iFo3%`NFd zlwaB}l5`H*8h#(OF2?({NUL?Yf06cT-R;J4y1iP*TZO!*d$sQNP3d2)qYVi6$Ul5{ znhX3si9w^i8AQG?K5AX`JCauGpl*~u@>lDs_X|2Gc=2DSh+j}s>yG6tKaQZ$K5~#i zx=*I}b(Rtxr27^Y;mqBgRjyt4@GB(rO*{VYnl$)>Z$B;e)?g zC*3CFb58PKF4EVh1rcwWOw>QRcgyf@lJ(0Y<2L{|w0}v`M}&LmF_Z487mnfgOZq;^ zFIm$6M*H#IT8}J$^O^tlAOssl@Y+vVUO}t=rAFExllBAB{?80*oh8kAqN{qxrJS^V#`q;HEQ z{r7{Jncuq>`h_SJH$QL{mlLkqnw-R=PcpXTIj#9`2V_v zf3@X%{hcL0>d{`B!(+E+rn4;e9$1>~e`aw{JF~sr5!y=SnU7JLPHKEB)nf+>}{6#t+9k}=rL$%sq9>dqu#vUoazg-fUXcN!YT3WKv_+5 zOHC`zh>N4?Yd5Zi7hNa9p1OvH@>-n3Rxe^GSBP@kgxUvQyC;SM#1V&7fH;b726Zqp zzEk=mH~#6YsyiDich*%ERJOK>Yt}-7Skcnb)WUr7m*mz~w$^T_Y-q3X*X`b3z6Wx(cG9=EDb=T1@t@m z{iFJ}STlv|Giz#^>N@>fI|3clK4D5>xRoiSjNAZ9!HS(*YpSbjs_*oRbc=X*7MYl+ zc*k(gn3QBOue0hJ+nS)uq6EL8Hehd{&CD*UBf$jf4q7Vr(6{(%S*5O#Fk&taYz`mn3hF zG}JbPE2}7Pb8pY4x=@{A#IMk?I)BxsHJjaQJzEP3s@pcM*%+=(C2K^RN^E^9BMf&e z?Yz6Zb;rsjO9Dkpb5>+7^<*!}C|FwH%gl6V7kQQzwaxx!ak8mhv@oG$@i zeetKz=JGpH4l|ON_Pg(_&8lx++ZmqKKIn!qDT?u}YFqu^sC;H5%59CSn(K0F{UOR{ z5LutkW0b_bMqFd5p&8*@d1paqm9PA+PBSBO4g`EFKLy)e>&l9tW;EMc{>G{$XAC-b z!oOukSnu8GTjAfRtKL<*gjBWD)$By9T1EpGk2;=)xSC36>J5Zk0b5q!t8Z$q+q<`E zcVJy54*R3=G&v|+Q52XT2zSJ$(^O>fyQA2g85!9lii3o*II5r@bzUz9OSDS=YK^WB_66Wbi(${|~h`ckuD0c~ARxEg-JWX=-i@ zwAM60DRMk{wFd4!t%4rO*eTT8344H{K2h-uvS z0h`OCuHGrUa&BA;EmNW_$11=1s?xF;uMt-a$1cW3A&%Dri*Vd-ON86_D!vhuk}8z@ zc#0LH62D`_1&-rm47dLE8@Gy#FY&K0-)aol{5mNo8Ti;3G=*Qj9R6WPRA;S;uH1X1 zNi}X?4r|;BbRMmlo-5KBrN# zT78A)+197=9@eIoe;O|4rZ-*&EbU=4uul_s)ag~64E4_uaS}ErE>Wgeaq( zh54c;5*KsFj(~ZL6t@>KMiQ=hh4(0J zU!0tbTEC`h&Fc6X3cYWO+qLN3;5wem3XDaHTqrkdq(()o_;iX}3uHtwV2f5W^>QFr zIy}5^aBE#-4Q3MUf{FRF$)Ut&)a4la^YStUb)kVObk;HjWsfY)aq5MnQ?;6tjR9s& zHSSMGFTo4csdZ7^+hD$oRj|*r_JdNBByzOf+$9x2on~8Dzocu5tfNv(grw?2pu8<( z`!4;$2VG@kW-dDXk!gcj-)MPfbIlSo4z2Etz}BilcLDmhogF&^Z4E`E7h`b$6pAQ2 zo5!ZV^0Vp8{QZW84?QN`oS!XiBVYMbe1=ecpe%vrrc#uP4H-FsDy6zqFO6f3Fcw;s z@>10S{9beDzavoG0yL&VAQiOwSB=fvSU-i{La5JS3T;WI)R^1Qps%!+H(45p&Q^Ez z3XFG+HO{@o9sY*0y$waJ?iJa7T!US+p>y;$qF5!$Un#G;$#7!~B6Rbv-PXya)5buX z!8mmBvIUajgK4o;3kzFV@>%Jp+9!ip6@c zexXj}TY54K#l09Js$07C%X%^*+}VSECqt?Dg(tZuRD+9rnmPkpn>wHsx~aJmilPHG z9d)hzIMA3J$QW6mhqh3v2#@AA^a3Cw6nz&i1I*atWmvBU8mpe+3{RFj%SBU>R8{L< zeOLY6>tpE|9-wT43l9v%4wUSKcwzac^bAw}n?}(P1JL+jJ5-o>^$a^9|3E{2*y`3! z&BwVHZm{BzIQ|#As@YK&|(S6CB zQ4Yt^LK?|0qTXF`(E;2RO80Hx$Mg;$ep1v%+UOt)VRnFeW1)N3s`vDSjGtTjIt6}A zdIw%$OFhgO{37V_?U(%iMEV>6zi7tb7eSA2vy?x3RWj+AE59$|=N*Ny)I+vZV_ma`ejpP?mkMAbQPeq~v{GypydVFgw@oNs}r-Bl} zuslb*@4Q*^^U6qdfFJX39nvemMaU;XwrUWy5pBe1dUq?}&kK8cqPuR;dw^5!fZkom z@Ih?CxbNJH-+XSzQ;!GMV>X{v|3;{;4;+EQo!5v&)H~3H{bJo+We)UMT-|p*75evT zaYU~oQ-9x9J38vDjiBapXhn_735RU{^Mm8{||fL0v}~{=KanjB*R5TjfzTTM1)ZR z69^DorcOwJph3|@q!i2yNyr3pBNs@J)&W@?D{X_KrIy{s%5D=2Ep4&IF0IkBEp6HT z@KVd}y4z^^cGH!1QPCz=<@^87dCugWcPP>p5wl6 z9$r<;dc?i3U;C%g{;D=C%jkX>b^0{w6uu`)Blj)X7rbx8zIOMLys-P6yvTh8pwsqk z*;=?Sv^9O-wUmLnJ`yAUXORCnCI8FrMUa09{M2YYH?GL+Zl4c-Rm(ix?Kc5$g8!t| z^XG-@-B+XYgsX>yWM>=>TYy*`?BKh@Uk(GUoba3 zKW(lqzb+U3;3V6V%eilstxf*N>~Y^R*}liUE>rupsK0Bife89Y3y$D)OZ6#Jhhg-Y zhjDn`({0cFw~QUM<9DLA$Do#Fcz(`)ww3;|tewVYFkZ*mOfX|1_DIj@CSNsdrya+Q z@O$+PeV714@Z&SeSQSae}3MVA)jd;qaV#@b>3Ut zh2Z_!yw$L~p!@!LTedDx_PEvki+Qxk(AE+ARwHdY(mo7ZH2N=wj0n;(KfHFA6-FH6 zO?}WGqv7?zpW`FSR_V*h^o7jsJXK!WaO`y_X^~B7xKJ|{RZ@E0$l*Q5Og}|G|&t7P0a4``vL`XJ%O~j zuJ#f8EMLUW{F07OlLFnGN5hl(Nu;%t{{MpTPZyr-5_*1j@M6+#p$oc$SCCEf_F*h&pUpk&HotHq=HQxSxyVyBaD?he8}!;?o#eHM2IVUyv>V>F$@4!6Khl*ihC z!Ht$@T^j1gQ&=$f2tM_LKdPsGw{gM@k89*;EV9q1J$TUOBjMMIzWh8n_dkkj!Q1c+ z$$HOYgFdS~HosSWNKd&w%0(V!_>FO)bV6a;T#U`Reb^_`lWq-Ac3-Az#)JxMz{ed# z;cME8JTT9Sd>+UBINEW14##~s{u0MF9QWebien3o$h--#-I2({V_5@b*2s0`$j1kt zT^{5``KbF296O93Dtlj=37=#aA|3k>+X%j$ci7i-c3!TTT&RbbHKALNx$FZ>AA+sN z#9t=ylLcYxMn3qnlJ&D$$R|3EqdXCJk3Z;+`1Sfp`@6+h2fj z`RjqX;jdeBABH~ED^}J|FeltAYq0{yn4bn&SogWpv9`gwZ`p|M_O1DI!&_Z*DaXw7 ziN>{LWufh`JjuuJ`0_Z%Z__@e9?;g9uVT{$GXfaDWn3DCdKiUy3+>W^BY3VAJ3gbH z?nfO}qmEc#Vbl}rD!GpF_2kqs_^cj}BW_*4TF2OQ!HfdvC}ZJ7=r|EN!Ztq0TFbOc zqPE~1FwxWFcePvlEGy#oZo^!Vg|R-8Ki3m+&BZ66`>Zi~thd{^S%=!~ybt!*o)J4X zIom^zO{izIN%h1ebA!V+?R`b%f%=ab{~YzH^C;*KqEA&%9Nv5qTStR)WZc-dEkF6# z$9+`)9An=$*IeeS#=bGOv2Ut<>`O4dp}i80Z;+R0d^=O%e(v4+H0=MIj(vbfU_amt z`0C7rzflkNO?K~~KUD6+irYyYTj0->{rbJMPpI*)cZPL<{y}+;K7#3IoNGeT^v&Lf z{SWXxM+er69hc(3eSi+kwd==VobQ9bL5>e^Ar1MTzGCSc`S8t(duM8I1g_1$e^);I zxa+mjn{QSp(2hX?*-tf8FAML`PIU$>@zy#|6>%l3z^V*^7B30R4xE8=IHdXd3 zvF3J`-G#E_)1qkk>}BsoTq2#k@WE=z`^vqpXkD^zm^$=W??2yn#?_slpiJILgXxD| zU&u3&tX}xvjOvnRMRmdbp{z6aSluC8{_mh|dcbc#*NeRc6ff{f8?R~G-YY(Q{o@v* zs{i~X^^Z@-qV;C4f869MO{7!6R{wuOnch|XL))9Z4K9aY((z~qSAuc{#tqinaNFPr z@=YYm!g>0trv0-m-d6f(yT`r0tc#!r_Q-x7b5Vy2eq^0C4u=uZ^z<7%U`ucA=lK4s z^@Wj72OhjJP4^%4^8-ocArAY|Dt)LIeQX;!)aMfDG7d*sn)Uhs_MdXf!7a=6YVCGW z`(*=G8`HX5R-|`tTs@+j>y!8H zF2Xps2;<#CjC%_({uOq=TVK$P@lg3DPS~DfhHqnCiN?2oJ5$Pa0DYuxag1+vd$8Nq zV=td6WnGy5ny6hn;p^S>t>M~8-#?vs(pjITF9qO-{A}fge;qHc=cO~VMxp&i;~0bE zTpZfgBhMRKGi~g_8XWWH^V5J2!XLBOvA0z+`bT)US`7#{I zbb(`>vu=Obd%`g%cHB>MjQeTbNp-mNqte0c(7~Nthl|c$hj*%6-KlJ3IO8wZO!*1s zg3hsLKmNXbr_~J~&k5&b&QEV+pPaX_k3h08#Z>Z<`Pd_IpOS}9v7sgCI2kUb;+!J`{ zFU|xXVjSn-EeYz3Wn_OeI_bV}xuW%$t7Yjv@qY5T4`U+M;%YphEK~ND6;^lOl>F{$ z>?hVYr2yy#&IhIe13*3Jw5`qP?#(Pz&no)v#q$K*|L@J5ubx%Bl2)LeRlIliQjA@< zVhpbsKZjm+os#&qUSD#@+U$n%Tdb<3;o8z!g(w*@w;O>1+gep6GO)!N!n5nf+cZdGooZYgi8t+Hx1gYKBG(-QvFh-sMTh>XaZ~R@;&$sb+jlEmM z&$neA!EfX5`Vf8}#6el~v%6P*?*VqoZ=KJW=sOS}MIO75*G?R3LmNU(<&D)1&3D{h z{9&sob%Rx`+t^f9UelDSu?DO7zwy_<&%Ey+e{pTo6JzgpSAFr76(^}mFw>Qhcb-pSoq=gtK&Bf5WV&=9o?{iC!i@*=zbtr2a5s?Yw*#3z0%ZDDAk#Mh znZ8D&zZ6LRLSP**8%Ta1ko;aC-jym&2R4Cz7YiHmp9GS>7f62kogzQ|QIY?sM*kro z`S$|J-w7l?{b-S&H&5_hL~%2a@@s(PUkN1tQXu(@faJ$@qvD^Z(Vq<@|0E#!#{tPd z7D#>%a5Uu8ZxZFx{~7t|mxugEf#g37B>y2G`44LJ?*)>-6G;A@K=RYC4f(eLM}fZv zNcj~&@|Ob1PybHj4*w>*`S$|J-=)#N3rPNUAo(`~$=?hle+>}hdNCIulwS-a|2!c1vw`II0m(lJNPe$I zKm8f;d^-?xK=C$>$`7`x573myn>>_P1f;wGkm>S(=xW8e8vWyeOoxkhl`b8~bo7_a zbfwznQ2W0xGK-G>wrXL4v z0FDJx-!VYyOaI}-mjw?A?g66x{GCA5tDk<)kdJ>C5b6Effy`$!ka{)%nGgMoGoK0| z^$cqCPX!{&J;a4L?-QB`+8;Pq>z^&?1yZkc;C$dw4C*W&{X+44Cy?jq7o6v_fX%>n zF_@D+4J7?Cko?Dh`M?7}rrRa-av=Ga0?D5TBtQLwlYcCb{HI52dKZv<9fA#VzDVdv zLib{jrQBnJM}U;OUugOXC*1(t04xTweu#IVeu{)9g7yb6s8U|GpclyW=|Jjt6oVq; z4*_|;6Ug&;@T>gTPxonDbr8sW_5)dOyM*o#x)n%$Yk;ipr9kSPEA(j?A=94(GX0A{@*e@J_LTE` z1?i`o=Yv2cN6z!TX`Vj?V~A3#i&%=ystSgboY6 zROlk1vxW8vJyvLs(1+Yw?g7D_K;~N`bfM5ET>AVWAoakeTMG9I?h&LvLB`XsB9VT* zT7dKeNqpCm`V>3?WciN@eOTz7f}7<$WT|?n5v&kgDOf6)2c$hu67(S7MOgCngT4uP z40tQ>2=EqQ4{#~)AaFHsKd>0s1zZB`1j6o=-|U-#4WO3+gTUoL=C=x%4TLVrFZNwP zFX#|(9B>72Eb!Anm_Pl7V^p!|mtCcYe&Z>p5KFsUAZg+P(3DG>coFESLKB0a#{;2{ z^3z-bn)#6?qE3{bX3|-pad6mG@x&l#>H}1MlrI2%4d`mnMC3c>Dqt08BIGFdhxKq* znh6?h@flRFwFdD-@*e|2kMYDQppObogxoP~U&a$BgYFTU7zTY%XyVnN4+u@H0KHde z;v~>)C(0u-zEfx-bXI=98NVAe<9A9taU$prp^1!d7n*n_=xst18NXR*q8D_l&_u@L zQ2G#2Z)1YMLgY`xJuu}5ob*c2m7q%{o`~m!W0nd{gp4soLKDY>Mp`A0$oP3e6LIOK z{C*=&*;5#Fw!{;0>7?w3@l!#s1wBdPiMYfW;}x1%0eYO!L>$VVm_7?MP3;<^Uk%s=hRX>8Rsvlhs^y2(&zu)7Dv_qc10W^_z>jlC_F{*2XkLq(yo|Ea`eQxKuneHQ5N3$~B zU03YA0{*0xck10!I;XhZC#Lk_|AA`{!vFNP>)PRY`bh3ksBc+68i`D4^b#(^JW2n{ z<8V>T@F3=4(q5r+h4u+ee}at97W!urpC|OyC;{UGLVrW}i-f*c;?4IFBP2bZm#Xl# z(0xLe3w=uHmqcE_(61pc=EsYAhEkF55qh!kj}`hM@?&l=-}6+HPJ_Ox#^J->6H6~- zJQ~M*UlYmH@plUUPb7U%=&ML$&NSc8@S;xn*OvINIOuwze<$(9Lcb;SHA25HbcWEQ z<@=X6s4uJ|OXz$ z7wx1s2wg4d?-BYgiEkJBdZBj+{e;MWROrP*|E)!tc(J}C6pBz+il!f*!;@^6#$Bd9NEbH8wol=nrk-^)fY9^dN; z{|+f{Q1lH;d0VA?-BR8LiT6qQ%=j~1%E$YU3`a!2mm!B?qLeQHWH^R{`UTOh44=h8 z`jF5YB>iTok005bfM`K7}@wdnV4mJjLkg#WP6^Q3;7nSWZRwO07Ivb-ZA){lj5 z68fmne`5I%Z^l1fr;x@AB`Tx|{e7W-D)e!ozbW)#p}&v%h6>TZ{dnxO(?9fD2Y-iy zKaBdZr_XZGub@2k_=%4Azec1zeuN{Qe%tNwbX}3au|}K&J>~v3g~uxM;Ah#!)>g_) z>RQ7*7#fmsP~s4-aR((1!8j<9K6Hnr;}DF463dl)(_xgFjOOD~FY7mLv>#Kq#Q#dXcIbC(T~D%$MeOX*B4Djt3pQ!+*+D#n_c4Ym5qaek!QQd3z* zf4qtN%bmBXh@qQbF-E;5mGHskpz|d4R5O)bMoU6(gTt|3@+oJA+8J(sOLM)r46J|; zTsYEIPJxYIzo<7sNiwwmz~PP^_?}tGtZk|)sjRC=seH7M8=F(Rk&8ChP>h2T%ZDp* z2*#+yAw1nhRn&YXCHB6tVCHPNXMnSdifZ~VhL^dv`Hj^TRh0`H>uce2k0Zl|%7QkS zMSb2aB_+*`)pb?q$J(EqU6S^OG)!?A3hHOyQc@Ud4vDa+Jz`)gD=Vq;#>*aFi_uBh zaLh8snVHHoxv#NkRogm>(X&ZLiUd(ZcSUES z7@v9VC7wM|jL*E=5zn3|#%Es6iDyp~<1?@4Bw&{pckD4f^P-Raq^_AnF+TG~Q9OI1 z7@v7*D4so0jL-J&QhbUekv{V}Q+$d(=rGeD7@C)_JaGA{!f^cq3A<(F85$U|{l>tEg#@mDOmO;-G3O`C56WZNl7*rC z`S>KE!4Ak{wRj-Zl+i9b#(zu#e?Gi_&461&DjbA7UZ)m8o>Zm8Xo$*7;?HNLaLa86 z>u@3=Hi=PrN&NYQg;y^7tz%N&AoRm6fw*8U@o?#&FB{5=i@#&whWU-8=L+TanP$tqehJV zuOtn{js1owKhqnfmoXkjXzcM?(A>|>q{p)=+^lU$Eo;YA2sTs<4T4 zdW6rKRQN0WCvEiSZBM3&2CR^bZ~XY1D~XoYsc$pn zwuFw&6j@G(C%)-9In!;rq*_{k*d=^y{<1ueuAlXa8ULf}Ui?nJY|U0p^I_)cHS6Y7 z++N&J^1t(R)XJkj{&${^^^<4Dng5-q^<=^cU!G9`JyBW`}Fm6|9tfwhFz5UU8e3}44!sgzx9Y- z%c%=OKjiTeuBbr0ho#Qh|3N>j3GqNcPCBpmy;tdoKT7=aYh$nXZI=*=L;R;kZz4GL z8z&3$ev!BII;~$vp}sbh(@_{|T{8A+&Dtd?il*xJ=z~1GPZt-W*XK$60Us23{~;+) zK_08};DW(xAG^?~yd?JzzBXBNo9m8Wi{05}*ndamCAojlFY*pZqHNeT+n`~gdQZi_u2HJZD_B*IIQ)bBX-<2?=N=+IAHRn97dr}O4|%8y-lvVSp{I_rQuwr_ZGyl&)+ z+Dj6D$}2@4*KZ6vMP4i9B^oY8UQ&O`@GGi{jV6yvR=vK-Q8g9b7 zO(+->F@2yI;|l2gtMD6n+3%07Mm&#Z953O>$8omdOj;WJbY$S0_Dp=+?!hk0asLK}2g@IQk*cJjANe$%%}Z(0`opKAYm^s#5+>38Z? zIo~VzJ@|FQeA=-Se#X9cF?=1schKqUE%>$3K6p--(|>E^cFJ=DncwN_0|RemS_4nL zYVA0EYkK!JFMPJ!_uG5AfA_end){vX-7{ay@Ah36cnW^rpPGGVLAPC3`We7`c#uC1 zzHI2*>N5D=Fn*7-o}m19uVegp_yS@%bljU0c<#ceU)MjPEG*9}kYlfdUieTT&3sI` zU%fu?6wl#1iUYmp1qNP$Y|^YN!^3p1ieE0uW&G*O0|P3}g-oM$8!AoF8S!roUkh(u z7Z9Dbe+R9<{ymf&JFoFI_~wMbKoR8H>%snecrtandPGkxkG`Fa&*{P6L(3cFrOmO< z*lr3@_E*rh`rKf$K z`PRUjQvw4`@MZnj$up%RvHY6=U!=WX{@P`_&fAJ}ch4`)T;$v35j0sr8P`*J$)5zIXRCUC=Ab@Zxs|4)je6CK`F3@#4EK z%kF=WdRp)mNxmTZy4_c%lg}sq%X2+}fo9Y{(+uhm>p$1&o6?L+kP-E_{V;qSMly^X zU02lMAsmsYd|ltVjIn-wy%zi{LKlyRGU9zJI`cH;j6i;HYtOvM*2p|l22*az>Gz_%ys zECip!F@9I!Yf_zuPsy0`;*T|S?cn+Q@yUa|zJu_`X=upE`mnk?ZO{1FH1_Kg|NFDD zw9aNcH{&Svi@2YK&(^Ktvvqzyc?SKzy1O4nIb->U@bNl7@Kkud_0+?thnRF*56=s3 z-KO%{xwR1aa8A>Gq4}kB`0io9inaR`<%{;M7WA#4qi?mt?!)L;5wu|BBgZ z`(%x;2Q0v%e$Ic6dJAqfK3R4B!Pen7Ha1-_V?KPcYJb)LEq-97Ey8FEwnwt^CTfqk z@=g@r`O=OouQOfnzQS&{r5!t5_XpAc>9@%_1}GUH#Luj>k$v1yvd~^J z`Z(%e>4Sd6F{S$J_Bv1Di#C|T7p-Fs_`rU+gRZ&x#uu#_-<9v}ZS&}lTlwO4_}PXn zp+9S1-sNZuJzu!ge1Upa^E>Bu&J`Rt% z{4<`u#2N^_|Gm$jew=+da=TgA&pfC92d6K=z9FOLztICfv-ER`XUH9YbXz-cf2Lyv ze23vWr~}{kV4bAL%U7{J|7~>bsOIVCxt8R4+RCe3M_pxMoo4MQ0G=>wHR$kFtozzB ztsVDd7QHqf>%Nz;2K)-ful9>Ce}HLRTl83SkNe`+w!C|$6xVBmYrn@S^U}e!AN0n% z0hjy$_N)7swFB2}9rM*1De^pZVZCVmu`a~*PH!6Z#To~BQNJ`G*LQXuzXBa+L&qCM z$48WYm&WM#$BEVsu1`bnIo792$BW~1)N7s8zBRjE{mg?;yRFx)#~yx^I&zNwlK6Z) zbFcfkcdABUEt!EeWhT~^@I!p}$nN*xm-9l;U|YW_J}OU7!8P4g39jR?u6`2rg?mRG zFQJW1-MoT&FxP)(?o{o_@*s}&6s;r3W*yD3)zOzt9f9ApA@gA!UFobN=*4Ti=o%h6 zu^n|CEk@ajP{u_l>q4xX7j&cSYRzmPQv6Of6l}71KjKCz&OKehO9GbuIJvq(}ntV!*$UqaCV0-V9=Py)eO*ht>_k5zqW?s% zmx4&^vEF_D%vDx5uCI0kM(O(o^!3*_MXirjy?eG;&$(Rr-z87b!#?`X8&^R$=6NI9 z#P!&gsO=}p2lqh_=r$`rIp)Jo@^MY&itz(z*P}Cq9@WTyB=Yy31Dh3{ST2l@7|(d$ zM6cN}2CI7@^vO-V$z%G8Rk;26&M$uJ4eRjHH+uK|;f-g$u>E;gdgmLi;CpWj^6Kjy z(9!ymv0cfPYZ{TOd4GE$_6R}S;n)#!MQ)!D9Y%;AUig4D*M#>e0G_*_t~*cu0w~LuRT5MFIl<|BHOukeiJs$ zy58!tW>;ff?0YZ&g$XX#Yj{Vgbd=Ti`~=siZ&qYr?j5-2L7w+Kv*l6rmn`_Pe4=F( z_C0Z3^xDPGJpAZv_y;v@K%I~L=iM)GYYMuBR^mDz9B=x|4y*evtoILMdxamw_WrOrx$PC2x+)yn_QpDF4|0v|ah(mD#e5MM zNL&Z8*Q_l62eldaBRc#3hc->x|7`Y>sQ;a1zDxdi%36^*v5Qi8{e043BGVTp2XbIX&;w77eDZ{ zZCrX5a*b_pEHm`rSaeasvFL;JiET;BY#)!mkGbF8CeJ|kxUsnZo)NS$;BkJ<$78%t>DT;{k46{ng`j<1>va_Cmirda=li9olPeSW zrS7?4czX%_X`QQk&|h}!eko3W*@^ZH!MvPh)^PTH1uQ&kNu*Z*wrA=XMiNuU95;EOD> zg=Z>U+tSuqKIb`O{}$@g_wgFy7kJ%BlZLdEF#^}ot{rFY zO?!^l++6<~{j;#2VEe?t0QQ{R;ql}6{pJT}yF9=AjT^tC<<)C;Q#LQ|w{$&jJ(uMg z+uwDs_1uL(%40eEuzut3uAk_5Q&&7kyXq=Pd+s!NShu{_p?L2t?LRWoI-u{%1O^WM z#QmJU*2TS<(UIF}Yj;DxPNaS8Cuz?yO(*a%>;b{wERR|DyeiL1IiGcwrz@V-69=#D zyUMB8(*7>wVRSP{nUu$Ml|r{Q`yJc|D8+q%GTbX!4SPI|F|?>`+*dn4w`rd>!nH4w znZIxAg+2c6v=#j$=i*-G{n#_>{D?nUIr|3AJc+p}s}FOXYXq;gBDYV(@mtB0HuGED zD=^Ok`Ha>X{~N^9C;z#SW9p3U;X%2$H>A0bWnqs`)QFXO&~zUTjRf$O>V?p^>pD}=oj!0zV5{_W#(jyn=&#ksRbKIz-fHmPI$uG-`e1p)<+>3PP6%f5xhr#!$lfI@%#?OAB%cHNQ zd7sHc9dXZr^{Ae^C(sjVvKsJ@A)@}nA5&#{sG+$$99mG ztJ-SRd&|PAU~z42OLJ&-O{JY<{l>QPmT+yoaHFp%-}O3O8ip#%`aQlu(mvctSr&b` zV?O)H1FlO|FxU=?E{^u&z`7zXjFmIadWvH_ixYoDS;_u8!M6pCh|><*oM)Z!H~OnW zBj-(_-w`zF4RYTABcrUexgh!HGxkQDR8hGm)Kb%&&o7_h98g^UTWi;@sjqF^u)$i0 z@1ASCm5q&zHleV7W8Jj!P*Zbdg*U&kG1TV8x6`$ib6Z0Qk+Ns~8fcWOl z;`?$J;9y`p125tjOc`|V9i(qVoHGyhH6DrLm|60K zI0ery?iZ-Ie7X2D{BCP&SY)|>#9!j9$BaM)IR7__V|mRYI^6IkJ zHCBZxHj#UyK9@5GE`Qe5H&k!hRKKBYX{a$&%h*q;*b@A@Rs9OV%gCZd)E5Yeb9vT1 z6aPtFr>Ltn)z$#jV>?xRpW0AYzA?P9rOnz@8E&p@Yg}VB)o)zi(zI@^)wZr_Q^V>t zA*-Q&O?6XwTfMcZp?c${P~|#QUo^O^F{%w&ujYGAMw)#xSl2H?p<_6BUWYNNt@%4q z8lJbO8P47sOv5i}m>)wjxjOcP|2Jc{!A#69DB3-75{)9Z2xG&xQ)m5ixuh`oVfKqD zI_umA6I9n#H(O1W&BZlsb&cV5tv8TZDwcAC#ppY$o5N}_RdF}TFg|!@g_x6y{*+jo zB1*&t6|q7rQxVI=UT=s_3rd~^pFBzp>q8s#U=jaE{rcIwVBze8%kO?~*OzX2`bYo4 z;W8S`H>Yj(OY<+KnJ#^dHGgFu{h`XquKC{cPJ7UK-*7=W>UiIY#i@(dL?z!DeV zF+}|7A>x-y=aKm!PX8K-KZ?vz1#$Uv9|H#Y-Y_nn8?>g1?~?dQnk_E<(IL|J4H2I% z6J9d;K8a7Jf1$*)eG-*_yHp&WU+{aJIQ@HY(WXL@^gXz6V(=zO--`=1hSN9_<^Q4# zn0-m&xql7Afh6{8{La!pdm{UsdZoI+nq~oiL_h0Zd{Y8f=KUJ|M}hc0f7%hC3)l&~ z5SRyC4Zd8B{%qj+I6oEmYq;z6Xd&o=Z17Y1Qr2d48`=L3-r$ez79R|G_J}9 zQm?5RS9yWd!vib_rURM(X?)=Se?j*F(?A~svfM|2EO!qOf5nG1`gZ|YpJRbc&&YoQ zeHsSNbSG)(z!O0H758fNEB~H2e+=~HI8T3}Kz|Rg0_o`=me?-X0HpjNkn&4`lwS;_ z{6gSmkdq5!I{K3(KmE><|0Iz7CxGOC8A$%)8vRFsOxFWsy8S?=+XG~}-9V<>3A_~P zB0%!j2o?+afJ{FQ$n;}?Ot1X80^dcZOy953-v=cBap1p!|0t0Bhk?xR5D;Cicn>fW z^ll*ew*$!^0g}HJNd5*O`D--#R|3gj3?zR5Nd7z^`MFOOx_t3CAmxt*lAo1B{?kyF z{3n6r?*o$mghqcSko|8L@ICOi1K$T$0RO~(pm9|(@LilQ1dafI9`Hw?bAjK1JnmNo z^dE%sl(!#9d7VJY+YF?N?_NO?U_j_JAYQwDGkkmoyryO7Urjs6ax z(i5ok)VL}Dq+IUrM7cg7({q2Py-4rX=--2Jl=dzfhXnMW#=QCsoIeiyTi_ue`3?fV z3%&yy{X2o|cM)JEFbM1d76NfIYg#t&cR()?MJ*n$(VqpReUAbD2JyT=Jq0{{KKO9H z4|pE17s&WSK=L0Dx(i7DPT*Kzhn(mBMyQ(N295p-;C~=~D)1bn_X0UjWdUEq`IAVD zA{X~*^uG)w|6w5cxqlJ)_X95mhJjd8O{)MtigZDZe(qO{X4X6RE&3kL z=V|ok0`aHzF(SVY1oA0^E)am#_X9bP?gBEu4&YP3ZNS%I#}SRIHUkerUaLlb4Ulq* zfwcD`jjP51{}bn5#>$=X2Y}3HJ5bHnK+c==Crdo=nxfy{5W##Ot3%x9-Y|7IZN27xTkN{y?y zFB9bMA=ZF@jL<~Te(v|c{x%L62KM1559y;o(tCt%7di-Jzga4Ha-?Pb3qO0-1j)5clS%%@aCT=#!Z` z-2uUEf-8aK$1@a#ULeOqI+h}yz>N{=eNeDdkl)fXzlb2-XX48(YX2qTIH9@!66=|` z9rb)lXd-BTDUfms1^d%A&3%@rSFg~=h2AAdzhR88k@%H@g+Q3YfAWW!u zmq!0iAmz0KUx8kmfwb3FjjI}fXK=m-2vaK#YxGwDnQl3758{i3o(KFI=##jPSOe@8 zd{OWSkm-5^56bzyK%$o0%FAWXHG*CRmxcHnWyivTNuVIbwN1TtMIkm-tmFv;RV zjsAH+rppF0T^5k(JV3Rc0y5oc7upNyPHFV_0h#V)Ak*~#$f#3h8`;;{>q= zRCb2BUfUy(?e#JcCQ^(t5dCN))h-=C5Axj%C}`H_H$fAB3EB(%7HHx> zf@VE`8#M9%fVP0&1x@*gU^ehRpci;0a4eAg9$-HZps*Ks z4d^4ltAL%r$w1VV4oEq>fwd?vaV^SAd8CQ! zK<^ZqI2$zcVLb86pd-L9fF^z(H0^{m@d41SLKA0!ZWfxz^y7i3FXgZBv!KTbO}qzm zme9m$pvMYLWO~*UQ28&s4m9RS>ksf>2wSsG0{;Q{d*IK6hV7~I-w?cCutl&I_+Q{h z-&K5{7Az2)D>xH~wir+R0`i>(Jb`qmTkD^JmjZu^cp_wSxorJCXd-EtqXnJD6WMR} z15tnDi8DaM<`qpux-ol%CgyM=H zsP-*`eW-o!SU*#7o-~p5Gf8M7>&GiJk@Yi9Xks?#ETM_dfCeaeM6`<fq_ zE$|f1qutK!1=a%(0-J&Rfla_JAZ$qW%cp_6K(7aO02_g9j}5>GumOmJc5`kJ_$6Q- za3?Su_z=(sd>H5j{uoHTcL2u%9|U@Uj{pIDkwWqP5lFr_f#iDwNWRlRfb|xTdfWkw z084?bz!G2s@OEGrcpESXTnbzcycLMwYJbEc;1bXQU@t$QJ-*Uf3p z%IFd76pRQ41p|U!K})a~iB)>RPQi#^P%t3q6|@9)G$gbe|f1dNlU9yYS?NneOf5 zI>ve2FOTaTm+9VfY1gG5_wGwOFU@rCymS|SpSY~=vP}1}%U;C)BbOb$%;P>X{^)p* zJMxKbpU8CgkAHW3ru*m>$F6|PD^BBo?-eKT|M(Rzq<{ru)Fv z2d~a_cTCBeC#ECz`p)Z7GuiKEL(drpX5jpc6EocI-nl2{Vwbpe)oRz#3xbPH}1O8KtMM=N04Hx)zwzg@6di~3v1@dSmqvgF|8bmW_?*Nam-xS$ zqWQ;OtK+BQJkxuGeoOed2w^Cf_AHoT&4LR75bRalhbtkI-%!D z{=JuI{?n{)l;0=iy;1bd7J97Ec|z9-9T57v(jG-ZzasQfp|28poX{(U_6q$7^T$|X z{Hxtf8anq}srBo~0zCrPj*~PUWIW32mGbfV=qH%o*8jgm`W%#3;&&q-JKfB9*s~cQs=>$j_k_Pw=uzk=4B0|oBJ>-y zZ}3kR`qvVlC-i0@;}-~huF$s&&GU>e6Z-oiuSRIU=-VdrdZ9lj^hlvQgf5lzUlICh zp}#Kly+VIe=$}dY146$o^pAx86zk_a?AIv$?>nfgbI-$mjY5Bt@feF%3cblg+8d<{ zg$_#mB|=vS-9&phANRzB{=q2H-YC7C@fTQDtHgihBGTR{?Pok}G$Qfu(4Jt!?Lwad z|Ae%Nb#S6?Klr@Bet|t!x>wViQTL?#GBo{50O_ehha`R*bYR$zgYgYQ9}{`UN2+Km zgme>J*w2s`BnZ1PAWs330=6I@tO!A2Q^uB33)>hGAkRXvko{W|&F+ zM_KM&68{y{75Qn03=gC1%#Sxy7}hEI5zB*mX3+avMl2uXGE78YCO`MBXXt<~r1uDY z675Sm8|7kn8wY9b)6Vb>)(2#m@pl@=KIrS_ILm*r_3bw}jn*=_FXIOt}5vxz+r?EYdhnWvvllmMl^8QxY16HGA4-u9Z z<=H9eC$fHB5o^4(&oyXA=GPCI44f-i-UgAk2=gN2P1rB(Z{8a?BKmKeqB;0HgZ#XY z%CKG9AO1R2xJC4hi2S>x{l`iC-%I(#V*?_}`=O&5xO ze2&fX_euFz34KcFCxz}3n$O$G-z)TeLLV2}B{b`a;R)I+_&q}ZO6VTp-zM~aq05Bs z6#6K}QrjUL*O99iio2B=i^=pWedx3GPno5ZamH%F97#Vt-f2WoT#r0R61c z^V#1(za;b_+9T)A#{zTe_7}+ ziGFuUeoqS>7J8bd#RPS-l}e+K>A9zVqq|2s$ev8Z1=|1TW* zA9B!NbI|)7wA+#2MUMJdkCwCN_YJHI?DQc=eQ^J9d;DLzV`;|OSKvR{yUENf5!M=Pe0XRkGDDaBMyGd-7)E>JL3PrQJ!3fK2JFK7dhnJ z?ufs`k)F>1?fReR$nOmYeXFDY{k=o}Ita7NyTVbwX%7DN4*GnD{$F&&a~*7#Zvat!9(dIW7SK{NRXv?+Lg=(wI3)HFPJG?qHO1*h@GL4uj z#wPfwS5wZ6GWB_s-WOLmmMkdEHC2!MBxdqi#w-&TH>-((4aD(-d@-+oRWo_D1 zjV0wN_l!^0KXfV>!IB1hbAyWQHu&z#2<2J+qgb<_(-tdyeZG{wysZ7fiZHcttACTOGIGPtUDIhHRl;>HmR9249Oj_2dNn|%Q%r#SGCl)G^r`I1cL>( zkSs{8TvzEA#6r3K}az&6T%QZp_JDt)dq<-BDM) zzNIo}RxLhpt8J+*2sMPttDD<$W>z)UZ(La4nBUM)Syz!izpZsq$?CF_>XOyX3!6;i zmz7q{5(O|(s)m+qbLN&|fgxWLm({*V+Tp~wQpJd>b2!%^$pG3eS1wR+ln3jmZaoOVuz^{#r7id z)~cmZSyT01tjdhD(ildNgGy%GjoSyx_JQC3%9cS}o6O<5h9h^(q`dTz+) zIs{9fP*Y_EW`UN*x@Do7mZ&(#;$LNqK45Z_lu?OFj)`>4FfnnlQ%p=03|~)5TwjZ1 zl`p>_T~S|$b7tF=YV4gH!axa)FkqIWM3qdmu(7_@wk$XLKrlqqRW>!1(q#n1sCuZZ zZD?*QD-V^2c}msd0-mxDyfgs2;cJ8B1q;$7ib{0BqF9I5NAt`&x1?l4!<`!{QX8P@ zo*2keFGaN|RqEQNsuFY=3T|t-^VV|xgC$Zw?S)O%%h|`5s1(C1SV?)lN|1VHvKL6D zC=&hdymfWzs<6*#8=FI^n3Hryl}h&|ZHfA#0@nqh3XDRjB*qQ}8_QENC(B-^8gBhB zNts8iLaBL^=#x5;V{B5&l$6+&UodlaSxX%@D66RE^ni}sHovjDqDpmA-On*36~KW( zNqyceB_(Qiw1SeVg)3Ooc1gO=4O1LupZeLilyKgN3Ofr6h0!IsSi2LWNn7pe`kLx; zFUFy=vXZKVdj?NxnqPnCvf*1xYThJ`!$h&#c#{{>-ZgB=aLKC5vFPlRs&q+{CDGZI zwN9US)$C+5N;V~59DB_g`vavli?R=tSk~{hwD2PW`yF0uU>%x@IY~t_)!-csTkD#$ zGqR`0U1?*Az$CYLS^n)M*b8mA&Uo}TVrP|L@Fov;z&neiQl#9R>BHA=uo`SL2dlKL z;TA&tFrrQmuXR$gCTSa$rlbU*Cfjn}ma1$@X%IHe3sPpuZNeR8N;N~vI=q-^FvOZ$ z!v#y^jN?kmoK4_>MyE(#Y4#GxJip}Ts4>o%p2WG=;0PvuY?PvN#Pw2vz3mp{%%}~m zt2Eab`31O{6n6zu$eT&3w4vCvbIbIqv$TG2-YKb%W~37(D_)X@&a1g|_0rl+RX593 znW=*UQ6Y9N#ARI~6$a-$oyxAZp7oL>G&7t|)-<<_ey4aRx@_+HmQYPKcD1Z1DQRn1 z-cZI1PgY7(&YUtDVZ`1(pXtmHfv2ysH&-H)-7>h| zN}|`SMoLbZj$G8z+_Ws0SG#R(xHwhyUNW24WOWrdQ=-l!t5nDOBqeW>2Eg{YC{^+d zOoMQ3d^Wsm3MrSDhD+416!Q-KKMjKkE}(|yGE>-aSj~(z9A25AgjIL6p|ycmsbMf_ zFI2-|w2eN)k?Xk0GrS}T#x0dFx=fmn=8O_*1;Y08rfA=Iel+D-B|tr%9lx&9vrA*I z-Lp&?I|rPluKK2D>`l6Yvyzc?2^o8NVbwI(Hq6C@QidxEJlVpH4Ny3jqaiOQDi7-% zxhJBZXXfRV#hsgxi*rhZhB+}JKvBAe*)dU|7^PvB8dzt{#r0z1`?kC(F%!?}nzD1s zR+krM7vOeaZEI~=a}Dn14Nl2e@Kk*`S+NcIXONemI}J+3O^y(s9^*b|c_p7Rwqamd zU$zMUTNgEEm(?uf_*vH0fcu=PiiS=QcXQL8*ipI5a%Pv|o{zpIt8bj1?Ty*C=uO$S z2Hr>24f%2247TO#xCi`E+6(tT|CezfhUmrVang;dLdXx<-jK^@DkXtp0~?~I!VjCDB%$srz; zC7ix6%-h!xpg5Il$c>2t#V8FkW1>JYO2dqpC{T6P4Q5v!bS19sYRoUwLR;+63 z8$x)fSXSAJ9o*r&xH?o*maP*4%BD9pH%`xB6|(LoT|vV^fJH{(w>VX~;VS$iy#MIF@IBXpOGjg|g7 zCwuyz$(OhYoIPjypV8U*RXNws$o39~_}}`>?A%<%lrv-2Z2c=ZYD4+PmWEq4uAQzP znoY0AtF_kudFS`-S9yU`paSnsQ>t$6!P#YvPFN0{8S9;$`?4Ap-h>W zFawZxf>T~l8u6I@eKTj7)!6nPO5LjQ~`?{erjDS_aqJl<$-qW#6zU$4kBWos9Ct@vIhQM-t| zvfBEJaHt8d_mmYEFIzI0ai7RDM%IzYNCw%FUOjKAuEoo8)pcv?o%Yx7(62LIp2ifoXV5#jyehKCwCLkaGW|pSYgmo=@EK%#uBn zxcAg}?xA#5>>f&a*h6Uo_f+Dit7>l=y+@eYL#byn_N+$QKI}CGC9&s_-czazd7PGW zUH7i&@56ptl;MGluC$B$Q6}tN9T>py(9sFr#|qrfF}=}8?|bA{=U>IXM~7uE&tACI zamx0*f9A=XFwA`K-i!L*#C}W%?p@aZ7WQPiGQqxH_zpF?uh-!IV0y2o!7}PHm@)>u zgJ~X1%kAdAMAWr2ZCO9@@6s;n@5LTVU7#<&GQWF+**6>eEb-jMBe9qGMVCKtX|=n% z`?}`tQCFuyM&f!JP*rSfN$$Z(?dharwFZT^T^8ROEn1DUp;Iyi= z&t=W(h0UDCo=oKb+7(ORaA8k3e2%I1V9Q5a=6*UK?WXto`Wp5aebu@UpR9ZV^r;`h_DmVsPI@0d z(@tn(lqLLMx}EMx)9v)sy;=S5U|%?t`9LBay*SUkeTZ=vEy0AA}c(UwCw<5iJ zIQxBh9edIlntO25Zh~lk?#~wjI``UQy}yDzvdkV^-qV2xaW0GdhFOk1o7q1tjEh0I z=yO4L``%(Ej`Dcuz6_UHvZt8gx92|-`+Q;fp!Voun&5nEhtY$w?YwG#wDiD$;=>-y zh7WYi9_!@uS%Gf&yX^KY&F>E1?&@Zm!FC)hEAxooTU71IO&J3y!+;lgKAaI4h-A<$ zFXiYlBaHpz;45|@2wfh+0e=A!ulMd_esTLMqdu_+*%x0n$#W9+=th3+*q@Mjg(v5C zM@?aTExhVtvIyc|dKgWYKBdy2<6I>%-^W@zBC>HJKGv0%9(ch84+#~Ow$@N&+ zFWK6EfyXs+w29kd_0O3PIk1meOy_I0E|{>{)$Q4Ab#Fa$X0{J=nkQn-W!wbdE0E($ zw{G*KTLU}?T|02>Fm3!{?JiZjvtB|MV}Ci>V?2y;H+X^A#T9~+W9`G)#vD6i>|{CY zWQD7HR+dh0=FP76^*-kJBaevt*?B?t)_ENJ9&yjdSa>n!CT$BFmbtsz-373@wC?uX z3t@BV32ZKQuj3RtMeVKyb{BNmT|4X!zDi(kfq@nrSU<@N4|eo`L@WANTdceu7*RaBn|*fACs8eyq#<-fe+>6SwE@ zb8mO;v$n<*$1!jp}S#Fio2r?H3PkbOFkpR!NCiaw3C!C;@BgnB;# z_lc2v=UI`hIJV%3(6#iytM5xc54dzc2j-i8J|=NLNBOcst!tZV>f|FK3-91;SX;j~ zY&BQcR@S#PTNUdzHdU3^G+7l@>+9F7*;H=f#`2mqjWy*~xVj-6t_(F=<#o+I-fTHx@KhMXkyhU362%JMLIqn83tDcMZeci|wP?3+~kNTOR-6y4|l7U4G7Y zzje>;Rn#e%>B4lP!%7Sua2XMIi{cP2xaYw2?tUP~={>}Jxc=NLG!eA_-BHSa?6i}D zF9UIHKCK6cwx703&ST9&KeN-O0`G!9YoEqdmYnY&sr(L4>jjdJ{;Sqe4v_Nb@BVt= zE+FO7-#zJKAo&Y{g(iab)6W&!oT&dEAo+I-y&cGO5g^mG0-26}BAKp6qklP&>572l&jXU5e)Y+Z^&RgK z6=RL3@}Xst{}e7>$lnVj|8XGs=~tipM>YBn0oiW*flSv4TnXF>WV-D@rrQS0Ksx%( zC;v)8`okbS9?0}#fK1OpiRt@san1CnfJ{&S`9OakknLss=N|=)sx9sTlAr$bNgvSY z-vgxloj{hW9Z2~RAo*K?D0*=a$ogImB!3Z*{PdSk{#+pWrvl0E)94=ytVBN>qj8l5 z41urLrExzH_s*v60a9)lSO>&4o1*FOp7|63nNI-7eCVGYRZ*O)(Vq=udM}XtSwQmB z(KY$$_a0dnpTKa#{9Xjs01p9g0UiL}3hV-c&%Xy)4wg=ht9Aid4qn^=SG5BvXETub zHUope3Lx`ETd90YfylaesYXBV6@D7=c|f?NQGScFfIiUY1HC}>W97H_JRp8s=i<); z#O+7?RQDhiA}{3^dOolh=L>-cfs29rfknVB;7!0zAm%US7kVME1N0(bJMd;;1h@d$ z0L%pjfwO_s^Dltez#D)*;5?ugxD+@J2p*;9T%ZRu7NJT%jN2+b^czn(H^RLiWLu<( z0nn67nuuH8%C9zQ)>AR)@j&n@|Li%SnICB)?&T^!+N85UUj}-t#1omG`T&(bXQUZ3 z1@tQTNB08ZgJwJt>Br#S4fm1z%xK`9I8THg%1`<-&}c)&w;70XD8J>C5#K5_u@rQJ z(8Q}j*9c8yJne(&iIYGFg(iLq^m3tzSAj+v#ZQF3V^9`F6DNW$5}JrKV*)}GuLO;@ zQv5{b?-QDcaw@;&sMqn(h4b$iFOW153l>%1q{o5AJvLR}q_ZR*p!kWXr!nckYY

      8g&Wp&`V1_?oQVp7yK!^cEP*!0rx?-$Gtth10JRKj@~~SewjzVJlgF( zGWsZfA3pEMdGK+ZwKoeMr7zik$yPKF{r{TqCDIrW%=5ew~_$_lAqD{%ajNHf5dr) z(L(=P=($4wM(Cdkjr;W~ye0JOLgxVKf7d*ZyqL62zp(Jrj}*hdG9LNUFA_t;D9{-g z(}Z408WWs(-gnW+Sh|Su9?LS%@3?Lu|K~7hG2A8eLqgL}9sTW^=W`jPahT_8wbaK( z-!Am!YGlFlK+-sZLeG)-NkX#&GyOE7JwneCdbQ;DNuh5M`evao5V}<8yO|z!(JS;n zp{~wFZ!vTRY>o5@i4T$=Wi`*wu4nu?-YC6*@!&Pj!>%Qb0pC1WZ$jJA-yhe-3^aAp zFQQHviXe~lIOxgnUDV}-(Vfxy_$JcPpC;FT4EK;f1OEA>JvT!C322kO(jH&LZ}P)9UUqsT z>cdW7pjebi_a7$nQ96 z#x8HPqkdj+$eZYp_dSO^zBixXtF^QD+Ge{GGL+Z0JaeaHD8`(e7^(D+mi&t`n^ZDt zM*uc2W2w?QGD_`8&6ZJWMu*h$lcvTdi{FUJYKe~!DMblnD3>E8^ zL;Hks`ie1?iC0l=QQFPl?FISK=wVfIj9VxD=JD$ChH&ks$~7O>sgqFzH`Ru4>NHqs z+P_j0-<_YG%T%fTRM@i^!WYtD9*Xg-{oX`BSvza@Ge`bF&DILbd zTAw(D(RvKiBTEvuE9#{nhsTp49EsR#Z3ssqgH8LPe22v3Sf0|ahb>3VXdio1eGER7 zHxIiGsd)!g(0ua3z7YVIT(q?Rp}s((ok@NFnbd#F*&xR_S`X8PiN(erY+VrY$#}#Egcvh@qTY*i{(9xy4|{vHmPm8XP%(Pvk%5tXZtmub_WRVC*njczV&6aaUZweOl4PwAlA>Br-qha% z)QCLZcV{>Zc~rFz4<#n5GqL|--j7f4JwTtxTPVo_*XZl;e%v7EGXrOc%1iP+!1PHv z#ch)CKFCWIqVke_4-gS~hegP7$YXh1@lXZ#&*Q>S-^&zUt&_YWDR*BRD?*w*dUB}m zWqhy?hAs_OW z&++}kDUoLq6+s@$*b8|c$-@rj`aIe958T964Kn9~rI9{H~O7RW# z{lhqs*Q>a3e_ra(8!+EEsNzekdS;gL`-kO7*8^_W8K3!#%eSmUz~jX@k3o*NH<#jI zxK$23N3#AF$Hh3#Hhh%tO7gw*UiH`u@7ChowTRkU8^7_s9`xK9#Cv(<>BM`1ioVvy zGhOq9W;@=soFQpHsnc$OeEU0iO3uRA_b)$e8B@F~sowLQg?Htc@6#F9>(nQ5UtnMG zK5HMoY{_cp`;yCC-H)LB=H1<8cyGKN@92eBxVs-lU9{lcMf1+5;XyynGVed)-Ohfz z+u0wH`og{b(#Y1I%nNS4cV1-cVc<64-vj@0Ui;RC{o$<{{S%ls-}~E|*8MQ*mHOSm zeEII*OX=N@pnf+@XTJI!Qj@>RS7f$tT>x1l`X5FcnD;a9XZhSe#k;8Y$-Afv9q*!M zbVqD$6S=Pdb&<9Y{Vl8g2ITF|#5<}*ct_RKO`T%ji;jHd$>?n z6Gi~>odMg$)Fs;oQ*0LF_{~BP?|xGs#&6jg*cWndQSaNb-m|H@?Y-9^-g)IauH?sa z6y25=;QdmZ3to&kX;WQ3UB9Y595U?jElA7vJ1=rW-Z}9y?Yt2sA9=xU?B_F}XV;(3 zJW0EWmJ{y`x4>@NaVYzg`gn!)0UO$aw%!WcF?O>WHWY$A(YETPex70dyejYHf`5zf zZ$&%vPs6+B?znF|lD%ur zd^q>^z&1Ems&O!Va2zyqSacjgUykzgH|*&#@<+#o)bmjgb`(v|-$=iO>BaVpU1Ir| zbw9?*YJ7{tdf>XDm;DmJ1T(3ys`Kp*ga zj6Ky~Pgio=F(w3Ygk?;)g!$gUd^2_Y|6}iM;H$dIbn$&o0tqyvX`7Z5sX1+Fo3?-n z1PGNQCm%p)LkT2>v;|K>5|ThjLUIC0%jjugG*xs`O*JZNR7Mk|BWh|>#c8VO#1WM` zwK8)%*HCdL9dYDNX&Wj`|If4byLR^3=PPN)x&Pn2cm1;SoM*r9de`^bd#}CL+P4-D zRHDAt->R=uq2;05yuL<%$JXX`d)IB;eh)Lo{6HybO{kiJ^ z)?9rFYrULxfaR~(Y3Vv+m9MPVLRod=u-0b9D&L@fK0q6XpPnkTZN1LwG1MDf{;W6b z7c_@%dW?I;kb87>4~+gr-IF7nJ-Ba+cn|D;4)=eL;6ADV_ulSt-qY#pE#ne=6l;?n zHP>VHJj(T0>DGFzz>99Ie`S9WdQFQF$4TAK^4<{hjs~6gqRcCuh9J_^j-wLkvY%)F zhB?DwexFeKp}Hy1owy$^z&(wb7t9Cqx#T%3UzkVr$Q$NQwELGx`NMk#ou5BJepY^2 z-(Q;f%KICqzse643@~rabcN@y2kRSeN*?xRWgspY#zj|`@uY$ESgbGhdKc@Oh-cA3 zUJua^4%)9nd|s=>Q7`$5F+(elbpGhNVz0NVzenDL^!JXmGEW~v9b%ns__95H`2!c$ z|Dd1o4{P_M{|)Va)L8C{(DzUD`=M*N!ukOCkJrb)lQp=*w*JE27wF#%J4Ty}+As8= zoC6{Kf_a~->%P5T_}|&i1FBBywrj>QT07TiGTV-+JJog`DXnaCS!rl*X^2OJwZ(t# zw&uKV4{IlV{|)V=4{MU6Tp!K*qaf~y9#QKY!`cY^MrtE(?RP(asX1}r&AMp=QT|9#PmecJjAY>y_)!8Om^ z(}8<5OxRC{ee=BD^I=HobIo!FWV4Sjp4{so)Q`Deh#rfYkcG$hJ9q~94W4cL@C-5Z z{?!BD-M(tzd$;;79>@LvrT<|J{OR|ufs4O&54@3SA6t7D!k=S{8~67x<8t9<^ZFAn zpGL7C1oy;v8T*1T&wmShXOY(5Azl9>ww}iG&0B~Y?z78>?m}K}DjtZIkC-Qle4L2K z8)123#!v76@Pw6*=C~5~c9_T~=DQxxGjT!|o`+`JS&MpAjQX_(^=vij+p2*}%|!#K ze~(A%Kkezzww-k@D0MD0ZVW#(ZjYeOJ%aQ+iheAJI!8P4{Mhfq>M?fFgKMV!-pl?w zw0<%C(E4T9jnub|`;Q}ET*x0c+FioH#o)&451Lrja7 zF-^LiJ%Rf-eXm9v4A-vPhkLX$IPbK_qypajHh(GJxRt=SA@NWe92R{JAyojWXr&LwjsumaW(fnG22iA^5a_B z_e94DFItvP9kHCKJC1U?sLE-MI*0YQ z>5=?Z+S;tH*E;R2-?XRe_jRZXk!<*+y=r~!pZ4^1jL)1Cbz3|pktT(zI zaZe-GA@1G8`g0B9&-!KVb)>K1p>>t{%l(IRU40XIFv`8FkOtO!)82pnl67(KHrGJs zM)$x&7&p88@G8{DBGku1)W-tU$9(kvKMt#t(fSMaOS<1*?2E-Z-Q_l(x>41|(0(zPGIe{&GxWYqp|-HL+}iJIvTk0lc0SBLDSSK`Jf_ldG&pl-((?@yGU%jVE?Z12i*f>32w{AXj0LWXA+Dj zFKxbi;PnS5VFP^;>krFM*9G=rZzd$59h>nTyRBg0VeA!iW`DkVzszy(GYNQCDevp{ zy*O=P&;Oi0(D5%x0}tUH%jJhRpgc-ZF6&V~_oAHEp>3Do-Y!wy+p!GoM-}RjeVpV~ z)rXqM5_MI*i$J zZ>{SA>m$Pry?_5g)_B z-`KzNt(WBY3&2zIn=&qY*Uc`PAFU^I-X>*x1Yw-OQQJ{l-5#i2TN5j#;rSCQGwplb z8`aJ2O#y>D)En4ZeWT>vT_pENW-D^H6oDe^B(D`ImJ(3p0Vz90@})!-S)~)ASg8~( zrpf%D>zc1hTXFPPziGde@9*~PoBt|P%6*Y~anntQ`%&)8psR&>68}9i+3pg4qdtbo z=i;WA?j;;}R}d!WW~Fq8Tx!XAn7ju!!*tviCQ|rWn503+b=p@(u)hMEMbMps{Yd^3 zZ&dqmEHDHwc}zYIM0d4d6gUJtB{&E~zqFti_(@=woG$^Eg69IC0nQWbN1)#U?*`TY z4*;JA?gKsr3;>bDk`@!ajX>&`0|P)GkotK*>SqHppg-S4Zx)dHX~6q{OeFQkk#6dr z2O^70&Y9?a2}u2uz;6JDfz%%YQvVq6I_UQTneJ{N^@Bj_2Y}RX1X3UKo0NWyiQds0 zO#M?p>Yo5o|2UBPgTQNH{}7P&dw{fm07(4~AoW{-%07_#6+r401F5$HNWEMj_2vU{ z6H}57q}>!C^)I1*Q2#uT`eQ)qj{>Pb0;K*hkow1f)ISWQ{vjasdw`6`K@+{)$DICJ zfb>@hq`z_?{gnXeuNcU1icIv5Vz89@r+{Aro&Zw+IFR~-Ks33MVFi`IrNudqW9EnQ~v}|#RJItG-RUp zn8*(UpN9WKKw0j;%y|X}U?NBmmqW3fqbG{dx1eOAi0>1-145WT95aW?0Jtle& z0`YLUpab|FU<;6XH9(97lvJANtpNTF&KHT?2gD_&WQB>|Jm4VYxxiUCpKYRd{91`G zkao`k@qV#n)I{$~!0+Jv3&5v9__1$AHfO2Z8i|6!;Xd4~Xk~$srTHy&~s#8Yn*iWH|&)^mYJQ4mCj9*#e|qDUjt* zVxqSQ$aH3ld_Iur%mNPL{5&9T8cIASdJ}=PcL|*+?VShWzNF-wiQcn7+B+rklR(-V zG0}ShNV~&8+%l9LH_^-Qb)fzpCRX5lmvADu_b3|K6Y$pu90GO=;{Cbei6ZBBSeSq3 z1y2I0e?s`6@T0=Jga?IZ33>z*1y3Pzv@lQp9SOY{74-@NcAaPJ{BKKeT-SS;uR z(teh3k8r&EQRhz!4g%@_u;2k8!y|Sfyr6I*xc4v`CF9!#WPX(bnNKSOxlbqUr3)qs zUO=PddG6au91$E6JS2DkxC8Pc;KQ^7d=Pj6jg$U*1P=<<0Qcj3C6MtZvVLq4PGtR< z4;;Y0gWSIpxHS=24S#I(^hd0Kze{N3E|>@W9_-H(WT$#RF4gA|-WDLnthwK2>M*bphmc@H4=E;QN7nKtHe- zSPkq3ZUuG#5r@=z;69)a_y{l$7zAbm9|dLsKMV8#KLE@Ceh`=j+z(6!{snLx@vH}q z0e1q2fk>O$x3U>{4171RAJ_}*1MULiH}?fq`&PCByTBWP%$LW2mB5bz^MKvJY~Wu3 zvw)8SJ-|-`Gk_lhrU5?=Oa^`e2rv!;X%G1~m$(VH5C}tY;%4xA;lvxkSsv6UehQrB z(g99v2QL*)+yh=Boag~B7EYvp#viuTewnr4xx$It!Lx-E@yMz6%cTB%aQesN4)a6p zm-!)Zrk9-fVeoX}L_9jG{W2*}1E>FF6V-m1u$8hB{40P^m<8SK42x#3#ONfU9=B6v*@} z0eXPBzzpDGU>a}{Fd3Kw1Q@8BO8za89~1dEMgDCd6LvT9fyjDC|7*dC9pH(;VsK); zxot%~AzBDVCiv3MfkESNMUrHUt|KPO; zu1#`}W}MAPa-a2#d6L{+w;jCA?LL0nF#eyr?L7YXWgX6PyH94F!vEmy2X1$}58d8} z|2=o~-r;tS-Ej{8d+zML)9pTS=Lr6f-gy@PkIx^T?{*)XKZyUM^Uvac&w}0s_`TpP z{tvz9_ zap-m3k6i~o1J{aMO@#y3n|xUKZe%d^M}_yJAgMnj{5sJ;CVZXne&Hu)oBDmi8xl-= zr-lDU?DYyy5Z*0(x9~3EJre&h;dhCAM7USNs}%lOrXTTb5zgmW@($s@5ndrYM|i#P zRIyhq+%57_;hz(EmT*4j(|)$_e~|F=gdY?CKH;sDyYXy!qnZDk$&p8%n@zs;>QK(- zRPGzA_g%=j%9Jk?{Xe3C(SM=vcZqzx@M*#y6rM$U)39HIa6ad9Us=6x0-vMpe5>$f z^Q1zN;~qCCyg=kW;R}VY6+TD!M&Z+iKPdb^(2+5`8sQ%kzDszg@IAuMk)w|4eH6aQ z{I~HBQ;zb`<@1Qh@4dy0{}{uE{$}AnV)=kq3cp$O8-&jmd8_aX!XFmCQS=W8UoG;F z2_Ka3J|(Ps3g#K@Z zzeo6a;m?Wx%fi1P@|jX!%S4_b{F6-275M(5@b{uFq+a2%IKNv!enI4{E9B$C&xrhz z@DI@*JNetp@~NkOipSzRDaXx--lu`%H{7RJ@3WAPyyHHsXHD;F;{bB)o<&_CAM==J z*fo-`5dKJ#$@v`yx{*Ya>-RN#7014_$XB{QUITejpH=^Zh&%W7<@g8PPm$M|ZtTl@ zlgasg2CQ>5PD*?GKH6iZ8&3toFH87(-&pSFO8>p$pW_snuAmX%g%s{*$b0f~*rDTd z68Wge^*+6)(2vknB5st=m-t<;{fqo4bSUo=|NLG8d6v488paPLy)VsE=8SSlZzanN z?u(M%G3n2KF%ABa-X2Nc`z5{OBLA$Uw?p##2a;ZVOi8)-N_zP|imp%MdjaK4_c=-L zknn$XnepX!Naz*|Pm%cX`y)(GqsTu99rAAR?`Hbo$Afl9H=FT?T)%I77uqMwyI<0O zRMOA!BDyyu{rH}ga$BUlk4gH+B>f#Czgop-*eLaw`ghR3YuG4}{FsBf&GJtZ{U;

      4%HL`2U9kM`)bWSSy{6x8$KPV8xBk@94lO&>Uh@B}<^K&E zPv>L~{d=wbI&QV-xw!Iw(DL8GxTw8FCcod>8_r(~)IRe6is?7n_!XP{kJg?JespWO z*2Yi%__Tb`+7mPRQfp5=Ki;)$w&Stf^q;r=KWq9vljlv==R*5`!Q`KieLhQ}f5G3v zrs8l-#AKao8TFSk;rZr&s=u^3^!K+~_7db%wm<5_{iprGn1tt_{o4QJe>XQ%wfu#~ zgYjvy_TNU^66HzlKgJ|H-~1P=f7r@@Mdh*g+a`ZfTf78nUrO6*v z`_TWr$-k)jkiTzowa!<_gt zez)1{FnfwjyKlEQT`GA!VOcCAnEs>IzR#Qdu(j_S zCU3L$&6%8U?R(Ya|6}dBgojjG{?Xd=W|P0K_VBNp3tIlC?ceRD|0`?XJ52s_YhSy` z&s+OGVDe6D->;jjc{}RQmrTA-$2)BZ{XK4{&P4f-OdjB&v6jEn_|m37v;Fsq^(Txg z_-EFiIkW$=jqg92{D-#xS6Kh{VN>SY?N63FWd1LGg(mi`WPE5 zf6Td3{fpT7?MeGQuP|BPE7IR+@^TxWYLhv=IeUFEHdy|L#*03O{w80Py#)Dd+Q0mV z{v$uD@u5uU|M5N8Se0=8o}hxO>EDWV&U|DeI>}*w`z?R1o!_>RUu_B(-H%F?=Tc-n zA5PT&0|gW1t5fnnO8trYh7|e3Df0JI%KuD?{Qi{u8jD2xWhwfml=6BGl9<0gMgMq; ztowb5`EeZT+%A{xso!;P{T>(bCjRbMTzA(!yBoIMv*Y$X4{Y0Y*S76-x9?7fs1_P< zj+FPvExsZXuht_UyQW_mq#d?>*E`-+9$~M=Me3b~QiJe3aHw$aOYT?6KB8 zd!9HXpTLBFGoQnIaySzYJ)*ng**Ov4*3htPPnfKOPlxfIA2QyU`B`_P=sqR8@2}sp z>-IZ#Gwz88L3+%n$eZ2~9-OhHmu405E3Qd;k^AAqGD$l#RTB<+lC5m-JSSz!_PTab zrfe@sCuM5S@_u(xrfhF9CuPd^hILY=Z11usWyQl@M#-X~?s z_DX$Hrfe^DCuPd^)_zi^{aId#Ps)_-4fUirtObjgeVug_fvvHz03vJN-gdDq>o58m#A z9HhsZ^WiQC!UscZNcAIi)<6-wQ) z(q*oLXml3rGHc{HLd#_?>w?NAvIg>13VTqw9q zc8Ls^xh1(H!nsfW<}g@V#!qimPA$>ia_rsQ5#K6vmOW>Lw#@w{I9p}~aoI8}&KZ&| zbFr+^*s=y=TZ*Wb*&s z-KFqf=_8n}U8HlKjt7xS`8BpBn}YaVnTMOuU6~a`?aHj!90s_`_IV&?SLTvggL1vT z&|7h{>7x}_r3TgdVdH6Sy0xkz=9Z@O4?Oa&E$vSqtjrcqt5)|vWB!hzZaa^0k3ZWo z&HXklEh@tj%G^fO{4mWObIa?Er`2ouWlyH{eh9ZrHL0vq2$#h8NSWe6g?6|->lVEy zpl-;&lQlV1IekKWq>S;e23#&TSI%weeT2ltY{rw!-mz@)ufpf?VIw-Rr}oWQ`w&Bw zvzgf6O!1&J4^a!ZOWmUPH4>LB?Nhw3jPamuHGARy``GKVmo*+#t=XHjMvYj1^*nUNW5Uk4S~$J2873>$$SaWT9s92Lvlr%3Niy92A2 za?-1iID}jd_5NlH2DRVAvDKp*{VOkXS@p>4hhAQN_De6lnjd+QYFwPBNEnaf*)I=a ztmND4fA8^EpCuO44)UxWxJUWIJlb~gJFXnMhrGw-NA%n4f4}v|DVN%T@E%0N!N;53 zeTXObw%!(ZB$}AI`yjVxE4SQQaa&~n(dNDF%{!ksy0`t{;X`*EJ^XlL#!#;`?(;t0 zmYyNC6+A~g_TOIdf|}QL{hd z$93-9|8KNS3Dw5@wtYJ-DJ)&SJoEui(>GeyZ2BJb*D^J|6J7^9AfL55=4^{W8!RJT z2Pq%bx)#r%0jgXrRJj^RznX$Rj_^LvPi-fDel? z9vuR`pX=sblgG(=gxW_sf}7us4fp@2 znW%qDO8#e4le6)QBUlETU zK6AK!cM&^aFa`nwQG zk53ctON6Cc@fw}u>NV}!&E2jHQ+-$6J)kg2yT?5l^{ENtn&Fw|P47q^=9vF&5FfF$ z?|wZl$yg*D#0({Lg&e=f?{9Fk&V7e7_iexMPMGgp#`~3F(E7}mzT7LJ3E^FXY*)_l zi+knGdQGd({SSLxTUoKWdSkUNY@5#K2;Fw{=-#KZUDvufzA@F}-ldA@-S%ut#izYS z@8@d?WlpK?H#OghZh5`ewdFtQ^UI!2fgPWVM1D`UGhf$cjl&$;`6X+6+a7N>T-)h2 z6vMd(S+7GU-s!XIeKjpb&Hj3#<$t>075Z&Z>@|LUuIsXCYtOVbr1TMQuVdc5pPFdO zZJWKUajXaS`69Jea=yje8~dC1bz`RXIj{Da?hDMDy(Y6)anRd~{H=RmS*a=Za>t3T z-R}+k!0h#6FL6oVAK{!0pDXpcw)TKe55H%7-{sraMEkBwG;G_6*R`4M3k)&-TE;yh z3;HpYu%x%opIWos7dY^!FZ@Zq|FsN1?isGL*voofpjbZ)SjN_QHXiZ)?LEz$ZL7m; z{Cdxx_$>jKkUD;oJw9KPN2HjSXk})gD9_Q7cIS_boIxQvbJ-?;uqEA9e}vTTqVs4*p)nYcE8;72d1( z#8<8!laBFn_b5KGI`XacVGJqmQ;_#X^ht4_6}u&|Oc;YEF+NIiJe70jPi&LgQluD# z8DeRx4z)=!TWVKqdx>MDcO7|O$uClyO0vc6{*J{yboXKCuVTD#o%s)gm)Cs1i~pMZ zNJA4bxIgsWpZ?>QEyi;b@wjJkkASbu@4ZaizE>;$M6i6BHsqfFVk;%o${miNFHOAO}M(MNN-HZH^!^ zrBlm6l$LTfXxnvY-L+NIOQ$sZeBb9M<3Bp=1jwJ& z2Yx^Id!FB)=lAD%-dFGY-uwQJ#!79ftmf)3F9;6SQ{Wt!bU%D-E9I}{-Qina{L!^R zIA{FoH++xek#-#Zeo1@BRVVLBU!LCrYk@y!(|wF1haKsE1@$h)<#(80x6$4Q`p3L; z?7T?yl_c|dBTnXKZA1RXk_$k)(N*wr~fb-d75(^>A!}Z z*vmwXR`1_GR$Rq;W{ucdKI*D@n+8LJ{EoPhr}Mmzi=&K?_H7VSDvH| zt`e8~-G+DT5zA z_qXPE%l^cC{@~E~gI^sMf1V+ht=PvS_6f`DaHai$)^+)=appZ>{uz_Uh!OPR1?KIq zR;_yJ9=OODDPEkJx#q<`ik|V=A4RKQ`BNT&Qv9<6`Uz7Qv!mD8d}=*CN=(%pN8WPg zXI}_ou&wh1F`&Tu{mv0_vwYV?R%`0ezDOq9h&A6X-xXlr;qgoF9@Kt7cAdrCeV;Y+ z$;g?na4t%Z!8;fmj~v6rI}e|~NAWwA?RFeIHrC^Shv>CSL0^+E_{j@VSPPX9`<*YV)fy3VSW{*zr?)bT&Igi ztkW#EosMmXjxlTW$K3bYc%Kp4aKe<27U}R#@Qz# zPw!?v6szy1jg{u($bKBTewO>j?@@kGT|-^J68ZBlF>lMhz%QY1RhidXV;Xm@vE}%n zcxmzoojg0&J(_s^Mqjx{{Qqm;afRRNc@P!4m(d+{y9ER$T~gxy4LYu zqk9w16Z$a>{22ZnKTlZZJR$!sFZ=U^%-8EXPk7>(@Q7>2CyoiToIm`#c`eJ3ettc=+}AH}u-^iDSaozD|EU*N#sd6NdL@*)ib0H~g3| z^R4rAt8RE-n9dWfbpMEs5sth!>(DYM?kB8#FUpDg34coW6J~#k$Amv8_Y*$CeJ47f za|V8U2lo@6#XWesXN7k_=%HzNBV9gpNUytA+>^26o(3gyRt z&Am~75~n%YrF#bz|Ci4R zt`z&<)N_Jw(7l6ydO`5O(f1BUU()aT!+RRJkLnLt<99{QIQG4RM;w#L$5dAL4*s6M zcQ9Mw-obs`*LLWAZ!6t9SiQe-ywW+rKfU0Z2Ufawa9JMS$Hu*b>hGcVaauoLy@%~5 zy~nkB|7EOGJ0}>ndE(x|pR0QZ71x6Od--?yxQ+)S;e5TmbApA{ksls*EO+8M@rmoi zzli4q|MgGt`0p3+I&ozG{-r~Y`@Xx%eow;h5I7cm^>}dkH&Z`Pzb7H?ExiH9gca-f z866WY{n*EZKc(x$+9xa@6aJ!JC;quTCS2j1AjgDDKgY*}tka*fW5N^X1P`7QRBYs! zkbjpi`C~%n>-8NIzVMglH640P_`>SHo4+xR`QI3Shu>goJdXZ-vB`g&-xu>cqr-mR zcjEWOZ_@9J|Ht9SgNfJq`{F#mFaBlD1$6RHe_x-^Z@rd(U#zw9v;J-Iq2~gxIF56H z)w>^UI`sZP^cx5Hcb%`d-6{g(gD@*&rL73Y_a^Hz%SuXXNEzr)b)hL?YzYrh*_ zcl^H_4$l0n&h6@X0AXzX|KNAS-{m*28Ge7NbsGfhJ{zk#IbvFK^QE-CK5F##|l$@!hh2`)4{F ze5TBgdp(!)*RIyYKfNJ}*-J@xtoo&RIThf8=le7gtsB+w)c7 zGfs5vUH#DYild%$!!gHU&$;0`LjhZQ&OG8dH#{ri&3(?z;kN2G^KZuUZ92HtvOM;O z&g(IMyTUkW443EdXZ?(u3#&h+=MN0Lq0hKsY?hy!5I*CEKJVn;kM)cjd?8$uPreDy zxbbtRYtj{;af7`~)Gx*}ZWybho^gZyulpG{$Ef2cK0d|XpXB=AQyZ>%@pPU`p}$9C zTyNLkGs|%-@%Gd?N5;67_6OlJG=9SN^$d-bp0TsybBSJK-PUrgxAtV#7IsXY6dBd? zJ-$yo{$1j+o>_Dj$JHO1@V$hR+F9slO>)|cgRan$$i&8uo( zxr%4GZ3v%fRQWkguldX&`hM6mhuBYq&m4Lp8t%&?_~deIe9dPLv0qu~c<|8sM)e%J zLyYxd&m6+0qnCasVm{uG zXAafyTe@{`z%z&F^H225A^Ln=&m3Yb4twTMn6CWHp&!@BfAp>3+e>=_o+TIMnR2Um zhF2Bm8+cCMV<+?Mwcu&aE7&u};PNxZ&L#G)Ck`*iqBDJ5I?J;bp6%~nOMc?9vE-`F ze@Gm=Qu)t8r+w6SxPGZ)s}y=2s~1+CgRC}u=HvHPogL~P-0RTBYjf)F71D`&*H(QSNA%Iq3V)|#qt=1W@hz{1Bky0+-+`L5GlS$et2-Yl*GD72V{@4NX5#+N z@|*s5&i#*+m;YXtf4$bTJpbkU?eCmfp5?i0W2CdQtndEs+VF4kYWi}d{QDpMr})yV z&uTpM`7X=-kI?^2#QLve81+4I{}l)8*YhR6=g%W9x34kEAAAm>{eAxZkA5plS)M`o zYFgvJT;@pID#?$w+4f(`e@58HeJj{|`62e&InU+#-%MMV+fd(<{`|%AoGd?API;D( z4ZZH*Ii2Na5WgAqsLo|u4z2%q&r#y}LiLYulbzGw;-E9zM$3qJL@6 zgS&Xe(!Pfu-t}Nl^Ueo5x_Dnw_}uqMXPElX&fQ)69_DRMH}Wv^Jsnr`;OkxY^jv-K z?yg-|ckO;~*G>B#xO>;bS9k2%d(XqWyY&XAt0Nt|!ulh-_kLvegB>^Ddu`Xwy?d{Y z?Cp7Y_k;JT)em_CcXoI0daz?Twd=us5A5PCP`j-A%6M~FsN>h9Wkk9Bdm%%g``z1%syxWXI#@U92; zJaTx6h{>+$>Wb{@y@#2)?{NLW8kd`hglj2s*Uo)Cd%~A$?0GnHS7S*GP9+c<1p9I0G3_VCqnWUgbG>6gWFWe2B#g}ZF(^5Qs{KIZy3m{aS2 zIkESURl;N*VrV$Iho+BEZ`-li&+ZWl^Y4Q;+Zw*voc<6awIaRLuSH88EQpnd>sxm2 z?J)@jLLWH1gqG}bi3j%e?7X{c*R_YaX1GeX?7z-O2_H&s>mgwHsC00R9rRVJaPRGh zgpVV2l@``r;VNR!dg60p1Wt^=i4piY7y%uO?dpj2?1_a3SVX?qg0)zjid)UbcggH*&3YR6uaMK0W@totGtc6Eh? z1ipt-;lg{RzZY5fg>7hOPmf+orcUkM)f3zKU~J!mAJ_ZMVv5u;c2E0u(Ll#Vhm618 zKYZwicOf43IZ}VhVOo2C@u8y4hbH*`2S@uipFvB$e>FMEG@^c}hBHXVg< zun}2S-hPO7S%&C}Mk}x^Qnn*yAL`n(v*++^TlehaNat|bmOXpA4wvYl=WxkE_ukcE ziun7k&`WN4*=>Wko0r}C_uise{(8%+Uw1sl`xo^`k9%Cg%igb25AUGdE$?rPJmPEH zVz16s6_gDdc*Kh~BYVV(Nwt|$yG^B5d@lQX&?2Az)n?w`-?(eEl1w;!eswT9A> z9UopE!-o#7pM>gv=+OGZmkle|!TNnJpFTWw%lodNbY#avuNsX=mGV!6L+aIZQpCN0Wr=?>MyngRfRTSijHZ z(}$<9pVE;X9m}(|<x>G8qVWJj*MxC1+X_Lp?3T$3173uN_ix&_y1nAf@3IU z2j~1xd`^tOVIvSD^;&+Q{W|AOD}LT6y!ssShXo`1Q=*iL%EU zffyN1`0=qu>G<_0{P_6C>O{^rZUkafb;6I|xbZyU$8X$No~Y^gM<7Pl6MlUB<8`c@ zC;a$WBXmOlW{g0LN>BLl8#l5i{P>L<%M&#n{|Lm$dcu#7f4q*B^MoHCYlKed-;5E6 zQR&|mKR)uB`I|AGmFoOy`1g_bR8+F%g#5$^{GyLQj1Ih(Kfh7e(JGD8@z$^Fa7W$? zRH;r~musk`JWjddjIQmy5!d&4M#YKG8$1Fr>U*vAaJ=hEW@)|XI_%H#lQ_=CWvhHa z_Qv@4(@x7DmfPg(8~RBcQX9`0(C4T_Dld;TR?s(_JoJBj_^9%|$IxfJzEb^jUhnNV zY6+ISehjh(k7_^b4`8;TU&+2DuisQb-{e~yuTcM@*Y{T_pOjM$ z!Tw&~qe}hn_xeix-|BZZoz%Zl|3|$30Q!~2uioEq>f`%Sqv%(%Z;iiU**9}k`E_3J?K`Uf z^VL-TEA_t(r_`sDe=E&T-s>y*&!(EN`10}5QS;yB z^_Bdm`ZO!A{;jk=%U<7uekK3ydb^dc)c>T{SMs0c)2)0Z|LOAj(F*p(e8ZLIuiooD zjvAj$UeRb~9HsB}`UdnX%}?eS`sriTUv-8x?6V!^|FvFUY5h!l{k97B)%lw-EBQ~t z>nr)s`m@ZwO8%4e`by)U^LnkXmG(cG+Sl&?sMlBOe-{th(5I6B%y@kz|7ktP%4>f4 z95w$*udn1k>(8}Gh*zTgks0-)ZG5 z&EF=k_x2q%KCNEgXw@90@ALXf{xf1@Z{;ib&$!oD8vhBeujD_Y8?63H{h##uO8rk>VC5_MPtofSROnxa zUwD=LXTa-+D8JJBYq-$bujD_mcbUGYLi+<=U#b6Dudn1k-S4*gy_rYF-vO^5CEH5& zH`iPFO7qw1_1?at{G-F`8?Bn7^n+es$$ut~p)Vh!{*C^}c9q62?)8<{Puc6YVgE}0 z)AAncSS9~Sd3`1S*?O^+ujD_|USDn5j+(z&ub)G|(*9?3qZO>w|4FZ})c>UaL0Ko& ztknOa*B?N?QhexmueD#%f4qK(Y%8t5hD)uy{AX2g^!_X6EABxbte_w8`bz!Jdi|v8 zUt#~){XT2IqW^gPDA`uBzxn-EzS8`)dcC*rDF5j2`bMkfDE*+5C8}A;e+VnkOCI88K zeWm`7dwnJU>HC1yU&(*6UO!5Z1txql%MnZ zO5;E8^|O>;$$!SuK$^K0rvVxW7Z>!h)`l~GW ztJ_YmujC(tUcZ$hE6vZ`G0LyG-r7VLP_ zSL%PgI?gA_zm?`M>-C)#^ffo4;8UsnI4uF;XZ^-K_`TvyHSMvYP zo2`D0-|7{P58GNz-&3LeDX*{8zk=6KRw$pn#p?I|dDQx!^7>J-tz=*LmL+rzR?SiV zox9Z(jZ7Y=3Xamx-)8zI^effB{&v&L{+0CeUSDZ_E*wL@==D?7zta5X@38hO`Ty)O z^d+zFt5AQ>M_;@C{$uEey}r`=j43ejsbqiCG4!oo-)z+!HGhN0&=-zTfB6{tnvYq- zwT7eCXC3;-qmfHu@UkG%vfk*{!4E_uEh-P)8mN5L@O|W~GP)(&S3cRHe2Y-|O7K$h zEg0Rr>08tv<(q@bHv>3C!yLoV067u?erL3Csg?kcro&X(QSijNAs(8TA=bZ!;8qb)#x^v zzNN|N8lmzvz;}~xqtVrwz9nXKYv5k;l~|vfXlKFbicsZw-f-kX%Fh|y415gvG?e{Q zMwf@ObKK}MQ00f<1>_qvy0qzAR$-g|ahlbemw< zPk0{XHyT|Xl$|jsJJ%as4V0a0jBWwHSNS4bPriAhn}f=yxS{rEpz=+_bI3PkbQ7j; zQT$Q9F{pgQa6R*#F}fkhDfE^>qf0|hxwrHiT_04r1MqC*y++sVT@O@y-Co}bWq*g!ZHKbI&Dg#T%8ocxJDZ^F zZ!)?DsQT)m?5{Jn$DqosGrg;^w9B&}mHh*-7aoA&xIo$8WpvG6zR@!VRo{A}TLaav zYNM-yvLj-2OYG0pud>mVp!&67Y|lWoI{+^wU)tzeyu2BzpIeP?6IA_;P)1IPOyWGm!U1wM-k`h?i%f>K`_`A*lKXjV=vUf4{Lk z1yz5a(QSg7?;0q(OB_e4y?J;V`3goi;pKU#_Qs8F6sol(6 z8lcu!EmV7JpxUc8y78M#o`dT5h|wjz`~Z9#`FoA78?GhYWpo|ze(XpX+qXm6(Pnhb zFiE?y8?BvM$SL%ebw;~WNr=i-JGP)k9{&mBR$h(ZL z1FD{c(X~Ow(RK0njq2&p-#Ls&!swd3d?S1}`Rk3Y*2@e0UG*;Hb4E7{ zxzy7#V|0_Sj`W1ljX|}OgBKtlF}h*6fpo^`hM@8dLizE4(WT+}r2CC-6MU3>wNUwM z;5*T;F}f-*k3iM4#NUz7mEqrzUNpLCsPf}b`>ipf8-?0$<%}*1wci>xx*@1?15ov+ zjV|ToeNg+Yq|qIK+Hdt5-FB$`);4%1@>Zj3hJQtRtI@^bBjl@vYHtnvGxXI)x5R<3 zRhtUl}jZ+$)g1q18`k=-sX>^@X{YyZN z&vv70_3~z@c62@kx;Q*Q{zk8_hkuT~&gf$B6X>g<@-1;5NA)kllgL*xx&^3wvrzda zq4JGE*)eK#IjDRCQ2A0&`I1m}9Wc6HsC*qz`L;vl+h+QfR-rgurGdU~POQIFBJczJ`DZ}jpS z7{>ptR&D{R{i4wopxU1^x*4c;r;Y7XP;YH!Nul3v~e)$eYjYxeT3P<|RWx+W+)8jWrvl%Lic+v}k0 zh#6fClwZzV# zFK>evBHw0ot?*r>TZ}Fa>qu`hx(2B6s)rXKuQR&!a0BUDqgw}+uLde!uQ9r6cs}VW zqZ{RXzV<5vQ2G0z_A4o)OM3YMsCs&ht_RMN?l!s>sPauv`;|tcYk=CXY&5z$cn;~9 z(bdAxP8iy0bX8s+f!ePueK4deL+w`pSEh_^0{%YfywQzA?N8Ua;9AnN zMmG($UZ;%hlThnx!szl)`;{Ejeq|79{q{oD-vhP3=r+1mFK>pjXRFaQK#f;DRQ@`n zTMso}wMJJB!~MV}tA7!$Mqe_z1*mvD3zcsQ%8m(m68Z8*Hx89A3zcsODqkAPu70CS zLFMa#%GU)SgY{7P>!AEIW^}bs<5vSUP7$blOI%-6zOvDkpz;-=^37S=%|iL}jIn*% z^ezKc&mh$N4j5gRm$!O(i zzA>Yly~665hKl!7MmOx`gHZ8)!07tCyancwHyhnn_*0Fjbw(G1=aOD;bnD8me3bs{WRVqicdHzY%J^)*D@| zm#=}Ux7z6DF0=fzQ0sNZ=%%37*QC+qq1NlTv3(3`eT^Dj4l3Sfp!_BUwSGIG>`6ey z@9jpn$;%s|>}fE%^-$xr4k~|*(N#l@SC!E%zTe_*5vu+IRJ@-vx>>0BJOLG-$Ds1% zpyK_A(Pg3X4M62fLFGFDWmm7!^+4rIK;>(LiudcF^4CE5=NhA{f*QZ2_t`itK;S#{o8I+9)zb%0BY)E9+Pr*|mp6I&dZ>6`Zm@ETQ0-Ov2UPtDqichzf19zr6{`Lg zqpO07zq3>>yT_o~8-b^hFKcxDUY>$#ug~ZXK(*Ivblp(xbs5_`q1x*(x>~6A%I~#x zTYzeB2CBVjqwDkXUPzIa9;0ja@~!Y~$m2%W1lN*oG`fvY{!?#kuYn38UKtRlXjE@!sfSFpT#`w+@E!-sq~K%0*xp@89F&J(Roz z!+39WMHt3=qnm_byoX`DH@Z>y_pmQ#bR#f~_fYlrLdBaNqwDhWPPh(vhtVaV`nlcc znxM*Wgj%olMpx_QYoO|_Ho7?$gYwTpt=Ac&n}S+jlSY?^VZ1lCk3p@kQKQSjFy6y3 z-or57L)D*vr;vZU(QWebMksq4jBY&)<2_XV8l$U*8m}s&Tdeo-9;*HV4CB4g&B8F= z!!X`M<;%e^-Wy#OhVdST@g6GQ0VunAjjjiV@g9cp9)|HADt`@R3Ad~if{J^CM%Uxz-B9&+8C?fd{RyLMgQ|a< zvAq?l{uZOFf?>Spq(T_)q1qdPVZ1lGelJf!wby5K2cX*PHM(x7_PUJiolxy{7+oz4 z<2^x4>$U*Z-V6-mz0vh~c`pp(z0oy$`BoUld!uWDvZK-HHo`F88{6xk?1&j%4GiP` z1=ju~RKF&SZXBwj4tiveUPQmk~F$*FK>c< z$QzBW0X|83qtV46rnam%x*Di+z7Ne_&D!&dY-q#r28mM?*ZFFi}@qQ^5(k()j zTY##+XmkZHpM#3`vqm=q74N5wZWJnhjX=fwtkGrQe$qomHwYE)yP?|afUD3ajIPbg zw?W1GR-Swdj)j^f7g<7xcjIPSd%ja3Wi$*sIm453~GIi8r=xgdd(W! zhoRP2#^{Ei;(b4q-}FGO-&UylTcF~1v(eRic?_zb>y54&YP@tam+~($+0ZRQjaSL& z3Q%!(2CDukxElSW(M>?b=NweNVW@mV@FemL8r=X?z9dw>9;kesP>g!3sk073aZW1bA9xC6crCkn&{%mZ|n%?z7)zb|% zzgyw1z3q2lH|i(cglP;q3==w_hWpEkNlsCFle?RhA>#*HowRZj{k?)4d6 z!ppZq)!$}xtx)y17~NK=`s2p-O;GhW8C?lyRQ#QQvU?b+y+Nq{4j5gpm-j%m*KKs2 zQ0;XX-FB$<+KlbnpxSFSx+u48;!0G z%8r=P)k68tI%9hclpSk~Zs~0EmocdJN1^(aGrBBPeZxjK1l6xWWBUM9eQBf15tLL< z7AoEk8{Gjf?}m!^T}HRv%j@91$YVyg9_}JtYjkU%;(fKzEfMV0&LUL2FBx4CD&Ef< zT>&cJ9K^(yS)-eQgxr>Cqf0`?`|VKqw?SO2rPb(~y?iTFJ#nMk1QVp2jBXuNc?DyZ zR?8B>7rHWJ%3BtVZUHg{Ek&a%K$V+?s(;4lro4O-()E@Jqsv2@Xc;%U3{?CYg#6z! zV08WPPSPo(>w}8-+o9TPg^D*VMz__=<52N_lhHLn^|R6FYM{zjL9JIc3SH?8lg~rd zTQIs&FV8`(*Ab%|hFV`4qZ@=;uLH*RG}QX)H@Xy5yzhnbn*>z6-vnh(6IA?eG`e+O zz6Q#kYNK1Ea~iJ&sQg8vD?p9coY74|#oatq{bNw^e$?o4Q1N*PDqk8ZUkWPT_ZeLh zDqj~=z64ahZBTZ#8eI!iz9y)A4N&oZ;dIMig!0dMqnm>ozZs};nt-Y|Z~B&TqZ@_F zHv*L}V`(=8Lw`25518I{K-IGyYJS^{uEERKd3lYOFTUO4<}_5fDJVN9jV=$>{x^!WPAUH8 zpyGWRs=YqQ|1C+Q>+tdfRD0WvZW~m4twz@j)!tTPdmO60O-5I$vG%5+*6kQndn1r6 zEm@=6?&Yme{cbV3n3u1I@}F9xtAVm(jnP#>`HvnR1>2X%EIZ0ZSA_DHVW{>qQ2iP* zx&f&A(ngnp>Q|q!JqcCc0izotv+5auiuY-w>-6&NQ1QOa=$gH}1~wyKV|3N6Y<)PyJxY2b%#rtNc{Bfvwzscwt zy}SXco{dIV54Vu6Gdc~1$}g<3cwaQSd8l|_FuGZ&ct2xwQ&8n5pz6;X-I$k;LdE-> z(TzaG`>fIRL&dK?Na$@z8eK1RqSxU8 zmQJd{n^-_G`(wus;3!hezzLkdM}T7`BHVL zcLk`pISy5B49dx;ChI-w2g|BUHSvH@cXYuZOCq*67wj4slv)jBX)ntkF$+`8ZU)V@B8S3AegZ0g z-sr}l#%t8*hN0r_AXNQnhzqy$8(j)2KKDT7>x9aefVf1`AldueX;cc)R-U2(}O)vplU>m#vw!#m? zX4nkl@Osz;x55T^Ev$!IU<_UZYvCqX1DWHs)$mFPBA3HS*5SL<54cbLfDfr3@L?!_ zIUAWSPTcF8U^n^(xD(dH|Bb%Z>uaFOQ)kEs zETS(lU$T$yVfg~Qi}Wn~uj+@_=b_4vdFJ3BqR)8!AXNKlh^=c=@ZX{D_4;n8@|~Uu zIDx*^>zkp<$Kl%Be(Rle4<2L3Mkh}V}Hca<-}dtniN75yw!|E8hJPkQFz zF!~&nJ_}Vo1BprD`uZaJl-DPr%J+J9!_T8nK$UNUD&Gos!DhG{eUsNWK$WlejKQnX z*Fcr8hAIyt_rN%OKUDe4p~_zdRi4-wuEQ%}E$OSF>R}Cp^;`u>My`aiOY5o?svg;; zdg4&^sD9O>`c;qWS3NPPdQ|@&sQQW7w$35R$OEvIb@aFNL+h}IbOW3v9rNiL_(!C* z-lQ)vU;m190V+KW|0U_XPv_tm>5NaO;Xfjs^yzN+719ZxZiQba9rx)5I6yk)(>3rJ z(h;97VfU@13ovXSQpei7Pv_vxq%%I9hBuN<`gAw^2herF**xi%w` zZxAxqYt!&u$W!p+FbNgkdtn{vZum*q2^H@Xa0BT!_y}x;ivP`!xmz2D%HIUFKWLDi zbUl1G=@``hp;km*19!k`sQpC*o=sZUZT7$t?1M#k7A!#7ISW4wbv;J&kc7m{wcYRx z*a=s|1bjPegFhmF6FiG_13VMzx=b2*41NykdQ3lzz+Z!94%R+Iz9N*o06z<7;q}OM z{pB;H^YAG+20x5E3)jI6JRR!#OEXMCt&1eoy6A=1A@77|l1{+WNVh?JaIKzacn0!j zsPTwHjYkvY*ko-3d>qz8j(OI`;OVdyJ_c*xCtx)^4MyO8sOvV`&+57jzP7dqdtm`S z3U&SFZBW;3o`jR|m!YoPtc7Dx^PuZCpMqKVR+xcW7lSYf({K$;L9L4j4hg{PA4 zhO)mCu0@`Jr;u)gvcDC+6?rp!3+XtN{Y~(PumP@y^-%W5AjgtxYvIYHYoP3@hB^+2 zz>{E^gJ}6*3El#WunHES{BIUM1a+Ng71VX2eJ~GCrT#G(C7tu~EbJkjfdL$Zsy_{N zyrb(!Kcb6CsQP>1Db&*q{}y>CY(t)a-LMV*5P2)qyf?!KVH_?YZ-NhyZh&2|9{ykC zF{pW|h4POY_ygqCP;nvx?}lamXecAs^{11tqX>08R)Bwvd=|b1`850i@=3UeJP!l) zAC{5l;9nxo!tJCp@Jcuc{{nd$s$VI%2_~TYvJIXLTcP~29(Izhg(b?>K-pCdt6&8F zALM2JxKTSLcqc5v|Bbu=SCMZPM&UGEKt2g&ZyvHQTRR5-7xEmGU0HYs9E5)jQ}FvR z2^U~5T!h{5Phl%myl94s7Y$Hxp&lw;#NY>Mr^d^xq2fgZZbn`@o8N(vE<(kN0*oV{ zhKdW5P;ns-9dca{`%~=I^)Ri243z)tI#?08u7AltQ&9eyg#QzHFVs5dhVsu&_&<;* zp!}f?%0FA-e@EU7<)3l*1Z;xy$Q$5alCFmbU<`f_c`f`4(ls!QFYvp_Bk<3WmpK3l z;|u%_@**rDFTgOqzyk7VD0?QM{4)>#1o;^JKcsbCPkxn!{|$Kt%C82Y{5B2$EAkYS zUnQaZwinJJ*L6S5b0?HvCE$NS-Ucw2L6-q3Zx8RU~t>pBnRKV$Ge zAv~?bJZ|Pc{6H$RqH7 zk=FIXkHHfB56E?$Q0*6>{A3#bPnd`Q1CGHtn1k~$3;#3JbwBw@63S0Hq5LEPA zo0}u{u|^;C_m|i@{?{jiM$hj zk8}bafNk(!BX5P@CEX0=Cvo_%kT=2akZyqTlY00sk;h!x%M|BpgTH3*{%>P=3-0{|WL0)OfZ*`AIAMCh}$|KZ!&6NfR7H-T=dO3gss; z_>Yj+!v99P2Fg#W;qM}kK&{_0CynGMB{+&)*C)eu3O6AigW=>{l2sfRh_F{t)yq5Py8{sAm=-ERh# z;J<}M_@7__{%fe~lk$@>C_l+S`N<%ZpQPbOus7-Dy-yj&PQeexm2>%TG%1YsiaG^ICxNlUX=|d>U$9 zPD1%f9)1=17?hvnp!_5YUqYUN-zGf>55P2h5qSzulTJeUNiWPI?}q=8bSIRbB;Y?l z-Uj~z=~gH|X@*}x9*4503Cd3z;Fpot!+%dY2IVKUa2RLo@ppQNx{F5 zJPG9|y-2@r{3H$skvBoD-v%f@sfS-c9)saJg`1F< z-saaO{5PZvQ0sCG%1^TJS;}Rg+8>1S6J4hrK(6bwY9|ThC%y0)vx zQ1e;~euyU`H8M8e-?S(%g3Pn zBnLl(JPUu1bOy>#2H{i4(@^cEp!_5WQ^CncCfUWD?K0+gT3!cQWf zh8oXFC_l-=$B~ag`AH7SPqOd;@(c{uDU_e2;bX{C@Sl=SLitHA`~>oDsP)?klDgQ>S4Gp;Xfu_3$-qFU0QxpfR9pc7OH(+mzJMQ!bgzjq1qXP@{=6ghdc|F zKLh0_x?bIbT-U4BehSJ@bY1#)U?&`f3HTChgTD=1;Xi;4P<~Pm-C_ib2-N@rm>s8mMwLfWqdyv;d`AG~u0c+ud$ZO!sq^qI) zBmy5mUVaPvWzr=mKPkd4lDgQ5-?ns@I}(CQ0uZD%1>(H$0%0=)qXXUpG06A z^73kHrv&9EMfg$V1(+p03*{%%@DAjMS-y~fF}-Y4{=JDX4i)LitHAybgIc z)OdD6`AGt9McxMGC#_I^(hRRf9)}vwCMZ8?fY%_ehw_scyc^cSIPw}8u2a~AJOVc( zFIRC~M!E#$Cq?){??Jxu7CxcLal7#Y;UMN55hBsqx!pqyB{G=6fjcIKheui`tl%F)fTad?~{G=Aj zPio*z$RqGd>@7!aUQ1AZQiN9^FF^UpER>&2!^@FRLd|O)%1_2%Bk~;7y39iPNd{hq zd=Sb{(olYqg6~J3gpZT%g$H0ad>`^o_!#K~l%KT02IQ@9Kj~&DKZ(QlB5#7dq#L08 zq#j;^JO*V?EtH?sz>ASr!+oS9P<~PlxQ>Io1m!11C_gE{dgQZEeliW^CzCLSJP$R` zV^DsQgXba7LitGs%1;L2JCLWL{Dip({Uiy`K;8>Ap41WgNhf?e@&uHh&_?Jdt*{1p zGYr=$Y(me!2!}{(8{qw<>!JK41~~)?uLo=W){vH;R6}+F;q^Z14(~&dpG-&mx`ed7 zHV?Hf`5PznlN6La9J7V&hVtJ8BvgghQDs*vY57kx#1+=Yq1wkcLjP%i41IVV^_P&x zNXvg}A*O}bQDql4hW?{^c6oh=m+yp#L%ZVejkpf1NuvFe3+s(}&x~i%GvOKcjCn>p z3+MXso;pvjdXt_B&$wsIGvZk|$CvlacqTm)o^j8ZXT-B`wlD9Q@l1LqJma1*&xmJ% ziIBZ{&x~i%GvOKcjCn>p3upQAo*B=iXTme?8S{*I7S8nLJu{w3&xB{(Gv*obEZ|h3 z{hk@mq-Vl2?iurpcox?A@}3#bq-Vl2?iurpcot6gUY z&$wsIGvZmmv%~&-W;~Og3D3A^%roLyc&jh(nej||COqSwG0%u+fnXZ8@0syTdL}&M zo-xmeXMvy_miNqfCOs3LanG1%#IwNPRbhG0jAzm_;TiXgc}6@7{9O{3_sn=EJrkaB z&zNV#vq0bt%X?-#lb#9BxM$2W;#uH0JuL5;@l1LqJma1*&xmJ%ok>{UGvk@`OnAmU zW1bPu0>_PEdC!bz(lg;1_l$W)JPYi!!t$OO&!lI6!41d&WE?o(1-&d_6hunej||COqSw zF;5Vdi$?1=Mx)yk(P)2=4x-U~Fu`^-7-L%q=Gpc~)6vz@zE!DJ(P%lk6s=md5G_R) zqd{~&x)6=BEwUY3HI9CE)g0U5s%%v>I#4xO6-4{12Ke4zm1et8RbpGHny*?NojGat zq}9=>lcrCi&66hBj+~TZJ9JX!q#!zQ(h%PVP8wvJJ$dBhXms%8p_7AX`s6{rr%xVW zTUb56dUbSi_0;NUbbNK5ZEp4G>L8k3o#T6U^$6PX>Ls?N)r)K=swbrF2S^?ZPSac1>yxy*+ga{lclGQx{LAx2G;pX8zP7 z+p#s{=m*|9_*N`fJGB-I*5=n%t;($(vqo<9XM!C~QPODlqbXw-L;nNt4(}s`@oHodI>9pkA zqtS`CPrki6+IM>D^wrVq=_72XPoFs*8`kC5MWdy4i)=Gz44)B=&YUsJcJhoVwu@(! z*=EihK67<+@T{S;n3c2AXH~6Ap4E3&>Z~AIJ~N4|eCE=b(dg7!(|lhztHgG!cDxpw zYDa2=Xr?yH_e||D+vM4OXH#o!xpsB5c=p2CtD}Xp=aI~uJ_&Mll9jZU9C!*=4_$#a9~__-5& zA3rybwtszkJ#(`@#ddc6+tJ zg(|pgKKXN`6Y$$BNf(_W!k!W^>fg(Ga!?;3a)KJ%`*cU4CR-#zs1)zR^H=h@D`yZCNwsGq5i zM*A*GU4%bgG{&}g(E{7KiwbO0@9BRJ^7o9rXLWSs;@ri|%EiMMSFIYjc<|z(i}AaQ z2axq&oMv0Nc%E%?V;`PAwQ-v5_{RLkAUe8nobRI>$Jh>BGI$BKUebTb>gduX$@i{~ zE?!bbQoLk=?aU>!Y$q<6yoAxXWP6-kaz9*n7v>rW*Pi z@ViSFFU4anEwY`xbnen1I(_LZ-={B~xpZ|j(=be)!GMF+KstIJ{SLN9bU!A=gpSikpb#-*) z1Gx|2l^>X4TmHb(2e4sNaT8I@4LSVLO|e~a$q%wp+#=tHKA7SAgZ8 zgwNdEcXM?#fAhr6c;(GSw*9T?R%~cpVmoq6?iT#9NFk?V{B8~`nO^Ewh^|A+sbU`w-vVq(YbB& ze4pD^U^{+${&s4;ef0L#(cwF?cM$RJ$lO7jcMPyyx;@Eu;r7z)jK=K?Fn|Ds$$@e?Q?_@Ub%-uP9C$o9y2(sZjvv*Qr`^0v7wtZ|nv$=g_ zdu}_kxjl<4vwfKD;`TDz>Gqj+X7jGpU9`7jX$P~pqqJjj2lnq+*n#~!ifprYjoig- zCQ1n`Ps}Hn&BSbCF2QUjW{^!Krr8ee9NNij?p)kSxt+zG%;wIyorRsu=FVAU(>rI_ zj@>7bCu_z&3sFz`e}oy(zZ2dq>&M z-CMYq*}QM|K4$a2sr#7C`||fq+{bL*H;!!dzA?6`&i+m;?;K&f*jZ*f-&yQrHaq9} zKG#`bJH9)=n_72|?q)Xc&)&~$-k-UjHt!!`yR0t8ft>TZi)_pHFCj19 zzi>aZd4G}bh5P5(=DJ3^n9Z)CE@rbU%{JN9*TrmpJjwU+$Cp0NY<5kNC*L)}cHn`* z2N=5trXL{g?U~!d_dU~lh}?(NAV8{0F!hu(J2cGKJLscw4Po$sFLrnlYW z$VR)z*rp!pe+c^?Dn5h-dx!R7!QS*<_OW~W_NMk?!^25r<%gFZrq_G(ds$2S`t~t0 zJ>{ONRmGl#o>C9J=_&Tmo1S^LBl~juSQ(F`9--X6rG2c7eWiVi`xuLT3zVGSS7bZ& z$Ta$aM+YCRj?O$f`zSH?(IvK--r-(s*q`3dK5731+tU6;w!@#ueu92%nNw$Lrh7K?m2gcddxG`;#4y|bC(>+}9#1|IL>C`lQVoxn z*`_|(|4DlI#1it-6J_$0o>*i%o(%HIfOU}!vdI7olEIMJpA6E;Ac*!Q1HNKSGDs=k zOfr}ir;@?6nEF)E|0(qOPX!a7lKjbF`bnjq3?`los#c9Y8H_y{j6X?LBTojT81g5B z+>>O@_XQJuq(}RLu|Be9`+||aAlFCMOkY5XKJ^8|VyQ1!6sLYAnEn-tCVw^P`&H75 zpAO2OrfBig!NRA5(x)j}_;f&u>HTytFAo1|ko{GP%%_54iu7zMm`hP)Diutpf|(RW zCQ<<@<{%YJis`3u}=NgGq7j*MoxC|G6OjIhg!h(Dym&%X}^vR(kPsL0RdAbWqYCQ-yRepT^XgbTFF^ z=F*rtl@3TTcIjYR%sd?oKMlv84#vgQ=Y#&w!^NkA^3&vBcsk%KtL^EaBu;-mn2~~7;xhqXY4@3+EKWQdOg>A!BhLo8XGv$C4Ti;mXM@3K$>0BMz*p*dHb^Vq+_OPJ zoOw2w6|-LmM!tZ)@P%Oh3#1nYgVLbVgTefuhGa099Sr6MscLF4AjOaj2GfJe`o*C1 zMP>bBF#knm{bDft#bEA>%KF8C6j{F*Op61*5e)tYMao0L(h%u|p`bKGk-|_gKNJ*) zC^9<~kYc3`1#{xaZw9&FgsI;O`hSa}If@QF7m(uk z=eZyw7M=^{pQFg|^Fj7`(u2>YBn3pWrIQ%(eR5w_C>NRycCpP zlIo>k?xmpc5~AE!gVC>&W$>%P&{t9Qe>F&dH5ibne;kkb9%2-4#CH-fx4 z^^IU!T=+&%66d}V6vUBSkjueLE*KUka>1lHmJ7zk>0B@)7IMM7n0h(re;F?2g5=Be zXE7J>mG*N%SsZ*h7?M8waxfxJzZ}en6E6po%0K>cz*p>gImn9(F9#*D@NzIO_WgE{ z`fXT#IapG9=(mH6(&^t02DlOCi>+L&;Y88$=T^?YADaH&m6qnTc=-7aU)FMtE6+_4 z`rLGZ>6o4VcgY;Rizm@a%YdMFPx!VMKUx)I~ z`uv$X%RlGypL2<&OFq3vEC`9admCX63Vf48C%!&j82We_S|6QW4?UqX415m_3gbJH+{X=AGpr+lV1N9AFy=Mr#JcX)w{y# zBYLj2+V8Eh^gDce4ZgmQG@8EO>;KEUExnaBqR+*?y?S5Y&Uc!A#Owc8lcjaPhCYcn zY5Lma>u-Cn>2?2yJ|BITr5AmA@(N4Ob6#7Y@1O7UGY0xxezB#ye0n$6@6?}0&co~T z?TdVVpT3>zW74nTJik7ddw*N!(|Yc{^tyjbpA8pSdb>~Gb)KdBefo6oKjS|At`DQ9 z?^(aT_SISblGp$5jh3$BI)y%u#VkGU(`)>Cui-j~K0_Zu&sbFZ^>DRc-!1=-z4w8y z>Z;O)&rN6vZA#NNg|yAcEybF)6q67jM9wsUKtr3B6jF;yOw!~+(uO1?frdhD+M;h- z;uti}L}hHWsG*{hap=6LXyX($wm8y}8Kc8!+A>Z$Wqfg@nktmEU+yQ$b`|5BL;5cn-zB7fGh5~5y#U!3 zGQI<(C#dfT>1%I+JmNdV_}1!EZdGAKHD=NT8e7hOXOK(?tkn~Q*cK~@S+mZRI zZ$IgeGQLgFYi*40QPO)EUwN-gwrPxS7in3%MC{9kow8LizUkcp_;!&NKM217cFA^H_BWuHkZxI|w7iEZ+Y5~E4$|q2?|#z9sP8yw z`EH)X_Wa_WvO1HxU=<-yzb4%RnQ(BaG)`f3Ea6>Gv|e`_XUNKFauxklx7nc3r3ZAHc7o zf1GqT<6Dn@%l6XE%I_n6^zBOL0%dy#;~PfYW!nNfMPDE3XBgi>(o@;LR@fn17USDT z`YiHZ>X+px|HF)L1?e+vuby<6@$DzQ3vrbCBcwMmz7wQ>bSwB5VB9gjThVWkp9?!= zlXdTfE++jI=F0@>ix}T>^h>t)E{8ng8?ICOUdFeZeEEKX=qtrMCfnoe{}Iv;FutRt zfBY`Ur(yrIUgi6Xl};o5Db5G`VW(_Q=Bxe@(m#ctrM)i1O*VO-Sm<%m)9m{%sh_z&?NVSHb=^_zsdDWPD4}PuYG6yF_0>kf?ZOQ8?(&1HQ5n(^%?|4WQ-Pa2MA*DJr0`G0(k(vP!0 z*>@}b1oQVe=?56!6QqC4_?;sCB<2~(moetczp?)CyH($I*dg(0pno2{Ui&vn{x0}g z_^sqWPk)^x|GyA-$)958&rdPVM81>q2j{82j(fDdOBjzX^1sCPddQb~RqA(>|8v&g zL;e8bB>Z0TZ)5(2*Q)*xi#_1?lm9U57n0vc`v%DW1jj=K`ByE|`h(=(js1eeyPy1e z%$uTri2Q2i(-`?bm;EXD2gvVcK26ws**}6mOnx@=IpaNQ{{f6!;g692cH}A6VKH*Z zUvgzU{dtTdOW*so{v*gsk^ghbKPY_At4UvoaXftr_Ak^g>*`Lw81^=6{p~A2BVX+6 zWAYtgsbA0fS7Dx&c-z;%{#xQW74u)6)<2sOPgmap8u?vTulx^+JoGhE-^&tj&<~LQ zy5t+^U8HBS{Q=S|*j|L~eO&6J{Z`UHmi$2dzoGp1B_BXPL;9N%f6)Jj^r>Q{e?xkf zj0fl&qP|(Q=fd}@{Sy)&@V`#J^%4v8Ws(10$q(?qN&Yd(2hh)v{=UQ?^4C-T%hdNB z^8aC}^6w!3x6;39I3HfG?cX5rchK^ERMGc-%73T`^hJo*29>`;;?Y;^8Zuv2lXE(f1B73{-fk?7XKliGVa&*Z(@Gg^zS5pVE@RToIH|Xo6zUxW%lm2h9Hyz`pN85Ws;)OPIKCJWz+aI9(Rnt}9 z0n)F^{0sX(%l72Eq(Xm%^gZGq^!I7n_wO5BT3Ef90)O{{Z0p9TzaeO|25_1d)U(cRpieV`_TTy?^F5j(!O_){}}DNk^Bp3-vaV~Y0pRGKTP{p zlTM-k){(BEeZ{OV$5GP$W8~k>c*@J8viAWWGRu?xa5XF0<(S zOY(1L{Av^{9Qmiy!M_;ezgg`$ovHQzhWzi`uJkjc|Aq1c zl>ZRvt~RZ|SnQvQesMl{SnLJ;D(TOQeHcIA=J@%kj5pY~p7tGpEn<(nq$}HdsK0^q zKD#~A8`!^1tpAHURNo_{Ys7x&?;-tzGNu2D^lcI!@V`L%s1{(hPG5vE!=6l%p9kzEH zeoK2J)c+r(bK6yaq1_(oUiRlj)}KrJUm|@}=3nSIL3*3a*PwqxdKu$0wNdpyyGr?2 zl9u(t#hzZax0?RHj{IxLAKa|q|kSe;3v(y^8zWX{5g`^C8-lm%L@0P5Ua@-m#GC zTT6d!rT$^k&)e9tAHo ze?sXMVh{LvMn@4H>~_mH2?{&$i8 zsPq@@e}(*6(*8ww_nG_^si2XML+tO*Naub??U~2^l#za_MC*T=^&|AhDD}%nAf&z5 zN%zn{7gnjhLo)w?e+B6~*uR@dAC>tJ{5wdmVE^h#KU1dde}(P0(B59!m&^XPQ+{Nb z%72~m@_x4Tx9f4Wr$YLJ_8z1Bh3wBqN&gBwk37|x z{2tQFmV-uq-Ol{Fi~Z?m|NeC;_!r^)>ppGo1JWMo!=xAdxzax&{Ta?@V^663voe0s z-~DXwiyXgiP`-)sh4kM#>EER|pZ~DdpAdgI=pTu_8Mwdw7s_uDe_n?3Bhp8q?^5Jw z#l6aZ6TgKXs#Uu2LZxd+e+uyzzCro`@*llKY5V;CGZ!h{TBG_dOIJEfTGlg> z`opC0uC+01I<5z>KVOBdLhmPCO}dwKCDaOkH|dYyx6u8hFG1gh?jyaE^}9$v#QHs? zpJDxO(gm!)gYkpA`Wc_`l{~E{R(*7ROKSykY9wdDs^|g{l_bvW*(o3nY zlk_)Pzk~GV%T?Y!|B(0UMgJ1=D=A+{`bX?f0qLt*zk>7++5S4x7qI?X(jQ{|O47Ho zelh7YtY1!gF6)<({xa*Al0L!y*N`4${U*}?#{M^uu44Uq(!XQ=b}}E|c7f*8DD!bA z^S7Jy*O|XLl&@s|7LxzeC0c)?8iru|2j*{ojYS&snZISES2KUdNpHUb{=Z}(dj9`u zq0s|C>7yX>e-qld%)c3N>zFn^R+g$cs=;GJ9=#SnWuV2=QbhbC@YR{-%we+@S)hpj! zvbuUs_3C9Moz=HiEM3vmQrl6|)>gZ9UUgk!X3OiFa}lA?5e{$!QCMQS@D^=zS5ylhmf z^qlI7)vGG1%U7*fQ)~u<%V|iuSs?x-g zQN@Xbt&CBfeksGALqka-A?>kBpx2eFmaQsZ+51SKi1utiybn_TpS=8=Q@u}tj)x7elB**pp$a4ue`sTeMJKu9VhMU zYOh4B<1uH)NhNc4pm3ZceWYS72TR6v;?6a9Z0@MFcUC^ro?AdoPdKNzcMv`Sgq?Bo zxVK82mG(Y8KrGhln7_TEPN&CtCrC7=c2eSgY3$g$`*^`S9sA1rr?IbSU>e6s`})=^ z5$k`P^{S`f_}c1;PLozQqK03_xHB1XrQ~}q<#JEm5B8`=I}VFWF8L`j`O6C zRIKG>l73D; zxx@c-D0*ax)68J0=)qA!DaRf?p`2t??4&Wc)k%su5;S46G1o@LTJF8CuDZ6Vsim&E zvANN9k!IJN>O0r0y7#_Fon0IYgol;D_giY4Hb>f%IzN$^!~2QE;@qD|FwXyp1f2m$ zBo`NfM1sx`P_C(ZUVBHIzki$;L2B*u_Lcx)MjtfjuX=il2!m&|N(%v3v>u79gE~{;>Z;Buua=~R|tAP=-vKeu0#HE!D zn>R+9JC=9WMOsBvc&lrhH$>t}?y$z5qbP1BzH^O(qqD2EKd~-U@M21J<$Ee{?d8rj z%UW8uHa2gF?Z)jxy4dvt3}EZ9-6LFHvUGX5T=-GVGQ3^t{CO^Gc`mZ1qs=k|7nx*c zS#P;#W6PF^QEgpuDvhkK-Q3hsy|Vq?;wt>N87!TOkSlGGrbunOT@s~KAhNNwV=FEQ z)-^z2eOmV(auqM)6Z$vLsmDqX7 zOueI}zHxnH1QM}X(SKMvMw%7uS_$xq&CPX2Q+4&`=H}Xsk^1Vo^%&OhLp61*Szf+; zS>>wL)vK4USiX8WE}gBZhP>rMc(>)u8tNiIlJcOIeO-1d7RxAkqM~0w!K3SX`Eek zc%eShUf0%WP1sI}SULm<#C#GU;B-xZh|@y>A`V9dNI3l!AmVUafP}*#>bH`nCVzMM zvSZHhWjkHrE8uj5uYki1zI>+>d<7gX@Z~!k&`{gn;NN#QC)RB@$Jt{y&)Hcw&(Tje z%h^RY&(S+K%h9oV-8=jD&4(H5o)6R6Lm#$hIQqyq9pS@v_`-+lbcYYy;SnEhY;?!( z%@geL?rH33unu0LyM8$YD6(%<7WsQsW~p4;Wib4R4DxwdI_q`mRMNM*}1-VzOL zX%gbGh)znoGscsu;Ed>`Dy)p27KbM#?~MASDxd*g?Ml=@@G+1C&*Ax)V9b@)u~ML% z(}#&Aoz6=v>F`=&L8rSCOFI1ICm1_Uipx+7ImwPVXcXbT}uW zoYOZ61s$$QC>NVlYU}G)*FIpK<+QJ8Ytd&F;>NARix5sE8S`TjL8m8^h&p|lMAYHU zB$7^lCJ}Xb)K@Y#%A9_ZXBIe@Zq`RN;L@0=!~IDl9WG5G8Ryd^vT;sLBJ1>O645xf zCXsde)mJn&JROd;liJF{5~&xLsxiMN5sbMtiJ;S~NkpAaO(N>>X%b1NOOuE?Jeow( zbHLNsESHS*+@q+rV`X!Fq%+X5B5F?=zQS?w5@{=1zM{^6C6;tXEU{#K$P&xO$1JgI zT+kAW#z!r&Y+Tq9i+bXQOF_5*Ug8rlkEotWXo&qPID)oCE_wOM#G(}_5(`tHh%-Kc zLe9Vh3OOPYDB}!8ppYX5fikhqw{d^z=OTw2Bb{{(a!K{wPIo?_hvv!rd+v2(jz9Oh z&WN0QZD**?y|yEU=U&+v+;gw(i2Au#b_807R$Q3<&#;mR#zL4x&>62JqR!wX5p_f& ziKNr*Nkkn!P9o`F>li^T8JW&ffjaihYgvRrYVj3jJ| ziX}UPX{?XG;^aRKI&vgY1xKbdwlCkbxwa{;OR@d2A2*g6eq3ip`0<^Y;Kz4lfFIi# zc|X1*;(qK{&mx@rBGGwBrog4RZFzs3FC4ClRLy8*B(;;|O_JJ)lPF2$BpH>YcH;C(QaNd^-R;b;bDgs7L!-F) zC6*rC>BJrE%09==)PQQ(iTFx5k~T=Q)l`7m$=8V`>E!D;lXLR5oJl$PT8?C#d?jZR zPQI2S`I1$NC66Oe(QEE@WMWb=FDDRkx;25A(~k+n9F9vM<@8hnF^6kBQoh$C?E|)e z{PblzoW#rNakb;z<0la37(W50OZ@n8&hQg(y1|d{NQOE**Codo?`>(0#C0P!Wdw@E z91$qubVi_%XF?2=bh<83$l<;~8K(;ag&b}Sl=0ZUQQw$Z7Vp=lj*YF0{hz0rm$x{| zaXfrA&%ZRUFe)7XSk}BiM&2TC6?v?$dR`zSZ(&fCKt|qzpeiv&?6EM%BV+UO3cNL~ zC(Y&sGVwoXc(b`O(&$-p@m^-JI%qQ31b4@8;#_ zMb$| zp^ozPL*629JvGpWK?f}is^Y_7l?C1^$_ZrT6?m&K>%0url|NrN8oAg{{7TUO{Cuh7 zq1NT+c{y}~#qnz@TEq1u=)C;gD9b+s^B|DGO4LS@EecFB;aL7!9Q~D7 z7$hFZ$Xn#CqFoMTf`3V(*>sH95$QO=g zruEZzrc0dlq{7#CnT`!XSlU zB&xT_TaRW&8F4#^h0$VHhXWaT3%pgd2_FWXP!LqbhoM#S=SvmNQbDyLlfW1G`BKlv z^?6_Wr6!BAsk({S9A^Yii+daJ4(F-BZ^E{MuIi({p(q|GZ3d7qG5<&HNow9)+eNezaC zufk|e-{XaRtfy{gul3V+CWDORnQeo=Ca~SJrJZbE4r3a`$j|k1>^$>wR2j-)WP!&B zHnq4QNywuDzTL|!4AShwps9<3s>B$v{EIt8Ul>%=V)&#@-U4q8wIYy_S0GiaL5E)n z#%})nsO|uBEdfhYOf;JC%;0>xjhGJCkc&$A1^`?u38S{S8_0`toFgy~#Yu*Gv|d!B zdM-aVh?56S0*9d*GI72q6PXr^qnwy4oY9Z*qXsdD3!^oo9Mu`f$XgUtC6JM~FsO>f z&^_G(uM9__m%(lq%unbuZ#^c?d@qL@!ATH>#N7P+s9OIhw9vwK-}MUE#w~W6@HJycWzC=GJU(jkM)k_TzswBC1Pk+;^2Ul@*Uxv`&N{jL8mEGV!z`2`DdwWQ`EES$Nlr9M)>vblA$ zybK?y-8ip>E1Uk{xBvAfmP)mj;*|feEUPgp|Ap~~?JE402_3MdS=Qk@0F<$5EMIEK zAC`f&w(9DRNM{ELKy`KH^0k#Q+Ws}n3Zf57z8c1jhVhEv6r)SP-m0tX^i6sexb@k3 zGP%g;ff;s9MN7A%2>SSHz7|$jmzGqP!~{mM{;g~^)OTd2VPJ_`BP-7^T&gWO=#$k4 ztl#pVTVEm8JC!Xd5HQ#{bvTwh&XUh0$|q`n%NyT@!QVzLHw4#F#bmH$57(e>;!^{dp-)32u*Pa}rCX`$jby#Q6I1y=NgYGl zpl+1Qr#*MQl6qydjkJ)lYy2@o^(_PMI1g*jPC2s^X8{aP2>TvLKZC^fPZWc9p$(%OcFNvMcPp z7CtsE>H4VjPjrqUK4-QqMVlq)|8ti8XSO}N_e?6}AaiEhx2!U;&9B_uS05iuyhR@! z@bl4RZ1C~7udcUhyVC~b-xbCM#kbjhisu!747}(4c(d@y;jyVtUTyQ-woDr+-a9Ka z?X@oCXXEtZH{|zA_>GvYet5R=RgGIL=j^e03VGTAzQk_WFc#S{A5Pbpcg!xf>I#4C z%-IEg{8!JOooTmUi#Ee(Q}TLRy4o=|^U16I@?3OE`)Q}Ee>jLwLDZnl5OPe)W!z_` z8vDggnG>W8zO&{4`E8Gli%?dAv6&K@w(}Lp?XdG4<1H$%(xG#PnfKSE8I`|=&EYho0H4n6eI~`&HI_QV z%IjHa#-^SWnWv3{SyPNnI9RTfve1P(cZ+|eu8fmz-#k%08vhwPaj2I$E8W;MjJbbS zhOwz86_{ykdITtWW&6;Q$9@IJ=b83=fYSo}R(-ad?dxxxJ^NQUuCQdI^S0z1_i0nW z8^#!TiTh+*r`3P>N%~MTadtt274J;{c&qdVotsjk^Nr??=9xXFByY|>XS^7{-%W`g2aR24 z?6va_F}3!KGS0_v>|^_+>uuQgGB=}T7y6XRymEPbKhU;y%;B}cc-qL;V;9ja$78?p zvBmbQt;YykHfE;f$XG}@hp~`?<3D>mtIvwljD_NJ$=PxA8FTIGSr-_a@V&XpS<{S7 zUC8rUml&JEQ-D_*n@W)5HM5OfcCN{snueUj=}RWYQGwLG7UMx=qB$or$T@sMCR56# zt)O{S`1bz8(k=T0D__-4vw*UzXO!H=@pF2NS@B6L)f3X_Z3n%6Y;d$95#%CmnEkh`^WIvsf+9!wewq! z18QZCf{j*LM*Ke6z&={aeaF@j8Cxm-W6PD3cE3|HIJO3Y#ujn{`4)}kg*Y}wJfp|P zh>f!kZa+xfXnbOI_4wFT*UoDjqsLm%A@K-0mI|;>k4s%`Owv9*o^;i9+v7gQbMwVs z*zL1Vnu2kd`t+|;Q>=Y!T2{cm>1vD{**9G+^T~|pen8H%?A#nfF20PMl4B|HQG?cb zQR=vJe4_dw&H+L=7r?p4w$#06FfW~g9k09Wc+9nrv~xIUpVsixlD#uRGj{$gWu|9N z--5iOO;$hr?2aCjIBb6a_0DWt23wcH=90Y=)rYL}l;3XnI(~;xmI5C7unp(8sUh^^ z0`y}Fe46_7ZzkEF3Gt)PKDy!OC3~|%SvzN?Ughaer>j3>sWWlBdj(=J12MSr>Em`B zFc%^C1tH7>*J93){j57aTdh8pq94o9pQY$m$==^?_;&m`nj`lmm;7=}=7XT}|4F|4 z)%BSNe8-vF9>m@MJc|8GGvd#s>;*~P7q*v2m(~rExErgiv^B}9xXatxTH0=pY&C*J zErHUOjkS%<2AA*+KhTKFhb?UzYdf%7Sh%Bh!#txT{9vT5C5&Zao9e@OoABO9Tet7FpF6S9|Vmj&!zS^^i!tR1V`}e7)g!fqrAOMTkXzzq_{0dQSpsU;);< zt;bu7_2CU|Et^{r{@6Cc>r!?Jax89h^KXpf;<}Qn{&h+CS0~}$l!WhMd)nva?WmaB zJ{*I`ZEn6?KXYzwzI6@GA>ih_+jodsKbs=W8#)?{`o=n0^RKqedOL?Np%x)ukY@G* z1qQA_x3tOIJDOT}4Zl8uw^Jhs?fRA?1B+tl>d+!`C$23QAxYY?kYG%tt+o+sA=@u> zFWuS^X)jU>im?x}4@m)c7@mSgSb)|U2037XNmxxJyhabsgg5z1Qm z%j)yp%^i(RR)Hbv^z|s~r}a*j^-EuJvVQ6i6-nz|Bo%DzjMU??J1Jcuzws1@{Fb+| ztRJ{Bhb~w@ZMe97`ywkoFzxZg)hgje9q*$9miRM5BAi{QsH`+T|F<`31$fMTNz9 zc6v+RLiuSl61U{%3Jua&>i9|ST{fx$kj9qS&w6s#6E_i8649R3)|UAeN|R^;FS~4+ zpHF*WnX#pyFh&)Fh1>bte0E>faxvy?Yw*mzakl2c8Z1&e`z{g{E$#TtIm8-TAaVCl zhUe_Y*=-T9WQdnyE^L^6*F#3}>^s-UTr}It8@cC!W2eAPcscwF6q*D>+QF@{^V@k3 z{C~;=Ve|&q58Bxq2@a$L!W7Sgb$a^8ccv&{ey}LC?W}hE2HC62D2gY*H6L zJ^Xb^_fZ&e;|qe!%6B- zkwm~2q<;zdydCc8|Jo${jwJkE^5w>kUwp%CagJrBH>FF--xE-OocwhG{8B#G)EU4( zNdEQ!ejy)j82}$$VQV0`jglW$n$fVic&>{49(3NX{tgbpGF0*7A10sR(SMW-JOd`1 zhd)NX%mse_&%h0J*>>R1kKadrSpfeS`9by_CqIn(*gW=4xkwpJ0sd*_iC-zi{OsFL z{v1`~(LYLlkbPt1OaJ`*pF_t6)z2kA$p1^oSQ21=XA=H?@`Ktxn1p|b{4Dj4Cx4G6 zX+I0!X_qa?{z~%0mXvpVbdVp^eh>NGsPE_hOx#qE?GXNONyB6RDEZ^y2<>?(vj&E1KJZM^Ii#ejzao6n$Bwhv5HXpmzh|gYtewa|clL;@*yh z6+o$1M*0*8sdpSG^~MyAP8yMbbF7x0_F8X*4NoQwaYpQrGj;1T>M<-^1S z#9ko&-CRjb2a4VSjC*On2lyOt2XHRz=u$Kr$X^S@wYBm}MY95U1L$%^vkaI6x>V6D z28!M#z;~d$P|+*^UJp7~(aZvhz3D))X9`gKZz!54F)xZA#({VM;{V7ZkE>*bAdlZ`5fR;paB&53Cz#Z?kPoc3@H8>0sb3s7&ln()=U-iUwKpw%AG zY7Z#wWs*(@VoEGeQ#56!l{n)*wIzQ7h$*cc_s9j!F`$f-qd?KKomfLGC1wGo-7y?_ z2o3{9ULKeddL3~I%jJAR_;ZMIL?rf3V9;P{EI*}a9s){zIlmG4R?>yUFz`nxKaKsy zs9`isC{~>Wir-EEF_n~$E1Jhy4xSACa?}wt(MK6l<(TUQ&BH(piSiLea~LT4_5sn= z@;$(*zyU>bH&FO}K%`cAucFxlM2O3~70n$$xV*ee(QE}uy>&p*TLF9#{U}$gD%Enc z7zh`YFHtlLfl@CQ2p5#kRW!pu@k6#^RRx$&!KKAOJPbNrp3VI%Q2Zuz2CyC|N`yX% zdUe1tpwLHv?*$H%9s<4_^Z@Bz;N76RNVftjLD!J30NxI|m~<}iHqc?x8NgdX1JoWM z{5L%f2>)8;h>xYe0oaN14M1sUJrHF^1Xu&S7q}K!3zT+IHoXjp>$X<=?*oe8-UAdn zAUiz=cn>fPTm#Gkt_F%-cLCFY&}G?i2TUL zKL+dqeiYaVG=Z%^JS;K20oV(y0lo{k7Wffh1@JF`Wxyg}F;Mgu0(*eDz{S8EU?(sP z+zHGAB39EgfMRDF@Nu94Tm+Q)=|SL0U^fuYF&hhkV?go4DDZy)j{p|{M}XqDVc-XW z2Y>~@A)x3P1nvM10OteyfsX-ufsX>af%(8L;3L3JU>C3zm;hs= zv&R2MU@K@D&&9wNU?K26U>5LdU>}0>i-}>PK^#MT@bki9;sCLWSVJr( zhKT^HUP{W6;*^xRxl>ZJ!*iyjmRD3xNu3Ct4t1rZq>P77gr=s9hK_|&QjUaq$#V>7TZH zT1v`}Y2DMNrW~C%ir>8#_g$Qla{8h!{NJ74lb(`t>f(uuQ7iofejm8x;3X+3gO}{P z1SOZ8!2gG*ADIq&GLB_H^0JA`Qc{L4-+y^Z%AU&y@&EYcC-DE#%SZA5!7GNZ0QZXh zSHO}hCh+^{m7`ZqP3fAkV+LBEF^c~W&p0w8CFRhJ5&VB(#=#k(l%W|1@Ox;+e*E7x zbH~h-l(B1$UjxhkZ1s$izLkhkAg zJOw>sPcQlJ!nhIp?dzUjgkGWh?LOJSf1t0B^o^JigkDSf|A;*7$)G*I6Z_F# zKiiuSe?cD4MOoWnpvY%Y{xbS&fb!|0AN2=GkFkC~>wgI2R_wK}i(YQ~3n<&(ip)k);B~SYzosJu|wKFLjJ$eKcl3dXa5h-KmWmajgtN> z?Xj*ELsC-oB2xjr#2CmYd19uT%bx z{kN}6{xfO&I^-wOj9-zR>Tj2F~-ne|IW9`q%gKi(_j9dm&FzOalV zCtc6+*h>H0!})50^itNphvRLj4}zlk`8x_(lCgte++ILBB#eQ|g132f$^!mi4!?{=bMn!JiE~WP7jJiyX1vAD)0O zC8x^d#1GqQsgInn=YyA_SL)}o{#o%4=mn&I$Ns;I^mp0+Wu%V-r9U60J$qz+0sR!| zpV0o*l>cXGANqSpf0_FFNxvZaz<-K#wd5OewhMBy{kz1|LGP6IApaA}XUP5oIcL95 z`~#79&@zvUKZfOi5Zf~P$EL57`4{{#^8Zo%f%<#c{!P=e@`@sKq@`uDe`0obtx1(I>#iSd=KFFV>{3pad@Jq>mQ0xO=_Jgv0l=*un z`MYV~dq{tT_Q^#$OP==Ck^cn!w~_Q;(Y~#u>%=~^m&W?5=)Vt=-$ncWf^;qIle~~k z#*Ntj3GyFfe6z^k!uWoZ{H=`d4did4eg8)O2N>T)&Y)f zx%l@hY0y(KK=TWk~ia(+4`^@q{yU_N1>D7=E+P=@6If{eum(X&ZLbiRBx9{WQ+J(ip@7v!;x{&?< zH0`yY8~6b2v7eJDr#<#_5evw-pMyvxU#=s`b~XFkOZqa(+s}>sjP}~kp?nT?#Q(+g z|5Wrt`eSd4Ks!j|ov_N(YTUNI^YtLJ{2S!%D2bl@F>NWJwWw?3484izd}@EEi8%y6=*KJPw6 z`(wTA0Aui^xx5n*EqB{n!AGNmC9GaJ9+?hSWj}E3ahyIm&tk0qRKlf@;8UAr$H8r% z_*0+C4s6%s^5DKKi+^gIi$BHWSId+5cyt?&oLdg`^u)ikq+LEai^p5-r{|-#-h=l` zR#@+s+!~Q*VXcSZ^{M$-tL{F>-gKn#z(pmo2Y;cggCNB}>bfuL-P-rHia(vF223Ol5ViRi%@Wk2aFLiq(Rh zGeMc+lU~81PRFiZ1>YtUjb&{T@gPOwW$W#L>gsAdPhWR`bzQ^#)$8$sg;Bi*uS~>- zJ)XIbv^CeFJdrMYlHrR6&JM|3Wf$8jVu^T1?fnsXgCeRu##-5quMss`?^i^*e%WGG zx5kn@1kSZYYd3eav_+aQ<=7=r%4>v<25Gs{S|7=l@N13lXK;<7$i~)=t<`n4cmV{t zSG{b-GFh3cwpo@!!y6aNWXY=45xl2>S7$7Vn6o!*u5GK2U->lV1$WCU`96uR)K%Mr z&josB19^a*g)1>s5~R^Q``rn@We+E*0v(aJFf`JBtL%B!F|^jmvrBdSSPSaeMKbba zjO%KCcBv(3eQU+Bj;6Ek-tVv&vF|#${TOz=a^w7(rAHPMug>;KrL4VDLMi)2poBuw zpv-+qg=FX^719(;Du&^cR7R6Np_t=EtAuiVGb*7B-vUc0Bfd)ZE>ltk_8Usxm%b8K zjlRK^Sjs*EiTj9_C(GRLjwRG7y}skz-o8qxPok!;#3dB7-v+ea*Q+vOUx)Mbrak@* zxrAD!GuA6zNu~7tx1?h7uGqqYq+-^)W|NS!-$P5P5Y5idPb#JFy(JaX7v7SJ@rAjh za`u~bRn`imfuDx*GIUAPz)y(zd?<_yz6PnnN56vi7V=>)$5DfKQlDpiuFG1P)%rY_ z^$TCiN}w|6dso^s>$6sVi>Ug1)=GO8_<1Uyk5Fk1pHEFG17Dg-V0e=6N6818d={DY z`3jUamiUuQi@o3CVd%XK`XPuB_#q{HE-68GeLjk$4F!MPNG#9{urh9p+r z^?njYTUZ>#z?XsCOV6uUe7*^!44-fOXd6CX@=*r9*OS02NxsJ;_W312LY=4tX9Rtn zWU-gQOz<+a2Rej1H)Wy~RFn117{6gEpMsG!mBltqg&-?c z(EFhbZ2;d?aYtGiJ|9g{20o2ay&C&2>+`ob%*Q?__{d!Zt=&>$5x(J_@I*&w^P6$V=;&at-f64h7M6MuilR#;u#)QF3wwM~Uu;?~zd zyISoEORe_|YzpSZZ^^z#FiL&@#cHk4=iH?4&Mn#Z2u7Bu^e`*cEK~a`&|kS7?cS1o zkDwb5hs!oZL&Bx1ZwUH$>sST&Kd-IIdiB2FBPc9Z!9$W1*p94FwEy?^*Sb9s_&oxn zOlzLx&AR?O)xL%gThF`8w-Sl=8A9P?-87&zA;XK z-L&r{?Axn~<3gN|GRZA<_obfi5ftvmGb><9C6SwW3h!4T)@k3uvvYXy1dqVT`#sjS z3FY#4E2slNK59em`kI;6`kM9{r_h`E3f7mjZB5bj&oj@a%J&xH*VdFZK7U17 zu{~&=;jR=}XIQ_B(1mq7ZQaqbxOIjphjoU>uj*9D;yTyPgl*RP!`?Mx zC+83OW`yVz-L`GI=BV1@TxV4L6Tfa`^0k?#VeQ#)C~c>#V;M5mf7;MBA>;Je>v&J& zy7`abbvnEMvMzQbzS(dQzmZVTm{ZvJSzTY>E%*4FWk0+RvKK@4e)w=@LfMLQkX?HY zvNh)*+i(uDt>+-y>5;W_T;lNRb>G@Ae-fpoveR8gSDGwti=20`nUP{=OxS!wtw8? z)6V%@jFeyAi+q0_V-P!y%)s2f_o{riFFyX}tv`J7^5S2f!u+!I&9jw3b)Dm90mg(K zM|+*<_%WSo&7)(mJ>=+X7uWU{lr?c}e@PxV`}zCSRbosm68+b1$NI6r->2>%UDy5| z`|Qk>ME;AnMd$5lSPx&;3zs?0uG@{ZxgW0^`Ct% z^x|(fq+l)MRIF({1#27Q+e6zf#J6rjyJWpX7l7HW^d0hI+%jC3|OvvR*iwa^-9PfVJK~i~VOz-xW`%VxN2*`F>{G3bel* zc9g=NWmsEz>0a2kOY-FGrN$duA1$8vEWTM1PR-gW`6=tVOB*+VAJaD@YM-_KHI74O zVqg9e)_oS)S;m#GJu=m(jLGX-(23fP$?A6-qV!o~?`+s|yw2ElHdWXBeg*A(5dOK4 z-&q;Mqp;(b%9`+3zE+FAH^|!hp)0L5@`Kux`CQttk9q9l9MLlk<=3J=n z=RQ-{xxYc@v)3Vi9LH3Cy6~O_juB>HEo}8mv`;>I$5ON4Yt{X^8&$Wgp`T#>+w_*_ zzYk1ZgZXdMA8P(9@tF&veMcYVILSE=`izY4f$A^+LGFHVl*{|92O-!fcy&QMyT;tUhn`?f* zK1*a%GXKAIo1FVI`}fO;fw$l4L-~GA$Pp9!cv|p}wXclNy;$Er)J`` zD(jrJCiKvwx7o)&LAE)4X4}^Apw#`?Z9+Tqt!(1qABN66`utnc`CF;`vFF`7&pwv= z;;Yw9+c;5u5%wb&V}Fv4{n;h>W?U-18K*wngYTZmw+EbKLB>gVsx?r|onOE{_?^ZZFTWz+FH$|1bN?piq~lYy9`+4q@D8Zg zrZ3=}7T*YbdA{*gyMHfVr@F+RGu!TfPs-t&+p&+mZSSkst=tHoS?8N+8Q8y|jT`Xo zmuVsW&WM3;?8*Mf0MFJZV@70C@I4!Qe>9ewW{suu{{KDrAK&M({ojT0dEWneeD{aW z|M)pgj*mjt7;dP+Qt%kAHF+ z#?p=050&8>Bi8k?zMtjRneNg#i#lgf=Pc@+1)Ty*$2ykCL9AEe?>_w9 zgjhX{`0vE;`!A4qmucJ)D~)@~rnEf&xI6V+?9wAKm;8~Kiyn!&0X^qq{ik!>cz(?N z@y1;A_}%CD@q8TqcyY+VxpLY=kKE?m2c|z1zYm-h(tY5p%XA-z?}Bd1!5rqd?@P1v zb)B=mF!Y_D3;(!tq5JqW2iG`^Gmktk=iZ6VBhoCnE|EKbK7nf|f2`w_=$z?u&Ex!> z`F|k}=l5N^#{U$q0pfn7eGMv%+7k;u2-&$ zU1Oh;qW2?*abNg1xX(3yaq$~hq5jp7zXtbU*QVl{Im$%mtk6}j)i|C*8lPJHM(WmW z6Y|{FOT5P^`{`{{aX&ZZn%7G3`~~h``?e=-8E4KuGx*qh<&z7zAAsxT_`4;P_S&_$ z&wFOuYV`9i^tS^2UWNYOx%YI-9k`!8#k$rm?cn!$-<zNaet zZTC5e9P^fijAx{u&Sy;UY;o^WEJu&$sC(6;nH@)@eq`ZFje-=WXw}{18<9a-+ollI#1-f7tXyeDfSTY8Q7nT-$T}OE#ha{XUn~C`}q~=-z?*b*FJ;&@J#HV<@rbhD0wP0mh}GR zmylC4jhQ>!Q;Zkz-IblXfA-OV{c}yq%$>;F%(0ZNPdcBWo~S-F=+6C@5F7iMSg}Lq zfEw2`3HGxg;Kw}!Ihk@?f3}|clzBqh#&v0nF1Bf-%%W>Zr=4E+!v@26rYlOse&6$d9;sndR@CI>NI ziNE{s7rhsP-*QhS=w68JH`~6bF65AXeke==`PLms+g5oD+I{c-#~@96vOh*LWLc82`Nx8%IjR+-2Ckh$+} z>s-gEb&vNPd~&Y({rqvRI{wf$ivAlAtfH#b=Fpru7Cq)9Re3Z^k`7=RAE2?(xYyeGBgK zS3(D~OT<4)H(QYy7y~Ok9 zOykO(H5SeH9dQirJeQJV<3A-^exG^q@KR&bt23Xx+PL<6I3I}}gPi@;Wudg}DNUj9 zTI&VnlV{JKMU1d@9rMgY||knc1##O_Ame9Ss4JTju8% z-;!Hjcvs%S_b~Tk?%Dd5d1@qL)T#^{f6r!uQzi zvWx5=L^ogFr*>{`K0iMY6>;;gi*odbn}0(RzKiW4b6h1jkB-~ie0$LYTWGHR!^V*P zL(yITy#Dk4ae5#qf7Ul)1y`f4sRe6Db4gl57X7%Vv7@2fnjDG@du3GTWK$$U$}%=? z#)2|U5o>{9LuVADr8UwVtypTwDrePtUEaPOOGhtnu4}0W78x5N9aeGC#@bFcySY75 z&!1RWTIE_=WzF~YqPE)R4UwX@$VROCkNT@E(x}_qhBc@=>?PqFYMbkutO;{-TV!SP z9aw6mrM?Kuq(tgEiq>NqEo#Cy19WNlqWVaCU0b82zQ}k$W?HO#SJBZ{)Utkkd!(aC zjW4o%b9cP$wzT%rqI8bEvjyu8Q(I+=XTBzfA080VEwxRXBSi+5wr|9$;15RXt;N1A zja!~@FX^BT@VS!s{VL`l*k74$*gg%1trrB%S#tF)r*v>|UwgsZWx0Y=5i@z+p?W`@d zUE4~arCX&)G6;i8vS*DffX*S)US!EImo&re+RhY>E7L*EytbrtJ!_WSyR{?IZnVia zAd1#oA8kPPM^{&ln{OQDI?#M)CBxEq55{i?<{zs;&GC}9w%V;~T6PHOXC>U*{!2rQFDu>hV~9^ zfLfVfXxqO`R1{cUX?s6haaVr9U05%bcA#akV;j!g)qP7hug5o2tUatb+uCi-zHxRt z=8f5R&9=X!fu9yOr>y`r8pzsXxZirt zo19+CIi>r&@>~^3`?9Xt`R%+1&U@gz2hMxoya)c2JrIU*iT0Ox)-K^bQTS>2lTEPD z`t{cT6CJs(>NO6!6Th*q#%ja5+3PqpBPj;H+`mIU$b@6~RHazFh;5(sQ%_zQ)+_RDw6Wed_jg*_HO z`RPAGevtkwt^ppTe-8OrCf3)#!{iqSv~S=6K-q%yr;}d{89)2Wlhj{JKJO!V;?s~s z|6mgR`;*i^m83ow-Zg^ySIqlIR(yQ&x10Q$0RN1WzXUzU>b{=*pTiqcf$<|hsQonF zunOW=kiQo8`HjCp^2-D4Kb1uP5++Vi{ciGu>^nj}!IK}y$Oy`x6XX{M*gqE!zseS* zzmWW5$oS=VXOj9mlGHzxr2dg4^)v8Ia@m6VA11#PGJf{gC#m0&r2YZ&mq7VcpZV_; zl3KQ)`9F<@r>U4Hz8T~P_K%FLfck~x2iezgnN|$4ubccJ`+CU_vTuO=Ao~uGA7tMt zyznYp0sj2rmve>1wkHmc|K^e()Ia2_F7& zGJ@>Ce@b;0>}I&87+b0Lp^r(CQ2Ap5e&53;X-{2=>UXD8ObgZ!ZU z>q$}{@1R**kbMWq53+CKy2SRS&ryDmeVOD3`7ek3Ap1(m53;Z8?b?2&LW)A{|D)uW z2Jo}4S4NP1bIA{~ugvBn`H_lKVlC^h`gbU12k^Uc67zTSfv%wX2X55*T>!u1@4}lB z^AC_8)c?ce?}i$5!PCEnxvIY`fImcjJ@|h1W!|jy)1ln&{3Se3`GWwQ>Ui28AwL&< zKl>)g51N0byp!!Kc=9JB2|qgtKPL&lfc)No_79OS^D8!weW%GU4d54xW3a7N@c6Hv z{2={D$Pe=0G4eV7Jo^qW#pPjE2gX%YsUk@?A{5YJ1kIy(-TTuQV zCO@eCtb)Y$)hFQ(kuUl0XaCv-s=qaWKSKVV0DjX#tslhCkgqCW>&2g6`vc?$`DY0e zE66|HN%%*}?+<9dX|Y&`@5$lM&%UGN_kiy=|I`#oZNmuSpCCUde~fn-2A<8o`6%SJ z0uKQ{4eSC+c^R+=Sjh5Iut&;=fxASG^f>w{<$c65;(qv7%I6RdAzp_;9|UF?M)?6n zvkNG4Ve+$pw}79iXdXiz2!B6N>hA*ze^AkEB0rsc1Bj`){50|pXdVJe{Z`@};wbW8 z%1epaKQ#+8&G01CaGbPn+t#5N#`xMPV;33d^6wN;12<+$tW&s<3 zqCW#D`cKU@r0MdLism>_%Ey2iC_koX9%cDqL6naunms_#TP`%}l>x=xQbn^EDE2N< zGz)=ZZ-Jtj3lw|jDw{B}jN6L>4+8h|3d4k+>kq_at904@7~2T}htj-P<$1W@Xa z1Eu~kpw!kl2=xpFH^kyoW2KnPSj{B6qS*(Oc6t@7YJk#CF3ab#JR2zWCve;>I0lsd zi~zp|eZz|8KJxp3(oV0U*$I?(Iuxr)fud&#@P9+DP|?g``5fTiqdcr=*5G_VYbL1I3OVisp8p*wLw2)dUp1^+54cjiQ;$@*Lo2 zP(DY|%w%~6Z~*1$isndI^&A9>eFqfHLE!&}eR~wm0pOG1?^ZPXfS&^0t7vuu#l9Vi zRSm!))UQ`G*8x8b`L&8>1yKA{N-P9Qe{z6g#~ej76ZkjKlc8v)1OFQQG)2<@iXEqM z&;T?~0mY7!idDyfqW2h3{4}a)4zhdz_(_!SRy2E9-VOW&%6BN5oxoktQ$frEir#df z*qf$kPP|S1c1qDa3G4^|gra#I*avz{(HsSey+;+R4gke(`xVW7z`uh0prSbd6u*#Z0t z>}yptn}AP%-=JvL06zqJouXL@6#FU^s|tXhNBvwyGY9xM^P`cH3SsBgFx}q9!0Z*RgU#f}C=b1m?T&{L^sRsbIXzg*EQ1&STTie@2D z>?lyI$^nYrFi`xIt!SRWLAR722YvwMV~XYxmLCQ_gz^zZGtBZ#pxBq8Xv)F6%W337X|Vv7=0}Y6(#E768Rhxr(O2@`)LS@c_zC zDVoQDB0mP)g7RaEW;V++fcKz0UC}&!CFdta^AvD1_$L+3ao{G<#}&R4+YS^vIu*?Zpfx`!nl-?7@YgAtl|ZqhLeVS(iXEkj zRfRy&n+v=fa&r~U)0tX+3fPMBlZxgT%Z~wDP(G??%Kcv1|Lr4o0!42VQ0#3`G}i*< zxS>+ftN=EHU#@7D0`CJ|tY{Vj#ohwNsw|-F|1uTLbl^tFrzx6p!&3Zq>I%hipx7}A z6g!S8numb0{~J~`4+5LOKcHw10mY7eisl}m*fF43)dv*4JwWkWx1zb056Xkz_0V?E^S70q=(@mmG46e#^G0E!*Cie?xn^H;W_nFWl1pQ&i31I3Os zMbiL^9j7m|u<9gG^o|3?Z^sqQ{Vd-Hycgwziso*X_XBHD-lu3*vb+o^_LVA{1;D?9 zeYuL}Two3OIf`Z&xDIr-qL~R4`!W=(PG#Wy0rgKRn&ZG~$RAfUj{(I`M~H`j(x3f6 zv13Tl8~}a+dUh+C{lNEw-=}Ey0L6}OMY9Vic5GLyY6Xhk2B7$E1wk2{lLG6eM5@oKHz&$Z&1-30ImVOThZ(TTKS|{)dBo6 z>bEMI4ZyXKuU9nJ0j+!@mIAGO0$TZ`Xoi7bf}U(eGYj}`v0u?l2Z|kOil*Fbvhr!V zg;ghkRz3l(d{Q*`198ZG^KPKn(F>I8Lai*XVR;#G3HjNi(}6QlZ{iZIe*!4=4+Euq z7?=&*NB#i$T`cba;*|X68uBa2FJO5N%QJ~-O22k{$yjb-g1HKD# zql)HHpvaAoKMcGD{DX?-9+r0lO_c9YG`oOOuLCIh%SjiKo(q)zgn`mtwxT(O@GRWy$P^Fbe0G>3sAKLlKe@_mZtAW+)bqiF61N<00EW-n0c^#DbGx1zZNDEhk; z%~GJqg@Jh}&sH?ESe^lt_@*nGX)HgUrg<@@XdVTMp2I+qA5k=iS-v0mPLvNRnu9>O zj@?6kJ#ZfQHHu~_@BsL^K#4;bDEc#jH$yH%(L6L&<1z#kxjjIMS3S#@0A*a|ke^Nd z=?k^|Bv8iRF`(2x0+jkgEXSIw*0}2>e+T*XK$#ELvAhx}28#YnpxA$Uiprk^iu@o@LLoW5O{*v|&X!Q>$^}B&q zd&FGg6yj)#^7j$Dh--hS;+ESAa(`ia}~`wK&h7r{5~)ph$*~0P0=)faB=x*!$R{IP})BV zlztsiG)GvzpXEa=A7uF+p!9P<(d-9GKl>D`dV$i<9!0Yi_&nNa0)7v;7AW>r0Ff%? z<%(u0Q0yyKG?xJX5po5@xj?Zu2Z)fD&rvjyGgdirLqaHr(Sl|M@EalHp&{V)A!GUg z@LRxM;D5t4U8GxqzW`lBx&ruLpo>Z80)GrTOgaPjFQ5UIz5z6LHS~57Yly|bi%}jX z8pJWg0Wq0AOdKF~0WUy#4Y8OQ2Bx6gAdaD3p@)HIVaEWmi&z8v9qktklTHJEk9L0t zI1YVH&@%@7f7p8;xT?eU!RqDG8Hum2+csR2YpC*9g8A?! z7=SmzT$ln?Z=c0&40N(X!$w!y2g72?w=|6jruF`I;4|`!6d>N*~7oiV+9|G%pQ2bGLj-RhW z@$*F}euRGbLya$XiGLmft6upy6#sv#_UBdqdDzN4^%!h{6|f1egq85!FbJz)39NX|Noo!f&DX!FM9dm#X%|LFTD%qL+D5!uP>PVFZ2y zeGlA(Ec0X;?1J6sJE8dLfI%qp=Rcuug{8=0_y}x){|CLyr~8l_VLx&R{v&LF+tJrR zspm@gH5h~sqc4FE!6Ntq`h1v)9Dq_Ux$rA61MWbd4j+Vm_+|8Is!xRvp!dNqp|?~& z$RJAJN$H1QKri#O_=&*#VGsN~`fk;C!TZp6!VdHus&9k$qHl$Ni9QVPL~en_unC?; z->CW!+>5>eK8wCa^_6fB`XD@ozC`s!@E-K}@EP<0)#t(@^cnE?(Wk544|k(agC9hn zs(K&Xh2DZ8^n*+ql22J@+ztESG4v7D_rOB*@_Vvc^j)g&gzrM%0Utx(rg~YY+=V_2 z%h9)}z6lnfZ-o2Mhg9DH8LLuiU@`hi)d%5D^d;~f^hMA{&WAGI1z-{STqx}}1IqZ9 z4tJyX!!5{ZP{zYlxC^}xI_)3I_$a@#yBmE!6#sot#@7feMBf9&e>arzwhO)seJ2$E z9Z<$+`90rV=v$%q4@1V(lonWkz6nZrS%)oxA;_4L(f}oV4O|E-;ZF2HxD~ks%J^Ia zcc9OQ0ptLb@j4ggq0fL$`-d{`_~AD6X;AuCDwJ{82LsT85`K_*RpzID_)heFP{K#x zAhN6rSxcmJLkZsnm%&cB34Mp^+n|i!t&p)cC9L`uDDz$uT!X$5ivJLl@x1}AhBYt` zeI;A~gK!o461WYy2+Dkz4_BfOKq;SGDDzteWGqfehf+R%$Xu3^23Md@g?AwPpv-?3 zybb*z^P7xs{ZQt^KDeCI2$cHjfifRoEpww32n^ZI)9eaP7`01;_tPClG%7D_Zw3HD#f^+@Kurj0! zDg#Qt(o#lP|2grMVP!}eR0foOrKOCpesba~!^)5{s0=9mN=q4G{o%w{hLs^@P#IAA zm6kHX{O!b7hLs^@P#IAAm6kHX{O811hLs^@P#IAAm6kHX{Ncn`hLs^@P#IAAm6kF> zWGB8dtPClG%7D_Zw3HFXKTfB1D#OZ>%hJ8xHcz|9>kWHOdyOY(S6apvV1<`_yok?i$0r8cJ$JGGZ%*Nqo% zB*u;H93waOay)fY_$Jc4sp+O^-gC3sW=->+p4B?b>us5Jieuxfle0YD(5yzzL$gj` zJu$m+_B8L%tk7()w|~|E`b)EV2y=1PCC)F->gIT5PX8P{-yFW#>pd~Iajw_fGxsvb z^K&~nhSN?{7FW{xId-RA;@Cc~W1iRBdu!jV)4cr)2Nq8A_AcyONIn;~bAD=Jm}B$8 zmW3W~U99tKiIjJmf=Dhv3 zj@u~b+qyY6t~j{@Z|N=R)4Yx8CuvoeR`j6hTycS8$BOeSJl=CFI#$qLR0Q;w@$#z3Dv!5&)#X*x=Bi7peBKMIx;Q_%rfJPI@8IeoV)d^cSncul zuI?vp@9MtQK5x(J%bZ{4``upez&i#xwykYnJI#Ck_RiZWf!o_}r`4|wao)G~3i`;} z-nAa@rL~c@T;tlFwLWjx+KZewWi)5d8gCDA9Jqb(c8|C3_5tGd-G1eEpZD_Z5zf0Z zFJ{u(GkZCn$_i&uw(HKVqb}Bk*AX)73YtqmtOxw613zIazm+^X}|R*?7yo!twO_*7aOPPJ0f$DW^4ul3YK4 z=JNUo$4l#b)_c5N>o2V*Z|g6v_j%8+@8tZ%hQWWnLqswO>b@=$IH2qTa#PbL>Sf~)vHCX-Y@}W`_7k^vW8X&VW#eVeFWu2|2R-zTevYl1&uyOOZMQpY z%FRA!Q(v10(L^@)a_rfBc{6=~bI)el&+E;j4D;GJZ^=8wu_>=P&*MFj*M$B=USpomJGgC#Se@H1Y^Q{`w{bkZ zy>+|C+p_)ic1FnUr?&gNC$~3o-n^q_2i|tHaqQc1We3;3b8sj1xbwv2{&)a-w z3+Jcu!})m2@8H;`lf%>^w5K5t{e zNzS|Pxo*&E4OBKP~e9E1dV<--q== zNf*}hC7mT)c}YhJBU(v2=jTe=IQBm<@BlsJfe6R02QEH9$cH)~;@TeSc!)3$wLRqV zo_^>Y=cgZP<#^%Yu7?R3ycnb`$}W|WN?9Mrjz`ZwN`9(B@1`ssJNFo+R^3vK{?PeD zOyyR++@v#+yPrnZ+0pW6ST{QlCG$;)z&niWQ(4yKDXdKccNzO<(v3Y;?RUw|J_`qT z|EBjydQtLUabFuZ!l!F^pXNV9<-_U5o~yF0=>=5&k>>A&meGDq?~2MF)BNOX_&c=x+vl7D1(U!M1JA1U&n+8s0%fHN331<@0xHclTZTbGhlqzq`-U65@$0&s*iw zArBFdD^|BWiIQ+uDv|0w0* zqrJKHvC7CbDi19;a)ZiscNjUO@`W`O{#gPgCj$1VRECj2S&*Qow7)$Z=A^sAL7y(YC^)$()qRr;!4UyItG z*79-pRXQ)}`z$N0_Gi?;yRXt6S{|)x|E#t*cVDHiX?pHHO68j0HVrS|HomT*Z_!+hFV7X^vryx^`w}hJ`0hSLXV;M)eXWvmkQwtP*~XqKH&%S)_kt2$p1a8BYqS#y-=*Q(a*f=ra=+%M zM)S8v^W*MAB;Sh=fAu{7kx!dk53;+D(0&;Yk;7{Lu;#x<{XeMfDdMKD={0D2hlwZg z-F<~V<6bY%tK^fh-q_uJfnL_@ljmdd`G{U$pZagm>+4tfCz@VJ(_63Ux%&h?r0KQL z&gJv6mZ!VV&&M=9cVC~qdVPcHe~Dh7JZzWGpJ{rHn%)Ckhg_e#@6SKePepe3`FSkI z$nL&93p0%D?$h(2UY}3>2lV<$Bk2X`r}B~C?~2^1@?)Bw zyYJ3?y}mT{|1+J>{3`#6rq`nBy`<&o?#uH|y*_szo{L(4?!G#|b-PL5-ACt@1;{?e zH}(JSO-9a8`P-UaSks%T{oUPXN4~Em*V8`VTwn19BX_GjEz`)oDtC%Zdb#TV<#Zzl zRDMR&Yt{5Fy7fms21~e%hveQ8RzACYnB-Q z&uV#|RQW!wKX+f5CuDx)dK%RKHT;SkQu!--eO;Q~3p$>;`_6oou#(;&i*lCF%QD^~SJJNJ!{1V!^!$v|@>wV22lhS~CFazq(-S=gwgeOnQ^b`3ssomYzaR)Ved_NSmA_9p zh`(o5o~i!6r1I@je%O<@TFytt0V%(K*YZ2`P9skxU-J2=w0FYK((tmcfyj5O{L2+a zu2lII>0kI;rSaE`Kf)hZ`+W8HA(e~O-zQZ5mil{6$SWWs=QqNWvcwLw$B|Z ze{+e6f4|DV(E59~%JVe8PpN#Hj0cp@AEI>`n#xdzqBu|w_UGy zjkf<6)&2?XPd``r0nM*>yScu+g~tCPm0!^IaJ$O?A@xQ60xCbC{vK2L7WMZ&m2Xsk zA5r;f^>(ENX6sY&m=+Fz6SGg;r|C`FwG=EoBJ|p?VKJ8s5f6r+CZc@2H^XFIjfaY(t%11PR8&z)8 z{B2kH-=w|ce~-!^)BNpI`8F-TIa)rKR-5pTsr_*+zbv&sD*2;4cc~nZ{2@Q4@-}Th zA5>Y^@nU~gL`8%ufbj{xjDhD)w-&eU&^Y^OCw`u;aztiOB2QuD~pLeMIPR(DD${wxX zr?fnWZX-PP@j;bk-w=uah=zYf#xLw2SNk^>82b^mU$6Q5ZIz2Oe;-%*7qY&e#&7ym z{*31Dg37^U=Kr#W{|6Zl$xo~1=N}~h_-G7*YH6TSm*We@)7btOwP;N7B2A^^wZ_ZQGJ(Htu>!emAv< z{9jhPJckn5y`Gd1Odo_mShM7hdm9_M_I%yRc}+n8kJnOvuQj!iML zyU*J98J{G)yD#1cHNLwK-jf>N$2B`2wY&SQ%~!j-ujO|sEAi*`?!I+@sNvmx20y3a-F@2*YWf#+eg7VONO`#X8h%mzx%({s zLc_cJ(*29(_XO=uK6k0Tk$x*5`Ch5`8}d7%bzbf6zI1=2_LH|7`z*D)`_lbV^Ix;b z*yZ;;jz7(Bmgdjh=kI@6xUSNov$hibK_G0)3qK>ej@_!)i^ zKUd`!)Lx?DWxtd~Q#-8&l|Q2P7WKbV?HwwYsl7wP%f2TPzenXaG`+Nd<$V5F<&G^z zzC-0)4gb&TzeMd%s@$UXIV$&P`hUI9q&KMcZMgLq*Us^QG4kJxk<%D|qQf&(jgq^^ z$o?^M4*elI{t54B`Q40H(e{0;+oI*#G2uTyCj9+lWQiLcf99C{%kT4|?We~0|MM~O zD`Wh9eN1{ETQ}O@k}>|LkXUqj_lzkItMpLCq5U<-tkT`v?#e68E8MzwZ|Sm9-b&}Z zUs=_6?!hJLDk;C$qJ;{+(>R4(k>Uj;dRVr_tttdVc{aQY&)D+LM(t=%ki?{B| z+fjPo);$GVw-xT#8+9G!73Jf{GG#|8=I`E~P=w)R5{NUZ;zkNJCNoaB=$f1$Y;!~3a#iaV>1JX%)myzN@6<e{3hp|>~WJD?jt@h)%nmk*k4Zl6UOruZ4SMH?6rM)B(XB#JV=60(@yd_ln z>#`DYm00P&UA|0DP9$FmmVROxOSJS8@n#m=hidANu~yq(>2&wJoh3 zSzR3~`-<9%!}~|gSz|_(;YEBY>+9VI_STe1@Y1M-CwZ7%MMkrYCTX_Ut4wf zU}>U0D6IMlH}u{eg*)ckJ4F_m0?r zqqI)I!h*fUrF-u!68~}&7+naCEh=5C6LnSRZRb+9^%aNrO&Ickyb^ulAmtTKolG3a znRi%DY&zg9$2KJp+F5dq>SPH-a#mw+Hj-)XZ#I^$blya?s>4YVF<|mICXHuU$V!SDQ@|f`^?8#%sot`I;8h%?kL}i5YKN zojgrlKmS^ZzJ<6>S8;Ab6X{sac$RQz=}id-mM)ZVU^Ch!9NcuXgoEp-m~eQfFD4w? zbijmzn_ic2aP4Xdhj;r|!V#QKm2ha&lM)VYx=+HvwcRHi-n|b^ID*qVHa$^ReY7Is z?vZ$KXQEF$`1sFa5|8gb)`)sQlXzt5%ZZ1U&YXB?)0-0y@06RV!-T8G3;~I!p@o%r z1ZNOPJh&MJ5)W^NfyBe>IFNV*cOXbShBFc*9^4ECiHA31LE_t@0HnJgze!5kNI7gk-d<8XOZ+2MpE zxqpgFFbxx2p1Brpty`CsV65nzIgzUC57lhc+s^UscGqTZoY0JQ?ESEFo4YnMcfxpB zCI*ryU}9sTI%30i(x zabh_7absl0yCw`16X|1)1wbqfglwgFBrZIVhF*TiFgprC% zFv3VZBp6|&SWSdD!}Z48I0N}O1|3Lq<3)*U$lM@NowIR=zntMP&e`ZN?y%3; zs!`H`V%pLn+}8571M8iV|Xm|D~; z-?(9-JYkK~E^;S~hc!;T4HL%08Yf=Pgz>PBjpw$ASc|FI^|1!GF-(|Lw!|A}rq5hA zVKl5`bLcJvCXAOwJnAnqZuIz;3H@ef#Petp%!!xXjqxnw7_>lh<3)*U$lMSwN?b!` zPP{1N7~IAoQJi6CeQad6HN;wUXc&&1xh`H93={SEta$OnAhkYm^hwO|oM&cC=+-2? zKBoS)JT|VM=#sFGZRTzxm@r;$Y&`8A8zzj0b!>VXFK5DdSjWb5+r)(N*2l)v^s*<6 zH*6jGyWpt5{jJShH(`9NWAm%&X1Vd=c_x6AzzcrLIS{Z#GjcM# zV+|T5JJw>NV3=rdSr;!JhKb^3O=!mIw5ACIWoAxnj+e8{j0xkJq}Ms$bDI2{C~^KO zI_fJ^Y>x1@eQ7FTU*rGvbi8~`{vDZke^DLJ+vG*&Jk*W-liYaDCeNCA9scoOxOP(4igjUa7qiPgi~=gmB(PORV(tcOH1muNjC;&1GTu9N9T z#)J6xk67cx%N2{WkaW&Q-Z`QUHdB|3;D>hEouOcE_Q z^H8v--z7CuL0m&-E}4rz|6m>8 zVdjPj<6#{e&%J}0FkVjFciM>dJ)3_j~74wyBe z8LQKJwmgG<(J>*^Rs-bi@hf-KmYj5<6o}GaX$SI8$+Ll zab>o@u;ia^XF(UNuV|NNWM4&b6Yo=6x?g@0G;AL0uTS%}SVMHPkzu zZ!O+B5+ik)IWJMwApY0_#ELXH@23^5yR(cFLj4`8sIQct@KBzk%db-Xt!1AF`4q9= zPxNQ(wX46nifYLn?|>YWzZUivl22F~1)mW6tVsTHk2&wLmUc2;{^Sd6PWZ9@xWRWm z5%uTaWc>B85ln_LjXJvdlY7qb$}f%mT;($ym;Ev%eYW_0t7yXGXKle9OCe+yL?PLE}`2;j#H~WxC{u;@jR+VFS4|=^-Pn6XE#DjgT6ctUWt3p40xM!HjJ$E$Z(N)n5pI(Vy7sHID*lfAz;|V%y*C)TexQCL7M( zYUm#0_#5tYW3RVC_i5>2k0|*BcbM{PeJ}5M)jBd3b1W?#ZhDSltiPgrOt_$dl}pEy z`e-_7sOn*8ebm$*sXtO(RaZaOUz_@4n-k~LqW)U(C)vAh{Qlw8$9Voa)Sug)!|JaM zf8)1jr#`9<@e}{5!v~Ix&0pV`{B`4R%qKNkmhtZpKWD6GZTDmq8(WkXZjZM2t3SCg z`SfW1BINHuqZ(DkvQ1SQ|6~qeCnY8pQ;}_U>EqL^pMUw<8^y2g-Fjy5hQRZ`N(x-1NN3+-g}%9NnrCk$ zX|5AKI$tj*%{kkPpC94-$4O~te}aGU`3p_Yq%-U6wc~eEraLLy9hC8Q$~y1N(2;Ft zDEp^7lS;qy#s@s#c>SOUo0l+2giWUWr<}Q#XUczo@_&``e=RAQvQIjDl{%1oNZCi0 zT29$XSw)ge`SLD~vmuYwK5v%g)P=E`{9E7io{5le$s3LE>g@T}^Rr&IHb%(*wc|TT zdpmi^BQM*?=hidi&E)X~>l*{RJ><)K=Gq4=+6Auxo^8rII8`~r(9PQ~#>v~YCYuh8fnd=45pG+GVL$v>`SYytC4l)n&jH$Uu)7W zgUfCj%#k#u?a6gf_ap6Rh2`iXw_8^SQmpK`)Te|^llX9ka=KD{_O0+bWy)SwO}FsR z(cNHOO&8l|lg{MRHu~mUSNm4us>7rKap& z-9S6?TJ6#0`Qjq!hjZvp3!HZ2^rzbcS6`ejdee{GyooQlreBhN-BjzIz7>J1Z+LyL zeCo9~irKn*w(IxBxh6gFHLRQE^tq;OI!?TV?bY2D?UnMnVPp(BeOqV`LuAvZHQcw~ zcw_EQpWSh0`gDu-@Zg}-+e*q!{%!I2URj)M6<<640Bx#-wsk*k>^|Dsz4XCi+V@na z9lCiCN`H}Scr_|-uAVVrRQ_DOJHEO3l0MeE+|=&_d`hf zXSj{|-8Obz;HtEJGSu`-ul4ji+FBnv)6UXf2}ZZGX_UjUV}m|vXBz{@TCI$n7a0d% zVw_uQnQPlPP1@N!)6U|gE$yt6cGmiVhhpl+!@P0r_&tN% z*Kb`-+H5^9VGCs*w#409P(wIMAih-D6WT<>jM~OZ)N@ua7ZS>OAEpem6 ziT`WIzjWprb>jMrPV?IFFSjDkW*m+g*WG+c_&)pwZw_1yC0Xrqy>ji@T)S{Fj3oJ- z{$5F#VIAYX+fO9VGf79vM#fm)o$ib+(%xp^V=Me=QgnFZZ|poFeP`&%_whxl zPS6+p*AnjwLq|sUg*RR>t1&hos%doZ@Hvkg;mK@qH_0FKJynzK627hcYRmY<4q_>|%_k{9YItOWtz% z`Np)Raq|5?x%_0jl`+SnPdTJK9Qu`;1J65hB(PEVMv~_nvEy$#D38X0GuC}V0+ z$BLV2Yt9;gwZ62U@Kn};w51nad4|YyMvaZ68)w}*ak?^&b}|o0dzW>9JEn%5G-do9 zy?z~U{FJhhd?_O!oFVbIvHwB)*c6=-2-d9>>42kw5Yqv)HNMA>u9aEUbNT?kBk|H*rH_ z;!2wwJ3h+&(pbH_J{3RF<7e!B+^n<9wwrq$$;S^F_r{ykz*xBtj8NEuj$-JRNZ7<mp1MUbj+FF z`Jh_{a?Nrs^PAjz$a5^w1&JqPW%NBp{CSizl`(~UZ&|PTBQKSfZ$mKU*BquU zl6%i--I%=Bl`YoV&biJH(+R(XYZyDO4afCZH@R`8oTM!l&<1~+^r2f8c^1|Br+o63 zXuI$Fw3~dM!aXNniSv zsY|n7k@?(jE&9%@q2+EB=>V}e>ZCZb59ar9rXSg);C6cjXb}N_R%V9D6Y@v_2tC%b8~`z zq|UZlk8ho3o|n68Os{#K(d;|nxo1&@^?28H#x8UH^pJJW49agS=PT{NN4Q^(x*l`C z+^b_;v_E-%5bv2TiG1_7kES&4q36Mk(4=M7|Gs2XM(MQs(rMP?5$UVe z{4Cm|b01)yQO!Hcv$)xjQMxo|oO>zhmQ|nJw&|4zr&%}V^L&&xKUH$B{UdYQv`_H>-*_pAZrZzs{u?gv@x@-XS_|Bm|G$#1>M zzn#c^uGEcNA1`rV{WAAKy^P^MqM!bl{Qrb|?rX=llb1a5w2i!NfggXaD;1<@pPi+j`GZ_(rZ2U#3tsg*WULn$w!3im2v4~j6F|s|20URy-Izx zdgs2&cc<0i_Qh7O_2D^F_}kP}>*tH#c%!)fme{#jp8w%<_VnDq^UP7%KHBgM#^ZM# ztL*qKkN2@oXU$+eEziKq@L^$>{JVAilHLczYrnFlapxQNUQha``%Fjfb<&chZ_XKh z&a=o$d*vT^-u`~Q?=~{)lqC<<<9ntLxoQc7(lYA6}f~`>@0hV*lk$*3+d)b6=4%L=v1hr@GQt^*+P9DEts>8#^iF^5PZ^JW57c+O`!j(v=^o#QrJeZ+>$bO_MLidm z_%hydecy~3XI}8|tk=Vs{G%vho zrT7f(Lh>VXXMlXTbLbNCCUN9?!mOJ*srwh{hwk%9>7RkA)^l#%%G@L2-84tfXQu8X z&U4IXA$AE$g|U1oIHdUIbR)X zHuIYN9dRBu^EYy7H(&DJ{K{75G%4HJl&!?Sc6=x0w}W!rPI=~0uG@GnDu07D&&Z^2 zN6%~H-QT$5fLx1==Wll2a^nq*GY?4FSv-^Z=v3}&{)+Vx>&F*qOLg4K?4^BGPI&(^ zYxw>}o&(9>R(!T~&!XwpJDb&+}+Ev)yZV z?pI{3Oc^ew4E1j(vt?atp_e*v&R6Pvj_Aq9u%2@tdGRnWiQnjZ5s5b)+fR8_pv2k2 z{f*owkGijZ3->F_pEm1%x$l&8e!^Jetj9TL4j;MC64{+MCI8n)UDx!H^XZaDxp$$C zxUXY8S@azl3*=g*oYL`=?zLFwhrYQ|!*|K)-}1JnO(WZe?W`_4ShvY4D=**eyzOcv zlCv}Yrb88Vb$rij7vJOCbbu}5qrDsuw{@H9*nNy3vh&@hCx>4KV;ougkMLqG-M!q} zf0Qp&9iwBNYS4*a> zkxMqO%-LkgHzcj9x*d-nEvu#;WaoXijE~e3D7G4Exzwsd75pZ$=16UQyjpQ8Cr^rH zR0SO{^)1yR7yaOo!xft>HZMO~v-@zxkpu39AE>IWqn_#t4wqMOsZM|$4fVBU#e9{z zfG_=x=qe7^Q3ZR-4j-&=W1H~2=s?Rr_bN4)Vo5XNKx6!cSLQ5 zjt3L8&IGBm$WU|^#>!3+P{1QE;?ek}>Y$WL8E(L(R&+|klGI%(@kX`(Qfs8OmyVlk zjJDKNcX43JuDzzcFIlr^P2QSa?ln0U=f`?t*tO<9)5qMrS}}H~oa$l{vWjaTInI~v zsk5lUu?q^`Yut`0w3zs&)QStn4ec0BaY%;t?PX41x$SU6EIJ+~t zD%RW=U4+p^Zxt^!X-6wd$M&nJ)JHlEDUWnGr@)4b+S)H)il;Tai|?84KA;V8+0$kVa*zU|naikfO>o5Kh5+#rk@&S1b?AcLxW@g0{Ar9Y3%PGvPU zyrRS%2jx4|&R9uCwpSb|J6c_D@<+xQ?y0jq6^|cfdMP)3#R+?;tma)6$F{pstLmI_ znqN{8oY+<6GR8Y@Hd#^vBaMW?Iw}qMfr!b7GfMNTb2HM64CBt|I6T5L;6=AZ$5=Yb zB}0cgp>K^!nK8gzk?CF5@P!xWNsE_?7 zmd{8}o4m-JCMyLt5^1;$$6kF@Z*#7Aq%?J~8WUlp3`Tl$oI;5nN2JcixE}5fQaxrn zWI3xdr|z^P~k>(i0N&c#!SMNk~G|WVAa_Y3{OMWG!RuFRR|~ zv_1*E+j&Vyou%<*A{}XRBc4pJaQn^Mo_$g^lekISOyyN|dDTbi=vh^DyXlj=Yn{Fm z)o=7-Rl^mSvjazJ-J;ke*ELEz`T{puqx%@ikMVD&oF|5BqSUP1WNwS9S7)Xgdj)1% z)dJRd?j$AaotW{}8LbafDWjcR#gA+;v(D%whs#IQQWhq!2#icJs5#roYrhuRY|9^| z(B0jmG;FrO;&0eP;gQ1!w9RqT8 zMg5V(av`IoJTtWJGUJm>&CV!WR?WzNnANeYp&X42#x)ga%A;oFyUneFJ96@qj@pWC z$9Bp>&v@MAEC(D1#Z}Ib736N&l>&H<3rZo$|Fas%l&25PnI34 z^FLbQ-<)CXI^s7sT7HHHe|=?Do!_~!+Tu@NUbks^`AW-L9$33PU@fnc<53t=)&KkNKJBs>(^k?+#;{0kTr>FY~jo_1DXj184a3 z-+i>MUb1@RfTQ)xHR+;v2Xly3T4Omkjn?u!tCWAs1EtIBtWs%oMDoDTLu>U~qkwgBYhwIG4hl+Cl6Krh2 zG$TK*(@&T4;LDYqU*L@hoQ<{58fA^OFP_9c#vJ3Hzfq6+j7@(s`>ms5$L4SBbG@?pWY&slRpEKffLU-%30@)b_*}2=;7w8GH zfGdkvN(gHoS(TYnkd$7P0R$znELaCZ~L26e?-!R=kg00fNRY3pQlAT>Z zbJzALb+pY?{3D!8y*{!d3tZVoi{QoaLQeXLRF{km$ z?Cl(1CTdr2G5h~*(fy<}cC8(qc%toB+Sf#WqJ2ua$@u(MKK~u{JLZ=2tz`GVJg%P< zCMy5`m+SGy|MJrRO#;7e3HXUP_E|)uYUjE0^OL+#y zGrS4ie@8!)YyQpW|85ERNq(YnIMMzgb$Q+Y-AnnkQ-3?|Cw}e3HDd6sI+!4SwC@vi zY<`;?Gt`J)KBxJ|{1;>AJNeG1gd;&?>>;(QA*M;4P)#2kFa2s=#O30Ay!21=0}uJ= z8Y;$ro7#0uiLrO8UFVt@dzaef$FbbEZ8xb|mb-8AWxbhOW(+ac?@nT^*vBiMR!x}S zLR;*?78AcsA3_DNk5@kZ`tT*5Jwpp7-u2~9Vh>DW&!5CzG>N@r5_?eXAzgY(Ib(>|JYQJNp; z`Yx+IUj8C#=O-faiOK(v+T*p)WW5QF*Z#Jf?QGp{5aYj4?eX&8toAUO8?XOl>mTF? zu#eY1()D3|Fi!kNwZ|);lWNyy7L)%Lwa058t&_xWSG%A5@rm(&X%hdvlf+MEQ(XC+ z~|I~#h;dt#MP3=z+Y`pSoR(lP`@#eo| z|HS@hPGX;}_A9sbb_e06|6+^oR{yFj`L;IrIyVTwepTpi}Xs>~iuLbZk=+g}C*^nWiFxAkW2@fLs z4DDnn@hr$xQaD6=gZ3a~=qwyCwEN&Y2-l$uL5bG@nIa174ed(EkX9)D3fj{Dwh*om z+HgB$E-MTe+PUzLk+Ti$4ERpMbG_=d1_zgnFgu;gn?Gl)TTx@6;LiXsd&Ntk*9eS|`4DD>V3VVj(zAgqo@!Jlw z;W?EXl@0KZh+l7L*T5F!YD2pc9!D-Ww1e;uksmg+OW;vt*`Nv9+o7bl8NLU7uA!X` zk0ECm+UZcj`C$$E1%`GiT#bH^@muWuQ0noDq1^|i9(xV#3sAy!!aqQN-q3DPeLZ{z zeT|`A3D+Z+8`?oA{vI~8bD{XlhT<>7(4MLKWcW0C%g}DT)r5aaSq<;NUTJ8TLkS-= zwDY0(-wwrpz|ihtKFvda4vPIW6nogvZc%*`{C)H%4egNX>!H*`jiFts`XKy0^bZ@_ zg{se0W73oh-=YoTDQt3|v zhV~W6TBESf(C&q7g7uCbDE&${$d_~*A-67tCk*WdDE+4fO8==gw9BFR3mV$IV8Mw$ z6TTn4&(OXy&*;y?5c&>7yB$h@J*W0oDE;oVp&f?O?@k%o%~1MXli|LTQ2Je?q5Tw; ze%D~Qub)3i$n~@-Ps67OA2zg4!6%Sg4DBX(9QmZ7-3X;VPZ-+uQ2bTH_n@ydw9DZ! zss zp!)MruA{@yJ_n_LwHn%CNYf}hWoVy-;x7c{`kpeh%T-^ZEP{2|3k~gn>e;Brsz;w; zXfIHGs?rA~Ub3M*$lxLAU4gae`wZ<~_&9RJ(C&fGbwcS67Y*$Tsy`2<96Jo{5S018 z8p=AL($HQ5#m@p*jXurLo(<{hg{g-2Oz6yahITTPcrw_{A>0rL{Y?=GYAEAErJ)^!4OTShW16cmT(xX*%;k4sdd=(`Q~bwY`M0!n%%DhHtW+iYn2p~PEYxR3YtIPv%daGyNqllb{i{BMWvB7DHm zUZDDZoQk~B5%4N_gA4nDp(+usIQ2ZrB{x6h+s0O)F6MqASb|;khZOYS7{DckdW+;A6 zLh;jRXosNqdCJgU1Eu~3uQTO3V7M;=X~%XCbkb3tRyK*9cnwg>vqofCX=J8QKj{=9e0^m%}yKgNAmA>WiU_SNTw`Cjcd0E?kYAZD_B7tB}(T?KCLq z&V~{{6-s=ck?mw7+x`6g$hl4^`R+BeBT&}c7vLgz9?JE#!`lcaf472m7)m>-hf?1) zhWmn0(%%dvUK*74GQ{7@WZgGtX!k-1cM(ea=`yt2RDTX~YhKuDXrG4MA{T}Y?UPW_ zZ8Y3h2_-*4D1M6}w|a#|hIS#8`8VIt-VSB{4H(+FP~v4nZcz#|4DB^g$}!#0UIwK+ z{f2fLlz6kD%)hCI_DpySvd_@&Ve#+uGsAtIP~x43-0Br}7}{-6@_EkCZh;c68A`sI z4DCiJ`8Z){KLsURJ(P573~kvoR>s{z<#s6HGL(KO?P>w!)}oMoGKKa`wGVQWBH=G7 z+o05E3#2O*hE!hyH=-|wTykNNp`8sS+#1Nzzi=6pa`zj(y};=0G^4jup_KPbL)!4Kqs z9*X}CL;GPU{)?dak-xVwlow_g+H0U(*N|nx4L}JOQFcS;x*S*uZA?8!b(HC98zV4K|`B0v2&kWY`Cuo zQe=gNhIRnTeePyM$G*gCrQ{MA7&{~ne=-Zh&d^>Pa4!%x5f{5Z^o ze*~qTNGl~BvX^d(A3g)qAih#k;Umxo_dyFj1P96I1F#>GR!Sed4@TgtI!XMBC!|?mC1%3}U!GD2`5T7X__+8ilzXNOFw_zpx1`I;d zOeul?1dHHBm=FIE2H@YrT=-R(0WZLG_yY99ufR0;C724o2z~Gi5Xi>PDLs@!H~FT_ zt@YGbCtL|Tlx^^<*u!dXfwy6Af;YoP_-EJ~)LsK6ex;JKxBd`&k=pa2#1FtZFc_I5@5-9c}I1lE-PVBi*>={t(>2NmmL&gH9{Kf85JFwF-NB#v& zgVKLeq4X~aFZ~Y4yIoT<=){b5DfBgq^ORKhcVbt$pK=N#N1)id;h!LPs@w+u7@07R zy$O~ghg7bCjD=4BllVo*4{B@cUqtSL$6+UY50vKu)2W|U_(S9{d;+$>8R#3~ z-%u_g_<^!-rueehpk1b!NQ59~pf=Meu4yC8F8N~h{O;CIot!DrF8 z!UM=*SOHt$pQ3Mq(~ukCcVGy93Vj3oHgXNT1S??}eGp1Hl)!JHFM^*$&WC+40Dl5= z;h(?^*bmd8jJJNs+?|pJze)U5_zl>1gK2j?P}-Y3&$yL(2}9N(DNXQ;un~R+hTvbr z2KXsh0;PQv!53gY{2ds8UxB&s%P<3W!gTm0=!XlS58ePR_yssf4@n~ZJ}CMKJP+kL z$yD@R@bk!>@NZ!UOh(@d--R58&qH}m;zi#GJCH;0f5QgoL0<{~1~~|ygC)?1J|Bvo z0Bnc3a2on_I1AYi*~d2}4NgJtgP)*W`Oo?*^n=%%d03vWd>p+zUy*u?K$&NI;KyJ$ z9D?$Eg9P#3;I_0QS@Ob?WG0&C2WEpLEi|^A%|caY=BMZYv7+FSHi!5 zL3k2uAt;BDI&%h9r_SXPE4QrsZzkDd| zH5dLi@iO3P^y%?1PWO2s{eqIZqkvfFFfzP{!L< z_%sYd8DCrAAHXK~`>+wpcpHK;-ZnrPUu)opp*#-~ISBt3EP)?_MX(X(!w3*Ly_37>-UoaqMi ztx(4AFqH9Io-@rv-w2N(hhPJ2fY+g~gij&|p^WP#@Ot$5@O>l|fYSbRVLf(v9`#=2 zbm&3$!#bD-kD!<5QWD+=YtdWqFtR+SdK|eQ$~@2q5224h2`|sBj-c;`??&!|(mp%k zA=m+{(98E!rG2(S8Slfe0(}dVak>dU1{+~H`jF}y;JeY+!2Reeq4){HDp&#^MPCFZ zKlxDldjOWfTqylF16IOx7)0-f;wKFrgsHF;y$?!$ELZ^tecTtI?}yTl`k?fq2;2vI zp!B0|DE+7lJ^(vmIq^H-7m%BvwDS;@_FV#{|K!76$hoi(%JZ_FFdY^^KimN=xCjnT zbF6QlZ>G_9dHg>VZ-|&T}rVD~z0hEYG#(LwTw|B=2;2yJRNoC{{OE$*13UMbGJbR*%lOd- zvtTQf{Dh%A2Wf#Buu1ieum?E=(_w?^YvAk1m2d?Ns=fruI8+3e!+bah1MrtH7cPSt z@HLnYUxj|?hiPyCrovx9ALLfdxsUx1h%a7L$Mr*QVVvhe(!V3f(!a^8#nL*Z8%lre zf)`*XWQpKB7y5VDhWsVi2%Y{0e}vosBd`d{co%@;KSP-gC7m=#)|}@7FGC;li!dMl zOzl5a{f{AP$DhO>pdS737f}4X3RB@Pq4;?X0_V9xg!KUFOzl*Ll_6zN8BqF_mNLS8 z>cm%ul_6zN8BqF_mNG(yocPMHGNcSD14_TrQbwj}d}UY}QU;X)rC(_&Bg_v@`pU2} zqzozpO25)lMi_sc_{y*{qzozpO25)lMi?KR_{y*{qzozpO25)lMi`fz_{y*{qzozp zO25)lMx_5s{VT)DkTR$YDE&%H86mP0Ul~@0ltE=cDgBeEQ>A}q(*FIbx0DguC8tw6 zm0@K_8B_+8ekE|?`MjG0)4Y|{UT?5GIjOkhVUL%;C3p*qyxw+ChsWbR=V|wNIks_Z z@toq==sC%;&D-wvdM_k(C3(FaN#{96l6vKs+?(w6UP|ssPEKl{(lO=y6tA~u%4LpS zQ!a8mHTB$7JWV~#@#NH|sUGi%sV6xw&D%BO;ta31bH)YE z+h(+L-a6yl439TFqm}dUjME&OW;CN|oYBO2#TEiyT|8J9iy1 zu50ByeBJ5mrg;ai8@etzsek6c%)y!Xy}teWY2M!J`#83yoJ*nPcq~9kr1Wwe;*kaU zyrKC9a(Kf4=e;-dao(KTk~+=XlX^MT>%ExT&9Ni(e5%LWmfFF2TWUMj#v4!Gh=&A;WcEh2YLbFcH!ZvGg7FRy2XVzudKBr^O`8nj`=8l__lbUaC zx%m_vm^(OkXf9Vc@8mpuriD0$=bh#ly5+yUz|_g z=AWNW-sX33-ZsCTWAFSvj-3lGEWppLr*GwoZf&_WIqAf$jklgex^!y~(&dGbg}n>C z-t&t(7ZGz&JI5=F`Z-1x^>RG#@AM}po$`nMr(yTvON)CJljf3(OOlgX|3CJ=2EMB5 z%JNQU?S@4fap`<#2uP51}| zJ@@xZ?p=HBz4qE`uf6x$Uu#eL)}&|fz2)*pFW+|gh~(o}JaGjidBtN_pcGd;ir>9c zj!Xf!Q{JAENcK#53%`4&yovv>OnDU{FHLy`zh9d2Q~ZDRif*KO>58A?fA5t?t^^lX zzJ>oiQ{S8l?9^`je{tI0X^`5qUDHM+JEnhq`iNxD^f&Rpd-{RtpgH|j{QuGPm!>C@ zFHZjve!n<Gc^%GZvx2qq!8oXWoDE=S0s{Lx{@Kr~yf^4tqMVy|i-$b0A zYu>yDrN8C?{{QHjm+*hrHGA;?kr`WNK$m7bI|Jb}Uc&z)Gup4k?`vPW7CL|J9{k>M z?YHp%iEBF$a^%|fnFyKrE&Sg$^D+GInfd0-M6!Ek4}N#gJTMcbDE@jecq@LQ7;UY1 zTQS;N@uT?tNb#29!enpyNP0x_>m@r%K(nNyWLUwrlE+FOFF{)<*@mz!C6D6&u97|c zJ?qg~klC!a@V|T30sMbt_LkWw+3a5Y@0tB3f6wimi=JZMGxH$PdENN`*1WgzzoYc) zrO3Z@8~%6Ae{nwI&wq11IGx{v-`(>M;D7sdk6Z`+xbE5OMkL?5?rr=&a9z)JiR7!- z9l-Bbuj|JDJ=b?#4<4?6>-t3U!1Zt9_krts@PF40dv1Vk-mvQiP`Ke){D0zxjvGcK z-@c(2|KGgfErcAnp$BnZz2N|Uzj{MAe*g4_SMmF&H@tE~Ve*;Gv*-k0&2(oFl6fVA z7MA%@=A}#`*_GLgu&&ID`2TjM7yo-QZ{q*1vOQ%eYuPjS-&Ou%IrOG{7yiFp-i!YS z%6rNo_k~X^gib7cY$4>g@KO9fQqjH;I#F??0x>IkD^TB+&sHMO%2)7zZ{?5hzqj%T z{=ZrI7XEKr^w=UudeIjA-?8Xh`2WPB4*Y*{(O&%DwP+9icP~1C|36*y3jQBi)P5tR zzUb{mBa%qNc>rZ5&!XGjelyshC2kl2Xu(vA@CbzI{i3FUwyU4KO^|xTnrfeJ|a9n zP5Q81#(@uhzC1<8-zD)MBt6jY5_skWot}0)KZj_91HV`BXI-Z8?SkJc@gEWRytIx_ zW1OGO0{2MzhHEwcC4pxMKd%Vr3FjxfC#D*$DfIpNGY({|YHz(CZfb8{;*c`G}@}_e2eE6FAHIMR^uW zdA@;qC48&EUm30AoBQJrWi{2Y@0tyND) zs_?&8_%-*zA5H1>Ja5a-Uy1&93p`Eg?})&=r9OsTsN?^M$k*K8e&`&~L*4#Z^7{ua zGNA5uh(0_k{F(dMbEr??Z>Gfm9e(a0T!6I=X@8I7 zoBY-bzfIEtqx^g4>i928{h9mJuZq5O2)*$vKhm$0^nWb!>yh+F!58^A_o+t-f9C%5 zNTL5I!uh$1`Ga1&(7TTG0B;d^Ir#;Dg~*qm|A~Bv-zNC4VxfWfse=FcG>v~;@CA~7 zvEcum{Snf42>uPG2mFA*i$4Y!^?R$-?{CfcDEO=t;sq)(yU@$)g32lRUc|2fIu+|QmV{F(dM^JM%p_pd)n{!kxV zq&|*Hdo=g2_eg*KmeBtj@{9O8B>um$egW?l_={{msIOru#hx=w>hoNI*GT?F0zc0F z7xj^m`gl;}?Vz0e%uQ)})dH7{(=g@8&obfn5rJDIzik4K6n>r%c&oIp9RmNYzy`T3%=QPEhqU?m3G**N zdC~6q`HZxuX9WHT`w#GE?Bh+OHyrI(`2FHVe)yp9W9;Y8GQSaMZ;~JFlJhW@&AUnG zx0>Yxy-y3hcUV5aUl;h7qzCw~1->6+5!1g?uG3%2{tGmXJ$)8o#P5~(?=RGFpTLK& z*Kl%*PX8X{!uV$kybIs9efr;~YW#S?W361_9~6BpK|Lyc0pI+5nfZaoFA9FEHmuT z3F!Yo=s%9SC;eTB$IqQYe~+Y3XEeQ61pY4C0qMUk@HaSq1AbfJpH2r1`acxZOTNPgcG{vQ+i!`NSf9-i}6pC6*H zAic+h-d6iui89-!0`oDEM!4ya#?b zzWMn#_78wB5cp>Dhx)hx;}$==XfFUBE%3*$(ecMg{N8CAe}&+;2!E3WPrnCDKU?r$ zB7cAv3OthhA^#Z?|6Y6(f1BVRBY%MJ68Iy+UzNa(CH`o9liq(7{EfozlLCK-{Vn3J zlla$&JpW4Yd)U7M-y-0^ckAwF&&5^zWY)nCDB_ zU(AGF@$=gmn%<*=KUetq6M;ub{!a>gHuFP&`keG{JYPY2DfD~%JTLG(fsaW4^Bt+r zKPErWx4i#}dk3=2?f_ExacFsKl zj}v$@%ZKv*Qp)?#Fu*8Jwcx)a<-HW&{48hr5dYsL{w)&!f<+qt?~{Q?9+yk}NrJyi z@W03MjzoV7J>}y0n*WflHVl>f`@OeSC%e8|Ymj z^xkLx2K*7hUnBfnEBFqve-{WmO8VnP0zW1F*KGpdApG7f@UN!m{O%PvOa77n27yZ| zb^f$7`1!WebAaK3(e-j1fJb?JA0%yrD;A;i`Wk$mb1inN1 z<3$257k*a={HpZF^#b200e@h1$?dG|046~uYmu*f}bSi8G&}d&t~a=Ckp(R!r%8L{YQoWaX0JuR|vn?Nc_(W zzoi0CBEQK0A0++vrs@341Yb*jf&VALzb^gPa>1__e(MB&N$kfTOMKetEYEtuS4;o; zS%LRReQa5(`TdFXUylfWk?{N91pa5V1Je5of!7Pa-xv4-k?%hU{3k+x+bx=YTHwOl zG(463L%%*J`ZY@U`?;h)gm32ey1KQVJkaM+h{sP~0`PgT&z5PpTKJtO@VBMETP5k2PCD^v_pH{P7}>=LOGkhw*5G!C-|8q{1*xQy!3|`3w*Wo z*VhQVYlco=Dlo@+(z{jQpGkjsr@-rk|7wAMRiWdz3Vd@$!w(6}^M$OxTZP}BN&nU% z`t?1;V}4sC{za7Mg^>4csyNP?NHl_^hPMbj8g(`C9PH;ve|rFBBfM1L^7A!( zwZJVGXgDMA2#G&W;9sDuOn;rg+~*=ZQ{dl9de$pHn}*(&|1sV0v9zr`QOxu0(EZ^JJ^9yVMK`Zj!1Abm0Nv+;>Q{5^sA%LDO$9e{rlh@VBj zZPULdz~A2o;A8;)n*jZf2Jm$O_#BK^c79s}<@rJ&{yzugcXI%LPXPXA0RHO${HZ|s zKN-mXsQ~`J2GV~oATRm{vdeRSfZmn>y%z)JSrfqX9+XXQa{zX#?z^?7CEL_i(OA>k z`th2^4cVKs8xgt^kB={EZdqH?hBtRxYAlTZwdIPM+IpR2dHGEhRTWFiR;;MH9Pb+6 zvZ1YJRby5^d0y4L>fYL_){RZIRoMp`+8hnnG&VNZRy8!a0eC3BthKedwxOmiTep1G zz1dn5bxjy1kfTYUf^Vr@S>`cgP|H^6cW)Qrg>+S7r?F~AYg>y`Ri#p0UUlp3%Wth( zx@5)5D)SVs`{uBDIkwq~zGT^om1WB+Dyu$TcKedD@}-q4jxinhT)cc?KdF2L&C6F# z+4IKMh1Oz1!B;M?SiY3CH{e9KFCSQJuQmpnBD5y_4|KbAmah^blISw$2tOjWn?ZjG z!Ye93HM))h5&Ny4Jn{W<4viR9DWQ?W>Lc*7wbzJTdF8f>{?i0yoRcC{$l=l z6SF@t=S|>~b3{y!i-E)rleE9j9gv{=O%jxIFhQu8LnC?R^Q0V%*q@N|#rH`qJfg?K z03wG=INMlvj6J;X4fxPeAy>wMTq1-V7RxK5E#*MW{>7XpK|pk&F+DB@5<5)N$0+2m z=;6J0SWLGk3f6=jIkXaTB6ubD)2w790Jt}!ISvJGojF&IWs?b>w?47nww*;LcGq^Y*KskNcCE!$MP zvGRf1>^joP$-%-XzOoojvMn`?OOsHiQJ9u&TTKH9AgjhKzN_%7ruM$7+WPydR@XE% zdfl9{O{#P)R;~QRtuVBft*B^Tx3QsVO-RI?de4cxw5+^xDa}1)^(d1yKrS2?5U7~B z+N{hGh6y$XmFh9d8kIoXvMX!W!2YZP6O}7hexhp89m^_KE?K^;s%l1beAhO+v!Nxc z4fI=T?o*^(l*)y~gSD@$crm_PvW?lA)~w^E@(6dZRkvqbQHfsE zh1uHXx~w-vSXM)OGMe;w`W&pG4-fb zF}$i@v!SM?PMBQVx@JYT3D8Wz!A&t zZbBt2U%eW0sMd%Cf$?evq^l|-BD%%P+H7lU&6;e!*nyI+Zmd}o#fL?>xhdPcIx1d( zq!Qk-#HCtB#kY6}RA+=V0=C_Z)eRUQ2S7bY2G#jTqzaa*{}dMa)eVhV1EPslv4`ByMCGols$Gq- z0v-MU^Tl|@5e5lVq-re z7&&jPT2^*TWmVbAmAAXW9KE?vqQ-AGR2y?Dwn^_s^o6R3YH4Q09k()DE>+k)zFxNR z*a7yMyS<^*)2rr|l-J+?z7@%nPb$zWs7~9C>aU*^TOnnc6VD>q(CUq`c}UgPG}bC* zB0cHRebI9pWy*BrcD#Vng~qYCv-^0POo+;-yq0x~H8R+#EW)~vk3a^*Y{v@dbqXCE zdR#k(l}Ro#jBnPGm&hg+6jpT1kcv0mZ$5&^8HTkTBaA^+jAc1d{Y^1dU(;G|QwlQg zRgaRP9W^Xdy92Z12FVUI9@txG^@%n~PxzJ&(b%*jh4DyhXdv-wjB(AT7EBNczn+FfvFbAf8zw=YYE;;z3NXYSp*tJe>Qxo5$iNGP zF=6(an8g>SLL1g8d9=>2V}rqw#7BAOPcDk4}h4LEYRMt-^38lNT$AGUOdo* z(a}Td*_1a&Xt00hw}9`4wb>O|&uMOH^YI)ks+(TVOQpq(&$HH#`U zWAS`71NE^N$jeAT<#0?SJ@!3TQ@O0?iZ)Q05t3E{SB9id=(z>T=~|@W_`+F*buwpb zZdt^6xlby=4#OuFse4<8g7H-pHdk%7_-I?=$rsI4Tq}NU^V)UTnJ(X`3X`9Cplnw<4|oaFEIEy3=swQ~K2nns_X0;=gBb3sPx4S8&`?Yl5GQb z`Wxz)X>Q~ID-Es7*JYcQlY@r3kk}#pSuK`jsSv-{+?377b7Z`ribX~YNH$MA|9BP| z(GpT*oPdBZRA{lRZf+sUKW5}Be=w!$i0r0jaRX5{uU4&${h=KxC@im`1!G#|q9X-F zYI*}R7GnTzTZ_|TH&#^OCjlg2&VJp2>`9j=lg2x%>InS^Jr(AIKO>qsF85#1awwfgCAT{^Ww zgtYDS?A-iBG{EF3W`1mXRxVl1NU_3%a1xS2%2$$0OIQR(v*`7W)(Y(9RWrP#!CEXy zyHtayw1*0+3o!XJWKyVOu}eHap~S=FWE+ht_D}(Ok%*F+xh#mbd6*yz#U3hP&@?xc{MtEb(jF$rOtFUwlt347wvkc*n=XyQ6h~q_M39R#yo?zw zR6qSqOe<0d!i-`}9oAguMll(QNY9y_zXqZxB3<~vmY$uTBGBOpBN6FYZW6oeE%t%~ zl@*C7DRC*~REdXjsY!zdCXbSO>4GFn($TqjD9>88Ht%N?BSM}`nVikh%^<*YNhBui zVS@Y?dnlKj*0@MS3EVk`*Je5dBk}|$PtkPAX61{LE}a#TYjFrh=jtJX#ZJ%5n^`f? z;YEx@q~|h;9%R%{fAiKdjYOp9L}geUg3+Z@h>#v3Jv)+?!h|G2c%Irx&vFSyC@v!8 zNw1QJQ`9jk)Gw|l%qQyDxiuA zq3y(y$mD4c9`rzyK-l)k!@?+DpTD3w~DU7bmqqB-c zq~{gqQ=MXif@w-hBFUwJ%G08YU36rVc1gSJNUKP@m>@stXjHL6g;W8<*$7>tZg#p- zOCm984-;gh7^r-ukIZal6eb;sDF#O86(vgGk)8r~o-2@)k3f4asa~6b4qTX*2rHsn#Pn(!ytQ@wMrhDesf} zTZhbG4S5=|^!nNZ@_#5LVNUvM? zsut%s<3LZBT*rr7C*Mc<#l2#Z z1*?Yh9bA3_^lJ50JB`aJZ^ueaZw~x(@$<^v!l(vSUCU4Ph5D7ddpb18-zzmbeToW; z&jk&dzuwy6d~ZAFBjx3t3xx~P>s_tsbxWycHfefWRu6Zc5gFJ-O?_|*879A1KBeiI zv==<6=@qsOcf^%lP<{b@_Sf&0jl!QOT35TKw`;?2zW1B+vFn!#7}V$b&uMyf(zZK3 zuVF*T54rkBeQwiNz5VU&NAiH$9?|DvkNPS2A(x)ph=cX9Wt&d1?Hih-u0Pl1ee3?= z&hLDZ)A*_?w`x^Dklwhty0v!c=+3P-6sY$5z<&;R!n!w+-@2COw&q6oFbL9nMd%q; zE?A)H?EpP;4lx*%@T1yq!~Hca0{iQ?N9dXMmIA$?%!GBDo@&2g{Jm7F%WL$nuuSu} z3;c!apXP5ZZfrL2es7?EY5$m}S13&EDz|$-6&NV5I*(o32J2Y41gKiN(B^-O(Cc_i zhjxJ;Db}Mlp4K|r2WpJ07Bx8jeVLij-A-)OY!k0MUF4nC&;N1DI0^YEI)&fcoz&Uf!D*!lMIVLSUa z4d2;Uk=XurL89|ufzx?0*xFQ(IaFWJzI$qYeedXvnNrAaTOyG;gv=g$7jX!W1-!3d zY$wCo3!G;UA^Z}j{l~}M#;Z7ofloQ@x9<~}@w*EqAkJuoAMcFc z+AX-L1($5ad+>nA0I&04{OZ1=&q2UN%!mAKxwpR8xiGVPU!tAe=VNapj^UO0Gt606 z^yR)yl_={%l(_439R?b~wto4fRXf zkL|b0g!+LPoNcwhVF=mwG`_nFiaIZ!q|5I>E;etve2JHjG$&BzB+6a@84LsOsPj!{ zp}dLh?-V3a?}<*o?2<}$6X!YW%SJjY$!{U~PPFgd0hv=LDEBhRf;<|XppF>1gHcr% zmmq9k!6k}b_i*Rg_a%HJ>4EM9`6m6IE1jK6Z$@Me?Ju}krFjqaNWEk^_d#FwLnoN0 z)+HUM7h&W<>zvl7q%-E6ccFhK9nek3*Aa$EtuVXrgU8&{UarEd|=$cPYUqhW$&2rYa zLpKki4y#aCU2qvnJw?}{>WuY)aiYlR8}e{}mxt(aSQ(6745SQE`uXByU5=n`zI*3) zAycpInLfzy%erB`Q4bTt{Cdc?UkE*%c)hd!va`JM8vUcrkWP{4*Jyle{m?X7HywD2 zQui^s-kqZ(=KgEa`Ca$euTC($zI*2js-Fmx4g7|yJO(10-1;5e`QkJ!m;a6W4WoZ7 zvd^Vo)OkSY@236~B+(Bfp|=Il-(k?>;gCK0fK4NI_CbdajBpOUS5VN|3tc({-7;m< zZMs0!gW<>Y1wE*PQK*BX4<>d#xCnX&{qyQh_uJj)II5j9%^36_tkZ<_8|-JJj+(_H)JkvK~E~a)xz2|2ReHDHFKG1#zPpBZTZTM4taW8p1-#K*u z9Ot&ZmpT_6Iwx~z;zykIJs1~$o^+n$n9(I;MHky5=>Rq~ARD*+bqg=RJ;rv)Z~n30 zq|7+ZjYoe@UTQ#t`7tl}nzm#-T8*zP8`Dlh8rDOZFOIGUQ!i#b#+WwC4TGFsQ*zYp z;3A7p6V7x8q&>a{9+}qGgCxcSL$4eCH~HBDITe0X^SuS~plrK^*Ln+A4_r57$uxbN zZiHSgf__#)PZvU8E6~rD?}YxUem2-=R4sJY?-<2CP>*3f1gU}w@|>-i#6;Cc8=l*fg-VaAlTPU$NqOydOkPYMVuweK&AIJwROi>zo~^)s1T>4P zle(OgRgjl^g^yr3YyqV=!SH(`!taU*pBWK89pUew?UN_n-iIlDq72L!ro)TW`1_iS zlcp^9C!IrhvLcj6$oIQ&&mM}y8ct}q8MA`r1uZ@W6PgBgYhKxoibw~VeVE>d(#*rh zp|J6RGD@P&7T^y{KR;%D!#>Dse;q6s2HUFu{cX~1Q{9uDonBq&zK8YD1v{JJ)JN7$ z*V)dYSqK}Azk^AQqg~E8w83f1|NQLCp=#LkPc6#qGPX4PT82Grg|SWR{*X5N1Xcg7 zXF0!1{O8e?93%U)f8JjHKQNYHEJt5Ld&KY`kcYC-4`ST?DgN5Q+baB-xGWRLhrUf^ z=mWm*j=Q09tw_B5?wv`*D?q-O#}p)xFZ!inup<-BbJd_rdZbM~IygDu$rL;i*SBdQ z(pR9bLH|;r?Ks*XOk=ls#`TYfdTvpFR^|}xF$m$UzVA=x?F zS1vs^cNEfQb(EY<|5y(@Y>YKeKL}gS)b)E(H`FDTo4Q3ihV?WaVP-zwKFoRc2*O99 z-y+V;%Py95dhVs?an!YL%<1;Y9O^RY%YLzENP~4?aAgXo=WIpJC79D|`+G}r)YegI zjz3Pz4tz0f#}PfpH++~fz7CsWYJ-2i--ABPSz3tseg3)G(ZJlP2l-SDbJk~IC#kv9 z)z11S@w*#se_uf%+M9oV9~Be4~jfBVpm_DlQXnDah(H+n?+D!scd zKzK~2);+D)@1qSKLV9JVgSOT^t#3(Z+&R4pf3eDsd69Rm$74G6nt-OG!^qo&&co0< z&Xr#iz3oQ+rrhMI`ViL!^4qLI{W}QRbN>F|Su$sz;kVg2AE1ocX6^b1zbZV~$Da(H z`+AYqIXgein*RUYJIi42zqo^C0gG8nz3&N3d`uN}@Y zVKdr{?*GQ3AMxyq-YTpW!N#a|CjQJG!`c6s@yo8){n9@DI_9cAbFR08%Q%YZs8x5gNi9yUz}Y%{hGlb&^Aen;s9ZCKLem}SdxjM53+ z{~Nh+?4@1A{QNpW8SD9KHTdXu`Or3kts_CbAzyYGM@hMCnKD1WJpE-1+8#!>0UP$D z+Vl>_UFJhR$zRvm&Y?Eg^LOB{1b@Vh1&-ge>G?e#-O7ux#v zp4>sK6&hRL*!5bEbwA2J*~r6`sT%t*97Eoj=++&fZ@-S z|8>}jIr9LnH{Qj0z_Hn3J;-0<517gIzToHqTkG;jq@fsSs-=$?eBFPhdOYJR&$x;C!IhH&5`| z5zA+f{~62^Xag|ZKTk-iwHaU7H=UimSZn8c@xHN~GiZI*wms$xqs)9kw^OC7vWCn& zR61FQHuC|kr&?#L*@))WzL}K={W=D9+=U}sH25g#M1sSIE2pddhh#<;ga0)+_n@ zbj%@!T@*ft2(H<4{zIFWwsn^^e<0uNCpaE6oO1_LHir9cMa~~ayK{B3zJ9vq4rVSy ze$Cv$v}e<=nK=>qWbW^%bufeT&mFkl`r({A*m5-bVB}_W!Hh?KeemXI@!a7U<_=%T z^qPHvcUC_wTCcld^|TW zdwCyzl^zi{}BSZ60vwbnhX=^MHY$3py!#2=P4Nw4M+8 z&D-%jAf5-r^8l{%opE~zMb;j|RM|tAbY|=!Ts|hACw!>p31egrVY~M3ofc=nt-WBVqJQuR97I%|8{{gsXezCT>x_M7HJ~!04?@T>6l;?aq@3nC~eKYjT zoTK3RNZv2w-3EKSGH2DPcRVK+)Gz9lIma~+y@QU1=-t@PuZ!Nj3cVY^8KF|cH}fBo zdjo-cM(S)o?iJYb<2lBCzVknY>Kr`6l$>$D+r8HS_e<+1UAW~-=1zfqH{%rCBPfmc z06tuHe8-~9p4g5*eRlkmNwFP2)b{|oa9{X%?h){=@P7BMu>TH0qK|il2j>n!^&s6L z$ehAE1l`ARe;_j$_Xl9Vzdkv!*1ii_cuB{yiRZ#U5zb*}a8K0v=#FK^et&&(*;-@2 zn|m0xy+k|aWZVOIEYZ z?El5U(>9__)PwW>{5=N0)!BWVy~#mRp}S*l%lLf{rRo1^)70D2we=Z z(?@q+eyP^4f!OI8C-dDQZ>Q&x|0vk&=3WBJQ3QST+w0~ol)j_jJU{7D+|laWi~7NR zBI{hgj>q|Tt-2Ge&nVjSEK`4{z?T2+>9OVMTTtH#j?eR)+VeaeU&tJZZTZt@%l}|r zY|F>Ce7LXB<2}hdvD@TfT1M zG1>CU*UnHsj~_LcI#yf02X=f9e2EWXJH9Wr;|Fg|ez^GTH1k*E$J2!O@3U_>{4fr~ zK3f6SR{Hnn*&guy!+g2_a}@rY3KM_+rLIYdooz$R7f{cy{`~eG8@|^v(J8!-KBj_x zm$Zw)jF8*X%+NM$@ENP`=E-fBh2lC>ndvPZK(PMy)B+6 z4E1?J_i?WIQ|J9YLjCLh+DB--?r(fPvft2WVDy^mz9F>kpL=cx>$LDaZr1&^zr5UY zJA=6{yYLeKx_^Ib{>S1M^kn$^pTi=gOhs@5ij;C!OQ>^<#XI*l*de%v}TV z^Mg5Pu3qW$VDxKW^R)#(SDco zUX8I27_RLH^UXW8UOT1FQQ9yAIDfR;&?LPfdHyI=pR{jN@^1EkxPIc_)6nu~ne2F4 z_FB#y%gi|>)}<{&>LB+pa%HG(LF32Tw{f=~E;&hOhi{H=`KcOBH2gYzjyMw9{l>OPD4_MhUfRp#A#9T{g2 z#K)%HwwQJTWJ$U0fqbsRn-Yeede;tT4$N6<(r27sa-SF94=dlhx=m4z#%K6`IVyit44Xq&Hpg@5(;KUWm77hHYnCG3|WE=8Jos1I}{D z>M!BL^)CbdzTNkpc;*9k$@bSKC*b$JsB>6S&$Bu(#d5H@e2a-!{XT z&&0&oH>&xbmDr~}Je#y}BYf=XeT)vgy;3^D+5Wxc_%~|swvNfi-2E_lF`pFPyG+5> z-Ld)ZUhV5Xb7&g!Sdb`ugY(Y5O}C+}x1!9;QTAo_wUn^@i(J;C#HEWhc~?0(EY z*Yv;rVB1(HI&TTigSj8{HtaUq@vAI5lDx4!+P1rGQ_^k^mc!WL{yOyD)1ZCc<-VKY z&4F33=KfIFocMTT78pN^jI49)$$tA|Z~e%~q&wmbJV$JI>NRyaelG*<^$fGuvws-d z>)i$3Sz&V)-K?o`Jx$G*r5}@idjRjw#P4Oq?`53CGjj1=@H6%9=)!u3vB{17ISXN< z@fW|rJjCB%?uhNqb(>0XPP(3+17L%-nrCkg6zNj=c|y; z+OLe?VAg9)`dzDmuNe-?8_WY(Gdz~_NnAS)dykB3hVi?_C+FSbFuTFnk;Z0--~QF@ z%5O`?Z~xLp8iO@Dv+u=qb_Z+e@!P-JKP2tzld_f>`|WqnO?6kCdf%H*|6OcT9+yqo z@18)smKm>Q#%q~oKaBoyKCJKI4qz=)?US1QZO#q4uy(a7em@pxgUWF>2=@fwC+HNc zWyZeWVS{|A_R?ef^R(KZmH!yqpRxTJzZ=E%;o$vIweEQwcSuu%;X5+kOaCD6rN{Os zZ0;ejmmb@nA2$0letYP&+MmC9Ew(>n`!lvbWBXIzk>eSSFrVN&%MsiYN;Jc4=YS@`}TzUfzvKHKO6Jv%M7RgY_3G``cW_6v>g%l1L?eOZ0deP8M| zQDgrM#0O@Voaa2%J}^)9efF+__`tl6J}}|0((-|M7_jz(c}b`b%=gv3T`hOz7e@JP z)v!)C#`nhd`~CXHycm8RE`bfJ{lMzqV>-28Oyet)c=Dz1vU|QUFGjfb14>>x)c$#X z-NE7situt3bQZyH+!=R2zaZ=_l=$rm`T?|kL7gi9Q~lec*4q`01An{X z2;S@1gZJaKZ(Kcxb>KUR=Z3VeXue&c*EWow7oHueF3@jR{NAW1@Mgt$^Z|N5Y#(s8 z4;c0Ch2;b0`62dZ1$~Co4+8DmF=w!Ugj4Zmg)OVGN>l0VU zt&ZQUQ2yHY!^gDkUpRiV;`F@_|MZRVylp7X+o}iXi>$wKCw{XcezPKev%-0d?_OxX ze%jU>$b3D1vqJBG^KF2h_|1y=%?ivFc($MCkjxv{xNlNq&kN%>E4=#;+)wlN5BxIw zK)qR^d~fJ^uzqKbcT2ptmiWe80&h&|H|7TZZe#q$T<$*04{Bn2{kZJ)*w5R}z8833 zCD}RFde6hWOWZ!vc{cX*7W;YA`}45(Vn1(qyD^D7UJlxf-pd#R`-lGAjIZI4dyYQ; z8~$~`BDH2{zkNm932*GY+_$L!ZPRhzh&1oOI*E)rn^c(a?i+TWGqH33SjWw4 z|77EDv_ieZ&b9ieby2l$e}T6z!}=br)--e9rBHipC@=1T97-adK9jE3GHKfk0Y4kN zzv{lTU4}O(zpvg;Pcx-=8 zJS}HdV*69Si96KRKf6xk%{0ruus#Q5>`rZq>UB?j9!Q=2LHmo>J-xMX_EDzK;{E~q zr+S=+8Hsu2W!zK1JIndbmk}w5n-w^#r^9mXNWM{9fi;J6tT~k7U56KUpg%utYnk@>OtY5Bwc4L0=|jVN8XVhr))31a!+i3#`b5tm(IO_pg&=?juyX}ruIqVH!Dud zn-xC0Ge7Z!aMXGj`J*iw%B)&%x|83jn8Z*UOv*C z*m-zO5^M7X(9dDe)6;imvm@-yirCIPWp?IoejMAGL*34d{Rqc?gpV{ApwAwLK6^O& z?6c56jKI2C;m*E-wBFGGrMU|)6VXm=Tk%1pK=Erce$9^&Jgw>sJ+!5k$?iF^! z57NGKGKcoVR~qvA7{()gmG;c{Bm7;;VyNE~Zw%g?t41Yb`2jx?R=&a z@D<#3wsWWrXEyJ^Ux}RE94loQC*R}oeF^mS7kD3qcY#9RtcY#RQ)X*^{`J__jBU+@ zuwg1-%ardt(prAMbSP@I_3=L1lNvbxVCA>27CqMTszX5ce}dbA9x@93o}0L z+|1#l&-HCu44uCby1xi*pb~9iA=*gA&etcGuRU6kRPAH{@1sS%Suu3hEGrUthwtRr znz#?&*OM61c4qu$MTN6{u{!TEdLcVf*j(Z2h2tPf6Y(Cd-=FrKq-?cqA$U4>un+m!$91lUHHJLF#< zJ(hPac;24tr@{46u7ifHkB05R^A1vQeN?TBUYS(uqGrw1td;hj@91^XvEDkVULVDK z7o&LJW|Zr5UagTrUdDD*YoxMfs?!*|Ro_2<2kWQr@{J0$?rPRljSV~goI?u7IHO_5 z2H(6ec5LolcC$96ZCQOEh-L0lcaXwuS!2hB?ZX}{@a|aPy^BNI_8z8eWUh;4V2hi3 zM@Dzd9nqvSW@~prp&OTYdyM9~)_z$>CXWwcopKWB@qHpQjx!ErtM6Co@{o=RXFTHd zckVqW&SU$t&(8?|PzCWj7vFPzM|tNsf@`PBUslNa>3;0TP)Gc0sJgw;_Bx35+MnXj z(=CmIU+++#RRbAIN%!air_@p@ORGPOe^j#$V&P5O2 z$=HE+GB%Ccg7+@8Ur&~MAJY5ZyFlMzy?61@5PR>!yhCH^gJn1q*Wsi5y3r@uJJ=t@ z-Nw$sb6MX>RUhpMXD-Xkev;oDi<0u2eIE7+G2}VhYVn>MWpY2((Sx!wYa`rK>_(qz zWN5|)|JkT9?%NpE@p~5-)5mq@eeXi;P3XOE?L$J}_1!P)M$b)hw$qP9S)cKro8L~d zeUGs3GIbTW3#raqnm&sCz$*NixYPsA0qiqX1{a`HM12hICn_Br1|O0M=ecUoB|WC6 z?j4-0?`Ny=7HbB?+4mg=`_A9+R6N(~Mw>VO90xqt+njj$@R|hHER$HfEWjEj_Kr8< zOj(8YDa&z*?|m6PF?7{A1k^px2oN_`zPW#Ak4f=+7cv*xxUtu~DcF&4W|fX`wtp`< z{*4;=&xWnB{k6#nyt_FH>&iu)!;*RqMH#31jy{)yt-E7${N4ramk!9I8}p^UP5J$e z55(>o?bB;LXUGhaACc5a_ahR&cfo#VH2NDeuXG0TKIMn^y$iPw=h*DG?YKVS*#*E4 zp3jdHJo9dR6Yp2&#P$5{=nKyFYRB&r?s)brY#S?n@4`Ft=AR4u&*ul%j*Xp=V^6*? zw9c+Sb&B_GBINR+wb!T2kL~s2ve)}{QGnh(T&2G*f^s}fk7@Skmu-@EYaGp@&WxaY|Adl|Ih?RCDeJ-hh5 zi_>>^GPXZY?|ShMZjJ5F*#3-tbZ{+*`_FpqS)XZE_RDdc4@!MV&IiTzC(ane_Gf2N z{N9?{1CH%a*A_2|Sm!J1e?Q7QKSvteQ}2#_6n|jPD`eNi_GfH=#`b4yf5z{vO;TsM zbI+)sg7r_|o;c6jbm1H%-^eiIIQ7%NS02CjI|yf|Px~7g!LzZf_cQSvvC-SX+<)&9 zpB)ZtRLY6|JZ2$mH2w}I_1Sp)JmAH!XMc|KqSf%@VZV*Rv+4}f@BGH^t-X6^`OZN< zC;PeB?mRBLbCA!;cB9?!jvCL`?!!H${kZSNw#7Emjo;=DhmOPh8spr1D)xPieN1QF z*Vw1-YgFP~??UXMRp6X&IeZhqcgJS&-7$dk1YzeSI|k{TWcx`!C#mfUy(c^HGm{;% zmN_bT*AZt=bM88#4|A6Cu46xEQVa6?Nue*eW4Y@{dnN3yBkvKuXPx^u_ui;G93LjA zGn4UqYw>$)+E1L>OSj(%)8}d($vy|WJUFIIVVOZ_Mfg^X|{WfL- zJ~B>pZ7s^i{2=&p>pqUNnuF!b?e)pYwf*^XV_k49B!2H=Nctd--@Aa%B;)Iu^WXTr zi(KDPoMXiMq<-^!Y-`50X8hho z@NQ6SYtp}&UMn~I>G9iY@p~6sn=Dd$y`B#~V?&y?*!aDRs5gAIe^Z__`XHafH1ArN zb^7?di`=)QY#&@)C*b&F!9R56eN8`ArxaB;Y>%h6I ziH@`0yxp+(qAe5Z@$N+Y-UZLp^3H@l(`N2Q#P40$w)4<>cbe_b!5O=Z5)( z$n~AYK7#MwvhQG@!~GfVJ3NcMh(E<&tL#b8uLS?F7NO2k+h?komUlxq2j7D`u-D<8 z2SYD@??TIz@)!#5U1WRXx!!Tj^)|=vT~LS3IZ^ed3t+BYns+GdHz+!Ar_$~V{ng!VQdQ%6Jy_~cAS;;A;x}3z5N%zcL94u-#y$1yM+GgDI3aCy#a`K zfcXB>YZ8ZN^@;Ds-5}e3iQl_0cVY*AchkWeA$SL;=v3d`JeG4!v`wS#ZXWjC-PAU% z*=rh&_7=Z)q2E)t*NXYx!%Dn6Y3!9%^4s)tejgt3dl%?yifs9v=vnpPJop`~CA^LG zdyX%wEIZQJPf6&p8Ha-J)R{Uoc=Jx3zYe`Sl)Nk1<=&O_-h5;k&7FXE?|flL@O=L9 zyb*Fb-n-}yIZw0_VYBf!%ChTipBt3jc#$z}divnlLccavT_~F#^JZKAuNprc>U|9F z4%lYAXQ$tn)%_9Qu6xLRyDtCzFI&&pN1A#K_$c|%zJ)P-)FbgbUAfzepKb`RP&hgId9ZDK|SOC%0bKv^_%B<&q>dB^!ka8L%q>@qTe?k-+2gU zs;HBU>%Vhp&d8fL&guI`&!zNU4(CytmWIc6z6T#Ie&2EQpQGnjELZ06(dYVom%boO zcM|G6LKXC<2Kr;_nR-7-`LGMuCBEMq)EVju*X)AfQM!bAV~8$|>HNCr(yyRP{&80C zx#AWianWQ2)^mUhdmeu(J<$DM>@sxvUvN_xp61cNeq!9H+16n~9zK z$LfAb?RQKz@4!~5{Sw+=M|19gCX^p#!zaJ>UWqpzn0Dpu#Zb=CZ@9NgA8_x9irXMo4NNn@!alqKC zvCWJA;3WB&i|t9;o2S;EwCxCU&c=-4v2CW`powiW&#vWqC2cQb=bH7)pe+`JBkhA1 zPM$A~(_))p{n4yb(8i6|BddP)c0X$roJW~8pn9y4({IjY48yx3!|?kNtVf)M?;py# z1kXsmhPicRd2E}-wpjuE1rLMYVEc{4WT!dTsC7%vIb+)_w$1j-oyaqBt*;;3%(wm< zubY{9nSPfT>t=)gc0+J)IbJu@W4N*PFjjHTLi^*8{a~!ox@SNKeXlsS%`i?@V7wgs z_ZxomiP$!aZL`=mJ00iy;&rnjw{8~OX8HFtW7|y5{RP*_g0QirK5%=Q>TQJh9N($1 z%|5?Aw#{POEVj*J+f2?&eZbGi^|NkPYQ|KqO`NWEGv@+*R?2=ipbB^B`EEb~?u_sb zzrP&Eb#`pf-vWr&z{2i9hx=bW9s83V>TQ77&N>x#)|5Yt?X00_XF2`cm+<@e_urLx zevs};qz37(gmV(_O7NU6$H*e*lC9<(;Fjd5t)mhS;Bh#+uKN|9T~7_t-PYG9C*Zd~ ziML@3@J8$~tY!QCQ1ETiW4YhTd(L6^Tf5J9c8A?VF!xb-=Y;pADUZxq-aQ0${=7h+ zE$4j{+7`UCVD=l$9Rz)k0Cxb8KhHk!p27a}GrLJo$JMefyrg59zR#xjuo>U^=njPG zeXXQ3_MG?BorLi&9ZiEY_1$d)GoA5KrQhn+{IkBC_oMF`ygs>XE$)A858g`*o)617 z8J|!7(`8HbcXr?DnslkL`MO6qeS2{yq6c+m&bU%u>g*2AuT##v$6?MlfwuCebP{Z_ z?+*66Os`Mgv-aIP6WH^C-z>Q!!Mm>1sbjtGx*_B&KlRDT%9W?`6(D=A5pRROKKaE) zq{Epp?>-a5s9(YLt{`mk`JkVQu{+KF6CJ&Lza3lo3- zrLIYdor8Y9Y!H2s@J_I?ePjRf`R@e`|4wY*4MqDd)z7*c=RN*6-2H2BgXO2>OtA6x z4S}_{{?^@2^|`ZdYu)X1`2R3#X-Vh!e1CYp-u-?*+6UnGBkaxXg9YB+Q#Jg`?E_4E zPx;)%8kg}cqQ^$>o>kHrgZ0}&H!ksx*otP%*)RIX^~Z#km`=+BLo5s(JKfSaXzbjQe>GYu6@^ zz&iP3t!O(L{G9{e$n~q7?G5G5_Op@axybVxm zD%bZF_0Gtb|-zXb>@L)Z?4Z5fl<-GIM|iIMj`iMmUn zu5gFCh~KOy2j7NYRd3aq(vfwU(o~{wD`EQ6%GF~ngCeUP+A`=~?(8&sXJ%iLWDcwuJuV#nD{In+Cu(j`u2_t}MoTbEqu>@IW)w@!oXM%Fl`(Q=Y{ zMxHD;-=|@jo=4d`Q1<8Xwlsg8>cBd=6@OZe;9n*<7fi1Adfu@GNp^rXB%GkLFXO@k9OS4B(A-O z%Dj&>e+T>c3GCx1v5#MXwmuB|l(@%Tk-!^2^hMWx`z7?@j=!<^qfhs7@aI0Vpndm} z`i9apx;Ud`mqn|lBDH)a?F?eHs$)vJktC_ zWzP=89-=KxeP;hxm_Q!&^}R(X6Z0XD%!lU?F2--l;Sl<3#^-s0!|1p9o3;{peFygY zRFs|dRa2mS+;h%o&J-Zbl>^T(1m(avj6R2;ZLC_v*YMcRNy1ARWXCXV=W9EZb@Va( zQC9D~&oLptkKONM?AeF>&p~+>qwYA?-Gur(7k^RX&ovl#mSYV1BE}$l{7KUeU%)dvyZw3Nw?oPLy^$ai+78X(`JDWEmJQL+zY+T+jbb`j8se z-;T$1)z8P{dOWV9f8_i-9@pb>o$Ds?xb8n2ciiK8yER|Oxh>4sdk1B{{;)M)N1jpk z66Wi@n6LMKpy%u7eO}qeLuS7IFy`y{bLZ<{zvvJ{JiFT2R_<_a ztLF`o^YuXa-1+)Lf%*DJFkc^;Ikebu4$;1m`TC)=&&7Owq=U6XnXf-IfcZM| zb&T`%-ZOT-j{chZf0E|wJ~<3Yr*OWGvbyW=@wk3U{6aMnM3(Vu;Zb6IyU6MNJu9&j}tFHg>&v|Z*1;> zuNr6LReJ_yY+hxJ&B!xqY));wZ%+n1e4xkXPoBK7x%&jqzTs@I@9f(?^w~UHbT9rm zHnR`s*|+wKGT*JipOd=m(&w>mUY|hN7=+=EVciG|o^|6nAok__K8$mI+y^}sXWuRZ z&(-7=ekVBYb{9B(!SieNEFZ>a6_;hm6Ze?M=s-E$F*@W79L^qnH{&?prJOiMXW-Lg zB+kI4E_C4AT$(lIp%`ICV-y0kk6X?S)b8^qaJ!xp!a>_$%P}bTZQ&V3o z6Zzf0K=Yb2=1bk~gHBRM?YM(F=A&N=8}pxsK1Q64+hg`;`a2i*UVv8@uaN|+b+($4Y~7h?bcWi|0fvhj||FK zkA0YiayjRYJoAk8M=;hO`9P2L_YbkL{$X$5d>_a9FL?XrHD;_oJJVN#KPOe=jrHdt z41Ww8&{*Go>h{g6!E59=kMfy0Yc=?fig(Ooe4u>p7$354?u^XzVT|vy#`wOG*f&p& zbl@lA`1j2dW?VMq&*R4-bbJ@tJY?t?`Q)K9C}TTh>5lCvi#=ZFjP1g+8r#9I9d}U2 zcJyP%IJO@-Bgc01-2<7+4{&UEWiY_89q~`n*pB)d=-xTT_9GbEkMujXFU8o-HfzTA z6Wu$1;pDx&Y3{V8U~{ql3URjHzSsE+jK>!vd_4Y2@h;R3%!~Fq&U4kc-@$vF`*A+; zHSs6;1lH(bXZ1Qbdjmgd>N~UPx?bLgEBnu*E9sZAzk3J4`&i^Dt&8N=YJSwOQJpIj`QA~dYgW(?@#YO8}lJ8=JQ&}T)hzY*qEQ;#l(pk$Ngo|clfz4 z(T=h_B<1ghF0p;vVHwdcoA1MG{vP+>5;$X%#67qI+=IhixB`6-&fJf8pgRs^OPTRL z+~1)OW!;WO__&O-o%g^F;T~Amr$5!(0Uqe@!T4!#HqGtjU9Gu{gS)%B5A*KV_EweQ zF5VB+-L-+72N;R`^_{%-ACCtQrRcY(#scIgK}iw z8+8wlbua-u;4a>ee=c(8{sPmPJ9tNU2XE7+-l+x7v)XRJ{X4l^SQfZ{mqI+VW)QSR zDnjnwU55CiX?%vTF6ZNqcke!_))}JjB8K@2nFJo$zwmD5O4QY8(VzEFhAp4IzjrV6 zrvu;Aw|4pN`1Bm5LvxVlzd(noBmBG4rw7vc{JR=IAHlKU9r$aePg?S_O74SG&rN;U zcmE3@>sm+OncuK%_SO#RYm8mn!EqWqbt&EVeD+4YQEs>2QRv&uc>G}AK1;WA%7yXF z*!>{v0=qqv|Db)*uTQj)apPdYaL8vE_CE_c-&J>FwVrl|_z`e$w^GZ^+5WG}@7bSy zsj$#Fv;u9fuv*)n?s>-J^ebS-F_vwhe#m@22Vl=PtioCh_&K*ddM)Nyd=3;qhlkeSd4+<6+3njvHk!h1auZpN&De4G+|}KLrnzLswG! z@-cFFigJju;gTM2PlCo{;EnPM(s(#P!{a&Ew@TRfY2OvAhV9Gr`v=e8Rw?i5y(ad) z(Z>_@xz(RtL>r@D{|o5DC!jBZ<>Yv-Pv7VK1=V#p*&GGH2C0K={_5p zVO(F>hrV9+kR78WnY%bE0bcKYIG2hlpB>%~S|&g8 z-M8s`SmV(BsG0MzZVV6n-M8ua9gI5|bZlPE6dBq)JK3_ku5D?r`lFBi!L->ATz2D# z`yeUxX@4pHrUV8@4)G&jv(-O!kw-`LQUUAAHEs%*>rx~$HN zIOkI>4Q+_rR+3)Uys~DE1PICdYZ^CX=L@e^IT~7*G&Qs})HF7HI$P(|Wmng1Xl(m9 zBRj44HLP3IkZr7+@2qWIQ_)z{+Um4srPT9RtMblwvXIF_VFPk$XkET8+qAp|BpT`z zo%u|3&1 z7d9y2*MPKBtE3R9o?EhQ_04tjwI1ZJqq=NOV`Fozqy97B-)n9{wHRLQtQuNxZfL4o zzFG+$QCch4Z>VX6j9iU@eAd>0-3=b^N>Hj!sHa(otF^4D?hY-2J6fS?<;ea%qX?j& z$5+stg%rCw?duQuG`R-UE$Wz3g}E(tEh<5zLm`t&2`zj;mlY_4g`!-~SscUCi6I5V0;fE%66V#gn#`B7m(h88w&Sk;)#!;y|K z->Fj(!+8=S0j4CS#Z@3gv!w%rd?BNTx*Cpw?pWAT^C^{m4zu_B;yOrH!|r8iwrNdU zz17zuoTA-;-X+*(Z#T5;|Rgky4@NYS0!4ltHFRxwr*IE zFDCG{Pz--a6fsyq4|6DLA(M^~F=F}ns0zq00Is}e0XxU5KOT%{1R}$Ql))Wu)v#Oc znowf&tZ2h1B_pRBHCSq)XxBwuHoLCEH*VCdgWXYwLaEx(kTIvo>V|BqqrX>VVU%rX ztAV2XNk9@hE8*Q|%tbIo&04;WirG4UgEhXY;qaDhYirG#tg%Q`Soy{_v@gZbd

      ? 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

      s?&D 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 ^<5DE>2V44r>4+f?@Phg@O}~}?>q#3o;)-F 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

      >D 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

      LaEsjkud?p6J#t;YWW)z#8I`KC1A6(wlK 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 Y_i%-GTCS<~z{?NUHkjcrv%f$7zoB|3QUCw| literal 0 HcmV?d00001 diff --git a/third-party/webrtc/BUILD b/third-party/webrtc/BUILD index 37837b16..85f2628d 100644 --- a/third-party/webrtc/BUILD +++ b/third-party/webrtc/BUILD @@ -2767,6 +2767,7 @@ arm64_specific_sources = ["webrtc/" + path for path in [ arch_specific_sources = select({ "@build_bazel_rules_apple//apple:ios_arm64": common_arm_specific_sources + arm64_specific_sources, "//build-system:ios_sim_arm64": common_arm_specific_sources + arm64_specific_sources, + "//conditions:default": common_arm_specific_sources + arm64_specific_sources, }) common_flags = [ @@ -2782,12 +2783,12 @@ common_flags = [ "-DWEBRTC_HAVE_SCTP", "-DWEBRTC_NS_FLOAT", "-DRTC_DISABLE_TRACE_EVENTS", - #"-DWEBRTC_OPUS_SUPPORT_120MS_PTIME=1", +#"-DWEBRTC_OPUS_SUPPORT_120MS_PTIME=1", "-DWEBRTC_APM_DEBUG_DUMP=0", "-DBWE_TEST_LOGGING_COMPILE_TIME_ENABLE=0", "-DABSL_ALLOCATOR_NOTHROW=1", "-DDYNAMIC_ANNOTATIONS_ENABLED=0", - #"-DNS_BLOCK_ASSERTIONS=1", +#"-DNS_BLOCK_ASSERTIONS=1", "-DWEBRTC_ENABLE_PROTOBUF=0", "-DWEBRTC_ENABLE_AVX2", "-DWEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS=0", @@ -2807,6 +2808,7 @@ arm64_specific_flags = [ arch_specific_cflags = select({ "@build_bazel_rules_apple//apple:ios_arm64": common_flags + arm64_specific_flags, "//build-system:ios_sim_arm64": common_flags + arm64_specific_flags, + "//conditions:default": common_flags + arm64_specific_flags, }) dcsctp_sources = [ "webrtc/net/dcsctp/" + path for path in [ diff --git a/third-party/webrtc/crc32c/BUILD b/third-party/webrtc/crc32c/BUILD index 52d9138c..8a9db1c0 100644 --- a/third-party/webrtc/crc32c/BUILD +++ b/third-party/webrtc/crc32c/BUILD @@ -6,6 +6,9 @@ arch_specific_crc32c_sources = select({ "//build-system:ios_sim_arm64": [ "third_party/crc32c/src/crc32c_arm64.cc", ], + "//conditions:default": [ + "third_party/crc32c/src/crc32c_arm64.cc", + ], }) crc32c_sources = ["third_party/crc32c/src/" + x for x in [ diff --git a/third-party/webrtc/libsrtp/BUILD b/third-party/webrtc/libsrtp/BUILD index bc29d57c..420d0900 100644 --- a/third-party/webrtc/libsrtp/BUILD +++ b/third-party/webrtc/libsrtp/BUILD @@ -37,6 +37,7 @@ arm64_specific_flags = [ arch_specific_cflags = select({ "@build_bazel_rules_apple//apple:ios_arm64": common_flags + arm64_specific_flags, "//build-system:ios_sim_arm64": common_flags + arm64_specific_flags, + "//conditions:default": common_flags + arm64_specific_flags, }) optimization_flags = select({ diff --git a/versions.json b/versions.json index 6e69f7fb..50523d2e 100644 --- a/versions.json +++ b/versions.json @@ -1,6 +1,6 @@ { "app": "12.2.3", - "xcode": "26.0", - "bazel": "8.4.2:45e9388abf21d1107e146ea366ad080eb93cb6a5f3a4a3b048f78de0bc3faffa", + "xcode": "26.2", + "bazel": "8.4.2:ce73346274c379f77880db8bd8b9c8569885fe56f19386173760949da9078df0", "macos": "26" -} +} \ No newline at end of file

      iD09Yb z%6`x|gmlr}B=vJ1(nZ%K={<^Y>AoxJO_unyNqY4@-P1+xQ`&~{PtrdOdCU{6_AHMS z;eUs6As<3I=-$WpC!#({dcMK@K>qbh`JE76DfR8NaG$id&j~-N?u5~AFg*!%|8ZQ>&YwnovGX;k|91WXc6p;i{-{H~-od*a{9BIj+Z^)44mme+v~OUTTT~UO z+EiAzd3|TBjcONVuidq)Jy5l+v35^w?T+fY&PMLUY55B`8EIqKm_g*^*d{i`-(lJ| z+bO=XZJJbkW!qe<_{t%1=Kj^uH~Y=W35jz&<&ZeXQx1u9Jmru$$J+`!B+l`aL*g7y zIV8^UltbcN9dk!+M?S|>4vBL-<&Zd2*|s6~JU=$$*71z8SGOg`POs*6&+lZT@K|+C zhs0(Yvgdc5Y@DhULU;X+wnb@_4j`gs-9t>ya%gcyG^p#~+cMZtzOn4?O@%F;W!og> zQ#EI=Ara%J6($=$do2nvXzs_Z#i8cHyI6DY^k^j(+MmYOjZ(U?J%turJYAhftdH?@ zbq0uT>@DGSJ*IM;E#X6B99P*M<+#fBI4k8TZ#X~M*1UOreXPq&l(LNDD>Q53D%-P0 zDLeZ?-NvHygO=E~({>pq0LQ$1+Dn@I<;Th}9e}Dradhp8iD%bd%+ysI-J73u$Zroc z%WnHM8*2mYtxcP%8ry62zW!6+h+o;RYHVz--rNwVf1tSuwxZp=DN(epAWD_s-CIn5 zIXU*MvDCwRqDVJ;upIgo0B04^wxd)LOB{BM)nyRHqa}`6LF|<&&gTX~+eTbv9dTB@ zxXL=RlTr?eb9KxQ7C7P@PdOyc@sva298cLE=hz=O2#s<)U3-k<>DnV~>i(blnFM=o z$No%0Xl}>TwdZy`U3+eae}Vzueu&mhh2F8)zJ3t_JVMFj1k`3^ky&0*wu}7kR)p5ln5t3gWlTSznHopcmcH0u6Tx-@ z@7}VqKx;!&U7_hKS{y>}cbq}%T#IYOo@Y)YS| u{Vd}q9kGIy=5G!iyOABns18z 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⪼sg5vdRxYpdE~ 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 Db<2lbroxC)>mk5 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

  2. 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 I&(n?nA7oSOmjD0& literal 0 HcmV?d00001 diff --git a/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Headers/RecaptchaEnterpriseSDK.h b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Headers/RecaptchaEnterpriseSDK.h new file mode 100755 index 00000000..faa7f5ff --- /dev/null +++ b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Headers/RecaptchaEnterpriseSDK.h @@ -0,0 +1,519 @@ +// Generated by Apple Swift version 6.1.2 (swiftlang-6.1.2.1.2 clang-1700.0.13.5) +#ifndef RECAPTCHAENTERPRISESDK_SWIFT_H +#define RECAPTCHAENTERPRISESDK_SWIFT_H +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgcc-compat" + +#if !defined(__has_include) +# define __has_include(x) 0 +#endif +#if !defined(__has_attribute) +# define __has_attribute(x) 0 +#endif +#if !defined(__has_feature) +# define __has_feature(x) 0 +#endif +#if !defined(__has_warning) +# define __has_warning(x) 0 +#endif + +#if __has_include() +# include +#endif + +#pragma clang diagnostic ignored "-Wauto-import" +#if defined(__OBJC__) +#include +#endif +#if defined(__cplusplus) +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#endif +#if defined(__cplusplus) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnon-modular-include-in-framework-module" +#if defined(__arm64e__) && __has_include() +# include +#else +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-macro-identifier" +# ifndef __ptrauth_swift_value_witness_function_pointer +# define __ptrauth_swift_value_witness_function_pointer(x) +# endif +# ifndef __ptrauth_swift_class_method_pointer +# define __ptrauth_swift_class_method_pointer(x) +# endif +#pragma clang diagnostic pop +#endif +#pragma clang diagnostic pop +#endif + +#if !defined(SWIFT_TYPEDEFS) +# define SWIFT_TYPEDEFS 1 +# if __has_include() +# include +# elif !defined(__cplusplus) +typedef unsigned char char8_t; +typedef uint_least16_t char16_t; +typedef uint_least32_t char32_t; +# endif +typedef float swift_float2 __attribute__((__ext_vector_type__(2))); +typedef float swift_float3 __attribute__((__ext_vector_type__(3))); +typedef float swift_float4 __attribute__((__ext_vector_type__(4))); +typedef double swift_double2 __attribute__((__ext_vector_type__(2))); +typedef double swift_double3 __attribute__((__ext_vector_type__(3))); +typedef double swift_double4 __attribute__((__ext_vector_type__(4))); +typedef int swift_int2 __attribute__((__ext_vector_type__(2))); +typedef int swift_int3 __attribute__((__ext_vector_type__(3))); +typedef int swift_int4 __attribute__((__ext_vector_type__(4))); +typedef unsigned int swift_uint2 __attribute__((__ext_vector_type__(2))); +typedef unsigned int swift_uint3 __attribute__((__ext_vector_type__(3))); +typedef unsigned int swift_uint4 __attribute__((__ext_vector_type__(4))); +#endif + +#if !defined(SWIFT_PASTE) +# define SWIFT_PASTE_HELPER(x, y) x##y +# define SWIFT_PASTE(x, y) SWIFT_PASTE_HELPER(x, y) +#endif +#if !defined(SWIFT_METATYPE) +# define SWIFT_METATYPE(X) Class +#endif +#if !defined(SWIFT_CLASS_PROPERTY) +# if __has_feature(objc_class_property) +# define SWIFT_CLASS_PROPERTY(...) __VA_ARGS__ +# else +# define SWIFT_CLASS_PROPERTY(...) +# endif +#endif +#if !defined(SWIFT_RUNTIME_NAME) +# if __has_attribute(objc_runtime_name) +# define SWIFT_RUNTIME_NAME(X) __attribute__((objc_runtime_name(X))) +# else +# define SWIFT_RUNTIME_NAME(X) +# endif +#endif +#if !defined(SWIFT_COMPILE_NAME) +# if __has_attribute(swift_name) +# define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X))) +# else +# define SWIFT_COMPILE_NAME(X) +# endif +#endif +#if !defined(SWIFT_METHOD_FAMILY) +# if __has_attribute(objc_method_family) +# define SWIFT_METHOD_FAMILY(X) __attribute__((objc_method_family(X))) +# else +# define SWIFT_METHOD_FAMILY(X) +# endif +#endif +#if !defined(SWIFT_NOESCAPE) +# if __has_attribute(noescape) +# define SWIFT_NOESCAPE __attribute__((noescape)) +# else +# define SWIFT_NOESCAPE +# endif +#endif +#if !defined(SWIFT_RELEASES_ARGUMENT) +# if __has_attribute(ns_consumed) +# define SWIFT_RELEASES_ARGUMENT __attribute__((ns_consumed)) +# else +# define SWIFT_RELEASES_ARGUMENT +# endif +#endif +#if !defined(SWIFT_WARN_UNUSED_RESULT) +# if __has_attribute(warn_unused_result) +# define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +# else +# define SWIFT_WARN_UNUSED_RESULT +# endif +#endif +#if !defined(SWIFT_NORETURN) +# if __has_attribute(noreturn) +# define SWIFT_NORETURN __attribute__((noreturn)) +# else +# define SWIFT_NORETURN +# endif +#endif +#if !defined(SWIFT_CLASS_EXTRA) +# define SWIFT_CLASS_EXTRA +#endif +#if !defined(SWIFT_PROTOCOL_EXTRA) +# define SWIFT_PROTOCOL_EXTRA +#endif +#if !defined(SWIFT_ENUM_EXTRA) +# define SWIFT_ENUM_EXTRA +#endif +#if !defined(SWIFT_CLASS) +# if __has_attribute(objc_subclassing_restricted) +# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_CLASS_EXTRA +# define SWIFT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA +# else +# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA +# define SWIFT_CLASS_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA +# endif +#endif +#if !defined(SWIFT_RESILIENT_CLASS) +# if __has_attribute(objc_class_stub) +# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME) __attribute__((objc_class_stub)) +# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_class_stub)) SWIFT_CLASS_NAMED(SWIFT_NAME) +# else +# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME) +# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) SWIFT_CLASS_NAMED(SWIFT_NAME) +# endif +#endif +#if !defined(SWIFT_PROTOCOL) +# define SWIFT_PROTOCOL(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA +# define SWIFT_PROTOCOL_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA +#endif +#if !defined(SWIFT_EXTENSION) +# define SWIFT_EXTENSION(M) SWIFT_PASTE(M##_Swift_, __LINE__) +#endif +#if !defined(OBJC_DESIGNATED_INITIALIZER) +# if __has_attribute(objc_designated_initializer) +# define OBJC_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) +# else +# define OBJC_DESIGNATED_INITIALIZER +# endif +#endif +#if !defined(SWIFT_ENUM_ATTR) +# if __has_attribute(enum_extensibility) +# define SWIFT_ENUM_ATTR(_extensibility) __attribute__((enum_extensibility(_extensibility))) +# else +# define SWIFT_ENUM_ATTR(_extensibility) +# endif +#endif +#if !defined(SWIFT_ENUM) +# define SWIFT_ENUM(_type, _name, _extensibility) enum _name : _type _name; enum SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type +# if __has_feature(generalized_swift_name) +# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type +# else +# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) SWIFT_ENUM(_type, _name, _extensibility) +# endif +#endif +#if !defined(SWIFT_UNAVAILABLE) +# define SWIFT_UNAVAILABLE __attribute__((unavailable)) +#endif +#if !defined(SWIFT_UNAVAILABLE_MSG) +# define SWIFT_UNAVAILABLE_MSG(msg) __attribute__((unavailable(msg))) +#endif +#if !defined(SWIFT_AVAILABILITY) +# define SWIFT_AVAILABILITY(plat, ...) __attribute__((availability(plat, __VA_ARGS__))) +#endif +#if !defined(SWIFT_WEAK_IMPORT) +# define SWIFT_WEAK_IMPORT __attribute__((weak_import)) +#endif +#if !defined(SWIFT_DEPRECATED) +# define SWIFT_DEPRECATED __attribute__((deprecated)) +#endif +#if !defined(SWIFT_DEPRECATED_MSG) +# define SWIFT_DEPRECATED_MSG(...) __attribute__((deprecated(__VA_ARGS__))) +#endif +#if !defined(SWIFT_DEPRECATED_OBJC) +# if __has_feature(attribute_diagnose_if_objc) +# define SWIFT_DEPRECATED_OBJC(Msg) __attribute__((diagnose_if(1, Msg, "warning"))) +# else +# define SWIFT_DEPRECATED_OBJC(Msg) SWIFT_DEPRECATED_MSG(Msg) +# endif +#endif +#if defined(__OBJC__) +#if !defined(IBSegueAction) +# define IBSegueAction +#endif +#endif +#if !defined(SWIFT_EXTERN) +# if defined(__cplusplus) +# define SWIFT_EXTERN extern "C" +# else +# define SWIFT_EXTERN extern +# endif +#endif +#if !defined(SWIFT_CALL) +# define SWIFT_CALL __attribute__((swiftcall)) +#endif +#if !defined(SWIFT_INDIRECT_RESULT) +# define SWIFT_INDIRECT_RESULT __attribute__((swift_indirect_result)) +#endif +#if !defined(SWIFT_CONTEXT) +# define SWIFT_CONTEXT __attribute__((swift_context)) +#endif +#if !defined(SWIFT_ERROR_RESULT) +# define SWIFT_ERROR_RESULT __attribute__((swift_error_result)) +#endif +#if defined(__cplusplus) +# define SWIFT_NOEXCEPT noexcept +#else +# define SWIFT_NOEXCEPT +#endif +#if !defined(SWIFT_C_INLINE_THUNK) +# if __has_attribute(always_inline) +# if __has_attribute(nodebug) +# define SWIFT_C_INLINE_THUNK inline __attribute__((always_inline)) __attribute__((nodebug)) +# else +# define SWIFT_C_INLINE_THUNK inline __attribute__((always_inline)) +# endif +# else +# define SWIFT_C_INLINE_THUNK inline +# endif +#endif +#if defined(_WIN32) +#if !defined(SWIFT_IMPORT_STDLIB_SYMBOL) +# define SWIFT_IMPORT_STDLIB_SYMBOL __declspec(dllimport) +#endif +#else +#if !defined(SWIFT_IMPORT_STDLIB_SYMBOL) +# define SWIFT_IMPORT_STDLIB_SYMBOL +#endif +#endif +#if defined(__OBJC__) +#if 1 /* #if __has_feature(objc_modules) */ +#if __has_warning("-Watimport-in-framework-header") +#pragma clang diagnostic ignored "-Watimport-in-framework-header" +#endif +// Rewritten: @import Foundation; +// From module Foundation +#import +// Rewritten: @import ObjectiveC; +// From module ObjectiveC +// From module ObjectiveC.NSObjCRuntime +#import +// From module ObjectiveC.NSObject +#import +// From module ObjectiveC.message +#import +// From module ObjectiveC.objc +#import +// From module ObjectiveC.objc_api +#import +// From module ObjectiveC.objc_auto +#import +// From module ObjectiveC.objc_exception +#import +// From module ObjectiveC.objc_sync +#import +// From module ObjectiveC.runtime +#import +#endif + +#endif +#pragma clang diagnostic ignored "-Wproperty-attribute-mismatch" +#pragma clang diagnostic ignored "-Wduplicate-method-arg" +#if __has_warning("-Wpragma-clang-attribute") +# pragma clang diagnostic ignored "-Wpragma-clang-attribute" +#endif +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#pragma clang diagnostic ignored "-Wnullability" +#pragma clang diagnostic ignored "-Wdollar-in-identifier-extension" +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" + +#if __has_attribute(external_source_symbol) +# pragma push_macro("any") +# undef any +# pragma clang attribute push(__attribute__((external_source_symbol(language="Swift", defined_in="RecaptchaEnterpriseSDK",generated_declaration))), apply_to=any(function,enum,objc_interface,objc_category,objc_protocol)) +# pragma pop_macro("any") +#endif + +#if defined(__OBJC__) + +@class NSString; +@class RecaptchaClient; +@class NSError; +/// Interface to interact with reCAPTCHA. +SWIFT_CLASS("_TtC22RecaptchaEnterpriseSDK9Recaptcha") +@interface Recaptcha : NSObject +/// Builds a new reCAPTCHA Client for the given Site Key. +/// The SDK accepts one Site Key. Passing a different Site Key will throw an exception. +/// \param siteKey reCAPTCHA Site Key for the app. +/// +/// \param completion Callback function to return the RecaptchaClient or an error. +/// ++ (void)fetchClientWithSiteKey:(NSString * _Nonnull)siteKey completion:(void (^ _Nonnull)(RecaptchaClient * _Nullable, NSError * _Nullable))completion; +/// Builds a new reCAPTCHA Client for the given Site Key and timeout. +/// The SDK accepts one Site Key. Passing a different Site Key will +/// throw an exception. +/// At least a 10000 milliseconds timeout is suggested to allow for slow +/// networking, though in some cases longer timeouts may be necessary. The +/// minimum allowable value is 5000 milliseconds. +/// \param siteKey reCAPTCHA Site Key for the app. +/// +/// \param timeout Timeout for getClient in milliseconds. +/// +/// \param completion Callback function to return the RecaptchaClient or an error. +/// ++ (void)getClientWithSiteKey:(NSString * _Nonnull)siteKey withTimeout:(double)timeout completion:(void (^ _Nonnull)(RecaptchaClient * _Nullable, NSError * _Nullable))completion SWIFT_DEPRECATED_MSG("Use the new api `fetchClient(withSiteKey:completion:)` instead."); +/// Builds a new reCAPTCHA Client for the given Site Key and timeout. +/// The SDK accepts one Site Key. Passing a different Site Key will +/// throw an exception. +/// This function will timeout after 10 seconds. +/// \param siteKey reCAPTCHA Site Key for the app. +/// +/// \param completion Callback function to return the RecaptchaClient or an error. +/// ++ (void)getClientWithSiteKey:(NSString * _Nonnull)siteKey completion:(void (^ _Nonnull)(RecaptchaClient * _Nullable, NSError * _Nullable))completion SWIFT_DEPRECATED_MSG("Use the new api `fetchClient(withSiteKey:completion:)` instead."); +/// Builds a new reCAPTCHA Client for the given SiteKey. +/// This function will timeout after 10 seconds. +/// \param siteKey reCAPTCHA Site Key for the app. +/// +/// \param completionHandler Callback function to return the RecaptchaClient or an error. +/// ++ (void)getClientWithSiteKey:(NSString * _Nonnull)siteKey completionHandler:(void (^ _Nonnull)(RecaptchaClient * _Nullable, NSError * _Nullable))completionHandler SWIFT_DEPRECATED_MSG("Use the new api `fetchClient(withSiteKey:completion:)` instead."); +- (nonnull instancetype)init SWIFT_UNAVAILABLE; ++ (nonnull instancetype)new SWIFT_UNAVAILABLE_MSG("-init is unavailable"); +@end + +enum RecaptchaActionType : NSInteger; +/// Action intended to be protected by reCAPTCHA. This object should be passed +/// to RecaptchaClient.execute. +SWIFT_CLASS("_TtC22RecaptchaEnterpriseSDK15RecaptchaAction") +@interface RecaptchaAction : NSObject +/// Creates an object with a predefined reCAPTCHA action. +/// \param action The type of the action. +/// +/// +/// returns: +/// A RecaptchaAction object with the given action type. +- (nonnull instancetype)initWithAction:(enum RecaptchaActionType)action OBJC_DESIGNATED_INITIALIZER SWIFT_DEPRECATED_MSG("Please use customAction with the regular RecaptchaAction.custom() function"); +/// Indicates that the protected action is a Login lwPz0qSe. +SWIFT_CLASS_PROPERTY(@property (nonatomic, class, readonly, strong) RecaptchaAction * _Nonnull login;) ++ (RecaptchaAction * _Nonnull)login SWIFT_WARN_UNUSED_RESULT; +/// Indicates that the protected action is a Signup lwPz0qSe. +SWIFT_CLASS_PROPERTY(@property (nonatomic, class, readonly, strong) RecaptchaAction * _Nonnull signup;) ++ (RecaptchaAction * _Nonnull)signup SWIFT_WARN_UNUSED_RESULT; +/// Creates a custom action from a String. ++ (RecaptchaAction * _Nonnull)custom:(NSString * _Nonnull)action SWIFT_WARN_UNUSED_RESULT; +- (nonnull instancetype)init SWIFT_UNAVAILABLE; ++ (nonnull instancetype)new SWIFT_UNAVAILABLE_MSG("-init is unavailable"); +@end + +/// Action type intended to be protected by reCAPTCHA. +typedef SWIFT_ENUM(NSInteger, RecaptchaActionType, open) { +/// Indicates that the protected action is a Login lwPz0qSe. + RecaptchaActionTypeLogin = 0, +/// Indicates that the protected action is a Signup lwPz0qSe. + RecaptchaActionTypeSignup = 1, +/// When a custom action is specified, reCAPTCHA uses this value automatically. + RecaptchaActionTypeOther = 2, +}; + +@class RecaptchaToken; +@class RecaptchaError; +/// Interface to interact with reCAPTCHA. +SWIFT_CLASS("_TtC22RecaptchaEnterpriseSDK15RecaptchaClient") +@interface RecaptchaClient : NSObject +/// Executes reCAPTCHA on a user action. +/// It is suggested the usage of 10 seconds for the timeout. The minimum value +/// 5 seconds. +/// \param action The user action to protect. +/// +/// \param timeout Timeout for execute in milliseconds. +/// +/// \param completion Callback function to return the execute response. +/// +- (void)executeWithAction:(RecaptchaAction * _Nonnull)action withTimeout:(double)timeout completion:(void (^ _Nonnull)(NSString * _Nullable, NSError * _Nullable))completion; +/// Executes reCAPTCHA on a user action. +/// This function throws a timeout exception after 10 seconds. +/// \param action The user action to protect. +/// +/// \param completion Callback function to return the execute response. +/// +- (void)executeWithAction:(RecaptchaAction * _Nonnull)action completion:(void (^ _Nonnull)(NSString * _Nullable, NSError * _Nullable))completion; +/// Executes reCAPTCHA on a user action. +/// This function throws a timeout exception after 10 seconds. +/// \param action The user action to protect. +/// +/// \param completion Callback function to return the execute response. +/// +- (void)execute:(RecaptchaAction * _Nonnull)action completion:(void (^ _Nonnull)(NSString * _Nullable, NSError * _Nullable))completion; +/// Executes reCAPTCHA on a user action. +/// This function throws a timeout exception after 10 seconds. +/// \param action The user action to protect. +/// +/// \param completionHandler Callback function to return the execute response. +/// +- (void)execute:(RecaptchaAction * _Nonnull)action completionHandler:(void (^ _Nonnull)(RecaptchaToken * _Nullable, RecaptchaError * _Nullable))completionHandler SWIFT_DEPRECATED_MSG("Use `execute(withAction:completion:)` instead."); +- (nonnull instancetype)init SWIFT_UNAVAILABLE; ++ (nonnull instancetype)new SWIFT_UNAVAILABLE_MSG("-init is unavailable"); +@end + +SWIFT_CLASS("_TtC22RecaptchaEnterpriseSDK17RecaptchaConstant") +@interface RecaptchaConstant : NSObject +SWIFT_CLASS_PROPERTY(@property (nonatomic, class, readonly, copy) NSString * _Nonnull clientVersion;) ++ (NSString * _Nonnull)clientVersion SWIFT_WARN_UNUSED_RESULT; +SWIFT_CLASS_PROPERTY(@property (nonatomic, class, readonly) double defaultTimeoutExecute;) ++ (double)defaultTimeoutExecute SWIFT_WARN_UNUSED_RESULT; +SWIFT_CLASS_PROPERTY(@property (nonatomic, class, readonly) double defaultTimeoutInit;) ++ (double)defaultTimeoutInit SWIFT_WARN_UNUSED_RESULT; +- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER; +@end + +enum RecaptchaErrorCode : NSInteger; +@class NSCoder; +/// Error class for reCAPTCHA Events. +SWIFT_CLASS("_TtC22RecaptchaEnterpriseSDK14RecaptchaError") +@interface RecaptchaError : NSError +/// Code relative to the error that was thrown. It maps to RecaptchaErrorCode. +@property (nonatomic, readonly) enum RecaptchaErrorCode errorCode; +/// Human readable error message. +@property (nonatomic, readonly, copy) NSString * _Nonnull errorMessage; +/// Required by NSError but should not be used. +- (nonnull instancetype)initWithCoder:(NSCoder * _Nonnull)coder SWIFT_UNAVAILABLE; +- (nonnull instancetype)initWithDomain:(NSString * _Nonnull)domain code:(NSInteger)code userInfo:(NSDictionary * _Nullable)dict SWIFT_UNAVAILABLE; +@end + +/// List of errors that can be returned from the SDK. +/// IMPORTANT: This list is add-only. Never change any existing value, since this class is +/// publicly visible and customers rely on these values to do error checking. +typedef SWIFT_ENUM(NSInteger, RecaptchaErrorCode, open) { +/// Unknown error occurred during the lwPz0qSe. + RecaptchaErrorCodeErrorCodeUnknown = 0, +/// reCAPTCHA cannot connect to Google servers, please make sure the app has network access. + RecaptchaErrorCodeErrorNetworkError = 1, +/// The site key used to call reCAPTCHA is invalid. + RecaptchaErrorCodeErrorInvalidSiteKey = 2, +/// Cannot create a reCAPTCHA interface because the key used cannot be used on iOS. +/// Please register new site key with the key type set to “iOS App” via +/// Create Key. + RecaptchaErrorCodeErroInvalidKeyType = 3, +/// Cannot create a reCAPTCHA interface because the site key used doesn’t support the calling +/// package. + RecaptchaErrorCodeErrorInvalidPackageName = 4, +/// reCAPTCHA cannot accept the action used, see custom +/// action guidelines. + RecaptchaErrorCodeErrorInvalidAction = 5, +/// reCAPTCHA cannot accept timeout provided, see +/// timeout guidelines. + RecaptchaErrorCodeErrorInvalidTimeout = 6, +/// No network was found in the device. + RecaptchaErrorCodeErrorNoNetwork = 7, +/// reCAPTCHA has faced an internal error, please try again in a bit. + RecaptchaErrorCodeErrorCodeInternalError = 100, +}; + +/// Swift implementation for RecaptchaTokenSwift that holds the response of a successful +/// execute call. +SWIFT_CLASS("_TtC22RecaptchaEnterpriseSDK14RecaptchaToken") SWIFT_DEPRECATED_MSG("Newer implementations return the Token as a string.") +@interface RecaptchaToken : NSObject +/// The Token to be used for verification. +@property (nonatomic, readonly, copy) NSString * _Nonnull recaptchaToken; +- (nonnull instancetype)init:(NSString * _Nonnull)mobiletecqlyzr OBJC_DESIGNATED_INITIALIZER SWIFT_DEPRECATED_MSG("Newer implementations return the Token as a string."); +- (nonnull instancetype)init SWIFT_UNAVAILABLE; ++ (nonnull instancetype)new SWIFT_UNAVAILABLE_MSG("-init is unavailable"); +@end + +#endif +#if __has_attribute(external_source_symbol) +# pragma clang attribute pop +#endif +#if defined(__cplusplus) +#endif +#pragma clang diagnostic pop +#endif diff --git a/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Info.plist b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Info.plist new file mode 100755 index 0000000000000000000000000000000000000000..69423c7622d64c1ce75d8df15b5e7b0bb95f37c4 GIT binary patch literal 712 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 M+IVRVsblo|ABmV@NdN!< literal 0 HcmV?d00001 diff --git a/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/arm64.swiftinterface b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/arm64.swiftinterface new file mode 100755 index 00000000..b200e495 --- /dev/null +++ b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/arm64.swiftinterface @@ -0,0 +1,107 @@ +// swift-interface-format-version: 1.0 +// swift-compiler-version: Apple Swift version 6.1.2 (swiftlang-6.1.2.1.2 clang-1700.0.13.5) +// swift-module-flags: -target arm64-apple-ios15.0-simulator -enable-objc-interop -enable-library-evolution -swift-version 6 -enforce-exclusivity=checked -O -enable-experimental-feature AccessLevelOnImport -enable-bare-slash-regex -module-name RecaptchaEnterpriseSDK -package-name googlemac/iPhone/recaptcha/enterprise +// swift-module-flags-ignorable: -no-verify-emitted-module-interface -interface-compiler-version 6.1.2 +import CryptoKit +import DeviceCheck +import Foundation +import Network +import Security +import Swift +import UIKit +import WebKit +import _Concurrency +import _StringProcessing +import _SwiftConcurrencyShims +@objc @_inheritsConvenienceInitializers @objcMembers public class RecaptchaConstant : ObjectiveC.NSObject { + @objc public static let clientVersion: Swift.String + @objc public static let defaultTimeoutExecute: Swift.Double + @objc public static let defaultTimeoutInit: Swift.Double + @objc override dynamic public init() + @objc deinit +} +@_inheritsConvenienceInitializers @_hasMissingDesignatedInitializers @objc public class Recaptcha : ObjectiveC.NSObject { + @objc public static func fetchClient(withSiteKey siteKey: Swift.String, completion: @escaping @Sendable (RecaptchaEnterpriseSDK.RecaptchaClient?, Foundation.NSError?) -> Swift.Void) + public static func fetchClient(withSiteKey siteKey: Swift.String) async throws -> RecaptchaEnterpriseSDK.RecaptchaClient + @available(*, deprecated, message: "Use the new api `fetchClient(withSiteKey:completion:)` instead.") + @objc public static func getClient(withSiteKey siteKey: Swift.String, withTimeout timeout: Swift.Double, completion: @escaping @Sendable (RecaptchaEnterpriseSDK.RecaptchaClient?, Foundation.NSError?) -> Swift.Void) + @available(*, deprecated, message: "Use the new api `fetchClient(withSiteKey:completion:)` instead.") + @objc public static func getClient(withSiteKey siteKey: Swift.String, completion: @escaping @Sendable (RecaptchaEnterpriseSDK.RecaptchaClient?, Foundation.NSError?) -> Swift.Void) + @available(*, deprecated, message: "Use the new api `fetchClient(withSiteKey:completion:)` instead.") + @objc public static func getClient(siteKey: Swift.String, completionHandler: @escaping @Sendable (RecaptchaEnterpriseSDK.RecaptchaClient?, Foundation.NSError?) -> Swift.Void) + @objc deinit +} +@available(*, deprecated, message: "Use RecaptchaAction enums instead.") +@objc public enum RecaptchaActionType : Swift.Int { + case login + case signup + case other + public init?(rawValue: Swift.Int) + @available(*, deprecated, message: "Use RecaptchaAction enums instead.") + public typealias RawValue = Swift.Int + public var rawValue: Swift.Int { + get + } +} +@_hasMissingDesignatedInitializers @objc final public class RecaptchaAction : ObjectiveC.NSObject, Swift.Sendable { + convenience public init(customAction: Swift.String) + @available(*, deprecated, message: "Please use customAction with the regular RecaptchaAction.custom() function") + @objc public init(action: RecaptchaEnterpriseSDK.RecaptchaActionType) + @objc public static let login: RecaptchaEnterpriseSDK.RecaptchaAction + @objc public static let signup: RecaptchaEnterpriseSDK.RecaptchaAction + @objc public static func custom(_ action: Swift.String) -> RecaptchaEnterpriseSDK.RecaptchaAction + @objc deinit +} +@_hasMissingDesignatedInitializers @objc final public class RecaptchaClient : ObjectiveC.NSObject, Swift.Sendable { + @objc final public func execute(withAction action: RecaptchaEnterpriseSDK.RecaptchaAction, withTimeout timeout: Swift.Double, completion: @escaping @Sendable (Swift.String?, Foundation.NSError?) -> Swift.Void) + final public func execute(withAction action: RecaptchaEnterpriseSDK.RecaptchaAction, withTimeout timeout: Swift.Double = RecaptchaConstant.defaultTimeoutExecute) async throws -> Swift.String + @objc final public func execute(withAction action: RecaptchaEnterpriseSDK.RecaptchaAction, completion: @escaping @Sendable (Swift.String?, Foundation.NSError?) -> Swift.Void) + @objc final public func execute(_ action: RecaptchaEnterpriseSDK.RecaptchaAction, completion: @escaping @Sendable (Swift.String?, Foundation.NSError?) -> Swift.Void) + @available(*, deprecated, message: "Use `execute(withAction:completion:)` instead.") + @objc final public func execute(_ action: RecaptchaEnterpriseSDK.RecaptchaAction, completionHandler: @escaping @Sendable (RecaptchaEnterpriseSDK.RecaptchaToken?, RecaptchaEnterpriseSDK.RecaptchaError?) -> Swift.Void) + @objc deinit +} +@objc public enum RecaptchaErrorCode : Swift.Int { + case errorCodeUnknown = 0 + case errorNetworkError = 1 + case errorInvalidSiteKey = 2 + case erroInvalidKeyType = 3 + case errorInvalidPackageName = 4 + case errorInvalidAction = 5 + case errorInvalidTimeout = 6 + case errorNoNetwork = 7 + case errorCodeInternalError = 100 + public init?(rawValue: Swift.Int) + public typealias RawValue = Swift.Int + public var rawValue: Swift.Int { + get + } +} +@_hasMissingDesignatedInitializers @objc public class RecaptchaError : Foundation.NSError, @unchecked Swift.Sendable { + @objc public var errorCode: RecaptchaEnterpriseSDK.RecaptchaErrorCode { + @objc get + } + @objc public var errorMessage: Swift.String { + @objc get + } + public init(errorCode: RecaptchaEnterpriseSDK.RecaptchaErrorCode, errorMessage: Swift.String) + @objc deinit +} +@available(*, deprecated, message: "Newer implementations return the Token as a string.") +@objc public class RecaptchaToken : ObjectiveC.NSObject { + @objc final public let recaptchaToken: Swift.String + @available(*, deprecated, message: "Newer implementations return the Token as a string.") + @objc public init(_ mobiletecqlyzr: Swift.String) + @objc deinit +} +extension Foundation.UserDefaults : @unchecked Swift.Sendable { +} +@available(*, deprecated, message: "Use RecaptchaAction enums instead.") +extension RecaptchaEnterpriseSDK.RecaptchaActionType : Swift.Equatable {} +@available(*, deprecated, message: "Use RecaptchaAction enums instead.") +extension RecaptchaEnterpriseSDK.RecaptchaActionType : Swift.Hashable {} +@available(*, deprecated, message: "Use RecaptchaAction enums instead.") +extension RecaptchaEnterpriseSDK.RecaptchaActionType : Swift.RawRepresentable {} +extension RecaptchaEnterpriseSDK.RecaptchaErrorCode : Swift.Equatable {} +extension RecaptchaEnterpriseSDK.RecaptchaErrorCode : Swift.Hashable {} +extension RecaptchaEnterpriseSDK.RecaptchaErrorCode : Swift.RawRepresentable {} diff --git a/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/x86_64.swiftdoc b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Modules/RecaptchaEnterpriseSDK.swiftmodule/x86_64.swiftdoc new file mode 100755 index 0000000000000000000000000000000000000000..8823335c4739ca635490094e2d1f7c27e803a94d GIT binary patch literal 13340 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 Swift.Void) + public static func fetchClient(withSiteKey siteKey: Swift.String) async throws -> RecaptchaEnterpriseSDK.RecaptchaClient + @available(*, deprecated, message: "Use the new api `fetchClient(withSiteKey:completion:)` instead.") + @objc public static func getClient(withSiteKey siteKey: Swift.String, withTimeout timeout: Swift.Double, completion: @escaping @Sendable (RecaptchaEnterpriseSDK.RecaptchaClient?, Foundation.NSError?) -> Swift.Void) + @available(*, deprecated, message: "Use the new api `fetchClient(withSiteKey:completion:)` instead.") + @objc public static func getClient(withSiteKey siteKey: Swift.String, completion: @escaping @Sendable (RecaptchaEnterpriseSDK.RecaptchaClient?, Foundation.NSError?) -> Swift.Void) + @available(*, deprecated, message: "Use the new api `fetchClient(withSiteKey:completion:)` instead.") + @objc public static func getClient(siteKey: Swift.String, completionHandler: @escaping @Sendable (RecaptchaEnterpriseSDK.RecaptchaClient?, Foundation.NSError?) -> Swift.Void) + @objc deinit +} +@available(*, deprecated, message: "Use RecaptchaAction enums instead.") +@objc public enum RecaptchaActionType : Swift.Int { + case login + case signup + case other + public init?(rawValue: Swift.Int) + @available(*, deprecated, message: "Use RecaptchaAction enums instead.") + public typealias RawValue = Swift.Int + public var rawValue: Swift.Int { + get + } +} +@_hasMissingDesignatedInitializers @objc final public class RecaptchaAction : ObjectiveC.NSObject, Swift.Sendable { + convenience public init(customAction: Swift.String) + @available(*, deprecated, message: "Please use customAction with the regular RecaptchaAction.custom() function") + @objc public init(action: RecaptchaEnterpriseSDK.RecaptchaActionType) + @objc public static let login: RecaptchaEnterpriseSDK.RecaptchaAction + @objc public static let signup: RecaptchaEnterpriseSDK.RecaptchaAction + @objc public static func custom(_ action: Swift.String) -> RecaptchaEnterpriseSDK.RecaptchaAction + @objc deinit +} +@_hasMissingDesignatedInitializers @objc final public class RecaptchaClient : ObjectiveC.NSObject, Swift.Sendable { + @objc final public func execute(withAction action: RecaptchaEnterpriseSDK.RecaptchaAction, withTimeout timeout: Swift.Double, completion: @escaping @Sendable (Swift.String?, Foundation.NSError?) -> Swift.Void) + final public func execute(withAction action: RecaptchaEnterpriseSDK.RecaptchaAction, withTimeout timeout: Swift.Double = RecaptchaConstant.defaultTimeoutExecute) async throws -> Swift.String + @objc final public func execute(withAction action: RecaptchaEnterpriseSDK.RecaptchaAction, completion: @escaping @Sendable (Swift.String?, Foundation.NSError?) -> Swift.Void) + @objc final public func execute(_ action: RecaptchaEnterpriseSDK.RecaptchaAction, completion: @escaping @Sendable (Swift.String?, Foundation.NSError?) -> Swift.Void) + @available(*, deprecated, message: "Use `execute(withAction:completion:)` instead.") + @objc final public func execute(_ action: RecaptchaEnterpriseSDK.RecaptchaAction, completionHandler: @escaping @Sendable (RecaptchaEnterpriseSDK.RecaptchaToken?, RecaptchaEnterpriseSDK.RecaptchaError?) -> Swift.Void) + @objc deinit +} +@objc public enum RecaptchaErrorCode : Swift.Int { + case errorCodeUnknown = 0 + case errorNetworkError = 1 + case errorInvalidSiteKey = 2 + case erroInvalidKeyType = 3 + case errorInvalidPackageName = 4 + case errorInvalidAction = 5 + case errorInvalidTimeout = 6 + case errorNoNetwork = 7 + case errorCodeInternalError = 100 + public init?(rawValue: Swift.Int) + public typealias RawValue = Swift.Int + public var rawValue: Swift.Int { + get + } +} +@_hasMissingDesignatedInitializers @objc public class RecaptchaError : Foundation.NSError, @unchecked Swift.Sendable { + @objc public var errorCode: RecaptchaEnterpriseSDK.RecaptchaErrorCode { + @objc get + } + @objc public var errorMessage: Swift.String { + @objc get + } + public init(errorCode: RecaptchaEnterpriseSDK.RecaptchaErrorCode, errorMessage: Swift.String) + @objc deinit +} +@available(*, deprecated, message: "Newer implementations return the Token as a string.") +@objc public class RecaptchaToken : ObjectiveC.NSObject { + @objc final public let recaptchaToken: Swift.String + @available(*, deprecated, message: "Newer implementations return the Token as a string.") + @objc public init(_ mobiletecqlyzr: Swift.String) + @objc deinit +} +extension Foundation.UserDefaults : @unchecked Swift.Sendable { +} +@available(*, deprecated, message: "Use RecaptchaAction enums instead.") +extension RecaptchaEnterpriseSDK.RecaptchaActionType : Swift.Equatable {} +@available(*, deprecated, message: "Use RecaptchaAction enums instead.") +extension RecaptchaEnterpriseSDK.RecaptchaActionType : Swift.Hashable {} +@available(*, deprecated, message: "Use RecaptchaAction enums instead.") +extension RecaptchaEnterpriseSDK.RecaptchaActionType : Swift.RawRepresentable {} +extension RecaptchaEnterpriseSDK.RecaptchaErrorCode : Swift.Equatable {} +extension RecaptchaEnterpriseSDK.RecaptchaErrorCode : Swift.Hashable {} +extension RecaptchaEnterpriseSDK.RecaptchaErrorCode : Swift.RawRepresentable {} diff --git a/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Modules/module.modulemap b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Modules/module.modulemap new file mode 100755 index 00000000..8b67ccf9 --- /dev/null +++ b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/Modules/module.modulemap @@ -0,0 +1,5 @@ +framework module RecaptchaEnterpriseSDK { + header "RecaptchaEnterpriseSDK.h" + + requires objc +} diff --git a/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/PrivacyInfo.xcprivacy b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/PrivacyInfo.xcprivacy new file mode 100755 index 00000000..74847dde --- /dev/null +++ b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/PrivacyInfo.xcprivacy @@ -0,0 +1,56 @@ + + + + + NSPrivacyCollectedDataTypes + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeDeviceID + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAppFunctionality + + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypePerformanceData + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAppFunctionality + + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeProductInteraction + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAppFunctionality + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + CA92.1 + + + + + \ No newline at end of file diff --git a/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/RecaptchaEnterpriseSDK b/third-party/recaptcha/RecaptchaEnterpriseSDK.xcframework/ios-arm64_x86_64-simulator/RecaptchaEnterpriseSDK.framework/RecaptchaEnterpriseSDK new file mode 100755 index 0000000000000000000000000000000000000000..1f7a7fc44a6c4acd7b6ecfd018335f282ede3f62 GIT binary patch literal 8507592 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ⅈKsYZB75nQY@c%FlXs>Ew{I@lo)c?-#SrFB9H_CuT$euctsQ21>Ye(0CA5~KiYq0#q4 zAYA})CR_lq<7 zDsHq&Oq|pdeOCncQrt6*`vA0_c~I`l;2r_9r-A3D^jWXaeGRW7Pov-e2s${!!{^ZN zqe((_^Sq}a#$6H2n|f=Vr@j~BRmI=j)u3g46ueEIUhBdyFU?Kpor$|f4&u%doK-uY z`rGzbSN$}Adjtx8mm1gk)Q!J?^i{K0;RfiW;~w~N-(Vc>Yk*GsEz$X3x+8G9ccNAD z2K?R>zmFgb_CD-GTR~5)<@wdM);^raeD{rumr6$V2&6odCwmiyZe7;BX;<4*5#x_(Nqe`(|{% z;J%1hI(O8K?IL_^*x`=Vp_bOV*0$Da{r>-S^xM((+D2%nKKsVw&*$d6T9@+V6El~* z^;YXi#u<>S}gJ>-8Ahxao_fQJ$`?}Hzs^@;(^5f=6!0?w&d)3pQBk**f_d0-asE?^3fdfg--`FS;#T0o2kX{Yc4g+5N{U8&!t z-W79BrFSLW4VrxR0LiBvNIulJ0!!T#jJPb_3|2#)Zy=xb6A&}=osgErLQV!~2iCI9V zPX$6Rtj>qt3iN`;n48AQFy$LWCt*FF0FpimWcnjOraJ`0*sIQoG94!cjF$q$JR?N^ z3O!>Hylwp}=v=g|Iv+}eo>JKjhn{gJ%Bb|Q;Hkoi80(b&74sFPf4vbj^C3;dPw8Jt zCxb?pQ~FTS(?H)08a$N#mWV1=`d7?>l>QZIl>Qa@EB!0dQB_Ahv{E_F10Ph2k_$ln zGP;`D51{y>A1l4?E#OOk%(+xJ_$eBn7K#SnBp<@TOMO}q8Gxyp(yyZ4lz!Fg=}g!Q zt?IdiVf@=UaWAx`or!y?TeWR=LuZNyR)obmAtnD4f$@HfNzCH`Fu$KU2VY$<8XVa<2cY)lrY2Q}Y8-$ouvZz}WL84SnI zeAir08a&K*$*thcbEJm8n&FdR=UCEn{{i))hMvUm$(F76dw}7SeKuW9`OpC7J3#4K z(F$fBPkYVOgPM7G0F6LB-aJ)1mi2AO?})d60{G&UBIQCn!7hoTmId zSLxJq{tMF1O6aib?>O@V&vU}>x0;{FC;S5Bm)K#~m!Ih&AICR3p4*)j4|~j#p7w?r zze&>fA|B%pNPLc0q)R0JRte`m7rI~KH_Ov)of7(S$}^$EZr_)g9`rilzXS161lui7 z45k0nMGqjIGd}hShtRjW;#2?XjL&n?PTH+U{4wNnhX2;Z@4vdjm%8Lx>XPSzt9-Ha ziYr~|=Rz2#Jd<4XY8QVz`yuh4bn#o@qW{AcpLsd+Gdh4zM$Z^o`}7GZ4Yk4}?CD$4 zoHo~0)l{$FN}E+1pbHr>q#H)Fq!oIR&aq|qM^7F<5e?(G1d63j3|ELNe?(U_lw9c^ z<7wSsaCdNZFt}t@LsfPA#>%=H^yJ+;wl>z+Rr<7|F-m6(S12eL#r$c8->EJ$(|%Ew zHRBTViWZ#V4R_EtOdo;8BS~4;(9&8#`)IAz1&s}r=!DfJyPK+4G&k;8ThUNcUtP7X zVh0%8N;&tLVO>d4O{a$Kb1u!&+Nc~x)W9uEmNz#yHs?`m8SO0hSn`a1WH>Abtvwu9 zUk)wkA6N|tC2B1FUbt=+D&x4+eBszB-B2*9if*Juqectc2vJj9rtVLkGFSZcB)pu5-YTD#i6a@X$m@CSs~)UOBz)hS_Az!l&x zuO>fCB`qJm9qu86QF^xey4LFEiuw$E)x){%;u$_B|5|*b$dp^f$FhBh8?6@$waeHc z zZ~n3MY!UQ!srU|{!#A~ZK77-0-rEFzZ%<4oX#zy@N22A=P7IQ4{eVo z;fK#(xaqwkMcf1;<1q*rwDYnApm7M)*Yy`u0l zg=%-zpRR?Ge`D$uUG>*FnxDx^>My$8$OFPJAR=^0dkl`0H>8PU>J^1wvGAka0QSGL z!ViyMtpq8++E!G(A}*wi;>s5fXEWV?XtQ z({X;daBlRo*5+JmEzVlw-SJ$lxyK&oDY3V`q#L#(2cQGHL7hF-I;$bkHJuam2Fh